Javascript系列——prototype与__proto__的区别

对于有基于类的语言经验 (如 Java 或 C++) 的开发人员来说,JavaScript 有点令人困惑,因为它是动态的,并且本身不提供一个 class 实现。(在 ES2015/ES6 中引入了 class 关键字,但只是语法糖,JavaScript 仍然是基于原型的)。
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 proto )指向它的原型对象( prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object的实例。
尽管这种原型继承通常被认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比经典模型更强大。例如,在原型模型的基础上构建经典模型相当简单。

继承属性
JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
遵循ECMAScript标准,someObject.[[Prototype]] 符号是用于指向 someObject的原型。从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf()和 Object.setPrototypeOf()访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__。
但它不应该与构造函数 func 的 prototype 属性相混淆。被构造函数创建的实例对象的 [[prototype]] 指向 func 的 prototype 属性。Object.prototype 属性表示 Object的原型对象。

继承方法
JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性。函数的继承与其他的属性继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的方法重写)。
当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。

创建对象的方式
语法结构创建的对象
构造器创建的对象
Object.create创建的对象
class关键字创建的对象

性能
在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。
遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从 Object.prototype 继承的 hasOwnProperty方法。
hasOwnProperty是 JavaScript 中处理属性并且不会遍历原型链的方法之一。(另一种方法: Object.keys())
注意:检查属性是否 undefined还不够。该属性可能存在,但其值恰好设置为 undefined。

简而言之, prototype 是用于类的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
[[Prototype]] 看起来就像递归引用

//当你这样创建一个对象时,具体是做了什么呢
var o = new Foo();
//JavaScript 实际上执行的是:
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);

相关推荐