一、prototype
用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。比如,在DOG对象的构造函数中,设置一个实例对象的共有属性species。
function DOG(name){ this.name = name; this.species = '犬科'; }
然后,生成两个实例对象:
var dogA = new DOG('大毛'); var dogB = new DOG('二毛');
这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。
dogA.species = '猫科'; alert(dogB.species); // 显示"犬科",不受dogA的影响
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。考虑到这一点,Brendan Eich决定为构造函数设置一个prototype属性。
这个属性包含一个对象(以下简称"prototype对象"),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。实例对象一旦创建,将自动引用prototype对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
function DOG(name){ this.name = name; } DOG.prototype = { species : '犬科' }; var dogA = new DOG('大毛'); var dogB = new DOG('二毛'); alert(dogA.species); // 犬科 alert(dogB.species); // 犬科
现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。
二、__proto__
我们创建的每一个函数都有一个 prototype 属性,这个属性是一个指针,指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,简单来说,该函数实例化的所有对象的__proto__的属性指向这个对象,它是该函数所有实例化对象的原型。
通用规则:
对象
有__proto__
属性,函数
有prototype
属性;对象
由函数
生成;- 生成
对象
时,对象
的__proto__
属性指向函数
的prototype
属性。
//创建空对象时,实际上我们是用Object函数来生成对象的: var o = {} o.__proto__ === Object.prototype //true //我们也可以显式的使用Object函数来创建对象: var o = Object() o.__proto__ === Object.prototype //true //当我们使用函数来创建自定义的对象时,上面的规则同样适用: function MyObj(){} typeof MyObj //"function" var mo = new MyObj() mo.__proto__ === MyObj.prototype //true
既然JavaScript里“一切皆对象”,那函数
自然也是对象
的一种。对于函数
作为对象
来说,上面的规则同样适用:
//函数对象都是由Function函数生成的: function fn(){} fn.__proto__ === Function.prototype //true //Function函数本身作为对象时,生成它的函数是他自身! Function.__proto__ === Function.prototype //true //Object函数既然是函数,那生成它的函数自然是Function函数咯: Object.__proto__ === Function.prototype //true
三、prototype与__proto__
__proto__
属性是从生成它的函数的prototype
那里得来的prototype
是一个类型为"object"
的对象,它有两个属性:constructor
和 __proto__
。其中constructor
属性指向这个函数自身,__proto__
属性指向Object.prototype
,这说明一般函数的prototype
属性是由Object
函数生成的。__proto__
可以理解为“构造器的原型”,即__proto__
===constructor.protype
Object.prototype.__proto__ === null
,我们知道,这就是JavaScript原型链的终点了。prototype
属性是一个"function"
类型的对象,而不像其他函数是类型为"object"
的对象。typeof Object.prototype === "object"
,说明它是一个Object对象,如果它由Object函数生成,于是按照我们上面的通用规则,就该是Object.prototype.__proto__ === Object.prototype
。啊哈,问题出现了,
Object.prototype.__proto__
属性指向了它自身,这样以__proto__
属性构成的原型链就再也没有终点了!所以为了让原型链有终点,在原型链的最顶端,JavaScript规定了Object.prototype.__proto__ === null
。"function"
类型的对象,应该是由Function函数生成的,那它的prototype
属性应该指向Function.prototype
,也就是Function.prototype.__proto__ === Function.prototype
。和Object函数同样的问题出现了:循环引用。所以JavaScript规定Function.prototype.__proto__ === Object.prototype
,这样既避免了出现循环引用,又让__proto__
构成的原型链指向了唯一的终点:Object.prototype.__proto__ === null
。参考资料:
http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html
http://cavszhouyou.top/JavaScript%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E4%B9%8B%E5%8E%9F%E5%9E%8B%E4%B8%8E%E5%8E%9F%E5%9E%8B%E9%93%BE.html
https://www.jianshu.com/p/686b61c4a43d
https://www.jianshu.com/p/08c07a953fa0