从原型链探究Javascript这么火的原因
首先,此文是对于javascript原型链的一些私人见解,若能博君会心一笑,在下荣幸之至!
为了阐述我的理解,首先提前声明一些前置知识,欢迎指正:
栈内存和堆内存:
栈内存每个地址分配的地址长度较窄,且长度固定,用于检索和快速遍历,一般存着值类型数据,如string,number,booleen,null。
堆内存的每个地址分配的地址长度是弹性变化的,用于存储具有一定结构的数据,如函数对象function,实例对象object等,为了提高检索效率,所有堆内存里对象的存储地址都记录在栈内存内,这样通过栈内存遍历就可以快速定位堆内存内的目标。
在原型链里,常有这种情况:多个栈内存地址__proto__指向另一个栈内存地址prototype,而prototype指向堆内存内某一对象地址。
重点1 -> prototype和__proto__(隐式属性):
对象分为函数对象和实例对象,函数对象又分为构造函数对象和普通函数对象。
首先所有对象都具有__proto__属性,而特别地,函数对象内一定有prototype属性,打印任何对象时浏览器内默认不显示隐式属性,
所有函数都有prototype这一属性对象(原型),且存在constructor属性存储自身堆内存所在地址,即存在隐式属性prototype:{constructor:f}。
重点2 -> 自有属性和继承属性(显式属性和隐式属性):
var a = {}
console.log( a.b ) // undefined
a.__proto__ = { b:'b' }
console.log( a.b ) // b
函数对象一般有两个隐式属性,prototype指向自己的原型对象,__proto__一般会指向另一个函数对象的原型对象。
从第三行代码看出对象的隐式属性是可以随意修改的,这也是javascript的一大特点,第四行代码实际上查找的是a.__proto__.b,浏览器隐藏了这一过程。
访问函数的属性时,存在一个两遍查找的过程,第一遍查找自有属性,如果自有属性没有去继承属性__proto__里查找,__proto__会指向另一个函数对象的原型,在此原型里找同名属性,那如果没有找到怎么办呢?
我们上文说过,函数对象有两个隐式属性,既然自己的prototype指向的这个原型对象里没有它需要找的同名属性,就会通过自己的另一个隐式属性__proto__再指向其构造函数的prototype,再到prototype指向的原型对象里去查找,由此形成原型链,事实上,原型链在进入到最后一环之前一直是以这样一种简单的规则勾连的。
重点3 -> 继承本质:
var a = function () {} // #不一定要是构造函数
var b = new a();
console.log( b.__proto__ === a.prototype ) // true #类型和存储的地址都相同
所有被函数构造出来的实例对象,其隐式属性__proto__指向其构造函数的隐式属性prototype。
如果一个构造函数构造了许多实例对象,它们所有的__proto__相当于前文提到的栈内存入口,指向同一个构造函数的prototye,而构造函数的prototype是第一个指向其原型对象的栈内存入口,一般也可以将prototype说成原型对象,当然本质是其指向堆内存里的对象地址。
堆内存里的原型对象所具有的属性和方法,javascript通过一些底层方法使得实例对象省略.__proto__的形式直接访问,且__proto__一旦找到对应的属性或方法就返回结果,不再往上寻找更高一级的原型对象,这就是继承的本质。
顶级构造函数Object 和 Function:
Function.prototype.hasOwnProperty = function () { return 'Function prototype' } // #Object的原型里有内嵌hasOwnProperty方法
function f () { this.x = 1 }
f.x = 1
var obj = new f()
console.log( f.hasOwnProperty( 'x' )) // Function prototype
console.log(obj.hasOwnProperty( 'x' )) // true
console.log( Object.hasOwnProperty( 'x' )) // Function prototype
console.log( Object.__proto__ === Object.prototype ) // false
console.log( Object.__proto__ === Function.prototype ) // # Object将会继承Function的原型!
console.log( Function.prototype === Function.__proto__ ) // true #这意味着Function会自己继承自己的原型!
console.log( Function.hasOwnProperty( 'x' )) // Function prototype
上文说到如果自己的原型对象里没有要查找的同名属性,则通过另一个隐式属性__proto__再指向另一个函数的prototype,如此循环往复,最后都会到达两个顶级的构造函数,此时顶级函数Object的隐式属性__proto__将会找到Function的原型对象,令人惊讶的是Function的__proto__指向自身的prototye,这就意味着Function会继承自己的原型对象!
对于所有实例对象和衍生函数而言,Object和Function是原型链的终点,它们两之间的继承关系显得特别混乱,如果按原型链的规则来制定最后一级的继承关系,势必引起所有函数和对象逻辑上的混乱。
事实上,为了让函数具备对象的特性,在逻辑上是不可能有一套通用的规则的。
我们假设javascript这种语言不再赋予函数对象的特性,那么原型链不仅会变得特别好理解,而且层次分明。
Object和Function两个构造函数可以各司其职,各不相关,一个负责构造对象,一个负责构造函数,多么美妙而有序。所有对象的顶级原型都是Object.prototype,所有函数的顶级原型都是Function.prototype,然而javascript作为一门混沌而具有无穷潜力的语言,善于搜刮各种其他语言的优势填补自身的短板,而其他热门语言所具有的一大杀器便是继承的特性,对象和继承使javascript衍生出另一项绝活------构造函数。
构造函数使得构造对象不再是Object函数的专利,但这也引申出另一个问题,那就是原型链的顶级原型和下一级原型之间需要谨慎应对(毕竟使用隐式属性模仿继承总有漏洞,需要一些手段来矫正规则),于是函数和实例对象的原型链在进入到最后一级的原型后,javascript在底层对它们添加了‘新的继承规则’。
这些添加的规则使得所有实例对象和函数的继承特性在最后一级也表现得富有逻辑,比如一个实例对象,它既然不是函数,就不应该继承任何Function的原型属性和方法,因而Function的原型不会对任何实例对象造成干扰。
一个函数,它若即是函数又是对象,在面向对象编程思想盛行的时代,就有继承Object所有方法和属性的权利,于是在函数的顶级原型Function.prototype上又加了一层Object.protype这一原型。
Javascript通过隐式属性和在最后一级‘新的继承规则’成功复制了继承这一经典模式,除此之外,为了填补自身短板,适应各类编程思想,Javascript还发展出许多新的特性。
比如摆脱弥补饱受诟病的弱类型特点,Javascript发展出strict模式下的编程,增加了许多报警机制,甚至衍生出一门新的语言Typescript。为了向Java等强类型语言致敬,发展出访问器属性,这一机制也是前端框架的核心原理之一.。
最后,希望你我都能在Javascript语言中找到编程的乐趣,以上拙见,转载请注明出处。。
从原型链探究Javascript这么火的原因的更多相关文章
- JavaScript学习总结(四)——this、原型链、javascript面向对象
一.this 在JavaScript中this表示:谁调用当前函数this就指向谁,不知道调用者时this指向window. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是 ...
- js原型与原型链探究
原型有一个非常重要的属性叫 prototype 一.先写一个简单的例子,看看 A的原型和A的实例 分别是什么 function A() {} var a = new A() console.log(a ...
- 前端知识体系:JavaScript基础-原型和原型链-理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题
理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题(原文文档) 1.什么是执行上下文: 简而言之,执行上下文就是当前JavaScript代码被解析和执行时所在环境的抽象概念,Java ...
- 玩转JavaScript OOP[3]——彻底理解继承和原型链
概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...
- javascript继承--原型链的 继承
作者的话:原型链是JavaScript中相当重要的一个知识点,这里我使用了函数结构图,来帮助我更好的理解 /* 原型链继承方式: 通过改变一个对象的原型对象的指向来继承另一个对象 原理: 我们知道,一 ...
- 《前端之路》之 JavaScript原型及原型链详解
05:JS 原型链 在 JavaScript 的世界中,万物皆对象! 但是这各种各样的对象其实具体来划分的话就 2 种. 一种是 函数对象,剩下的就是 普通对象.其中 Function 和 Objec ...
- JavaScript中的原型链和继承
理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...
- Javascript之继承(原型链方式)
1.原型链 原型链是JavaScript中继承的主要方法. 每个构造函数都拥有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),实例都包含一个指向原型对象的内部指针(__p ...
- 明白JavaScript原型链和JavaScrip继承
原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: <script type="text/javascript"&g ...
随机推荐
- Mvc请求的生命周期
ASP.NET Core : Mvc请求的生命周期 translation from http://www.techbloginterview.com/asp-net-core-the-mvc-req ...
- winfrom程序文本框第一次选中问题
想实现这样的功能: 就是在panel中的文本框,当第一次点击文本框时,全选文本框的内容:再次选择时,可以全选,也可以部分选中, 可是文本框总是从左全部选中,还不能从右边选择,在Enter或Down事件 ...
- 获取当前TestStep发送的request信息
在当前test step的Script Assertion里添加 // Get request url def requestURL = messageExchange.getEndpoint() / ...
- mybatis 注解的方式批量插入,更新数据
一,当向数据表中插入一条数据时,一般先检查该数据是否已经存在,如果存在更新,不存在则新增 使用关键字 ON DUPLICATE KEY UPDATE zk_device_id为主键 model ...
- Python 12306登陆详细分析及操作
前面的话: 1.第一次尝试爬虫,登陆12306,有不足的地方,望大家留言告知,谢谢. 2.前面引入了一个requests模块,我不多说,大家都知道干啥的.还有config是我的一个配置文件,因为其中涉 ...
- 最近在研究syslog日志,就说一下syslog格式吧
syslog格式:<PRI>HEADER MESSAGE syslog的消息长度:不超过1024.syslog格式举例:<15>Jul 10 12:00:00 192.168. ...
- Minimum Spanning Trees
Kruskal’s algorithm always union the lightest link if two sets haven't been linked typedef struct { ...
- 服务器搭建lamp环境
使用的例子:服务器版本内核centos 7.04 Xshell连接到您的服务器上,使系统处于最新状态执行以下命令, yum update -y 利用yum命令安装Apache执行命令, ...
- windows下postgreSQL安装与启动
转:https://www.yiibai.com/postgresql/install-postgresql.html https://blog.csdn.net/irainreally/articl ...
- grafana 安装配置
Grafana安装配置 1.下载安装包 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.1.3.l ...