简单来说,可以通过
new
、function
、语法结构
来实例一个对象的,就可以称呼为原型。
- 每个实例对象( object )都有一个私有属性(称之为
__proto__
)指向它的构造函数的原型对象(prototype
)。该原型对象也有一个自己的原型对象(__proto__
) ,层层向上直到一个对象的原型对象为null
。根据定义,null
没有原型,并作为这个原型链中的最后一个环节。- 几乎所有 JavaScript 中的对象都是位于原型链顶端的
Object
的实例。
PS:上面的描述看不懂没关系,看下面的就行了
首先我们先明确 3 个概念:
- 每个实例对象有一个
__proto__
属性会指向对应的原型对象
一条完整原型链的构成:
PS:构造函数其实就是一个普通的函数,当通过new去实例对象时,普通函数就可以称呼为构造函数。
以上例子可以看得出来:
- Object原型对象都是处于几条原型链的最终环,而且Object原型对象的
__proto__
已经是null了,代表了没有再上一级原型了。- 这意味着javascript中Object原型对象处于原型链顶端。
PS:其他的例子我就不列出来了,建议各位童鞋可以自己去实践一下。
javascript中当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
下面我们来看相关的例子:
可见当访问对象一个属性的时候,当对象本身不存在此属性时,则会向上级原型搜索。
那么根据这个特性,我突然想到一个问题,关于 for in 遍历,接下来实践一下:
由此可以判断,使用 for in 遍历对象属性时,除了会把对象本身可枚举属性进行遍历外,还会在其原型链上面寻找可枚举属性。这样子就可以解释为啥会说 for in 遍历的性能比较差了。
那么关于这 for in 的问题话,也会引发其他一系列的问题,比如
- 在扩展了 Object.prototype 后使用了 for in ,可能会出现无法预估的问题,导致后续可能存在的逻辑错误
就标题而言,这是七八篇里起得最满意的,高大上,即使外行人也会不明觉厉!
不过不是开玩笑,本文的确打算从__proto__
和prototype
这两个容易混淆来理解JS的终极命题之一:对象与原型链。
引用《JavaScript权威指南》的一段描述:
翻译出来就是每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法。好啦,既然有这么一个原型对象,那么对象怎么和它对应的?
对象__proto__
属性的值就是它所对应的原型对象:
上面的代码应该已经足够解释清楚__proto__
了。好吧,显然还不够,或者说带来了新的问题:Object.prototype
是什么?凭什么说one
和two
的原型就是Object.prototype
?
首先来说说prototype
属性,不像每个对象都有__proto__
属性来标识自己所继承的原型,只有函数才有prototype
属性。
为什么只有函数才有prototype
属性?ES规范就这么定的。
开玩笑了,其实函数在JS中真的很特殊,是所谓的_一等公民_。JS不像其它面向对象的语言,它没有类(class
,ES6引进了这个关键字,但更多是语法糖)的概念。JS通过函数来模拟类。
当你创建函数时,JS会为这个函数自动添加prototype
属性,值是空对象。而一旦你把这个函数当作构造函数(constructor
)调用(即通过new
关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype
的所有属性和方法(实例通过设置自己的__proto__
指向承构造函数的prototype
来实现这种继承)。
虽然对不熟悉的人来说还有点绕,但JS正是通过__proto__
和prototype
的合作实现了原型链,以及对象的继承。
构造函数,通过prototype
来存储要共享的属性和方法,也可以设置prototype
指向现存的对象来继承该对象。
我们知道JS是单继承的,Object.prototype
是原型链的顶端,所有对象从它继承了包括toString
等等方法和属性。
什么情况下会出现鸡和蛋的问题呢?
那么具体到JS,ES规范是怎么说的?
相信经过上面的详细阐述,这张图应该一目了然了。
从上面的规定再结合其它,理出以下几点:
以上3点比较容易理解,或者说规范里就这样定义的。由以上3点导出我们最后的问题:Object
和Function
的鸡和蛋的问题。
Function.prototype
是个不同于一般函数(对象)的函数(对象)。
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。