必须知道的 prototype, [[prototype]], __proto__
首先厘清这三个概念的异同:
TL;DR
Prototype是函数专有的属性(对象没有Prototype)(甚至可以说,只有构造函数有“有意义的”Prototype属性),这个属性值指向一个对象。默认的 "prototype" 是一个只有属性 constructor 的对象,属性 constructor 指向函数自身。
实例对象没有Prototype属性,但可以通过__proto__
这个getter/setter来读取对象的[[Prototype]]隐藏属性。这个隐藏属性是设置Object 的 Prototype 的接口
let a = function() {};
let b = {};
console.log(a.prototype); // {constructor: ƒ}
console.log(b.prototype); // undefined
console.log(a.__proto__); // ƒ ()
console.log(b.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
[[Prototype]]是一個设置Object 的 Prototype 的接口,是Js对象的一个隐藏属性。它要么是null,要么是一个对象。如果是对象的话,那个对象就是此对象的原型。
__proto__
是 [[Prototype]] 的因历史原因而留下来的 getter/setter。__proto__
的存在是出于历史的原因,现代编程语言建议我们应该使用函数 Object.getPrototypeOf/Object.setPrototypeOf 来取代 __proto__
去 get/set 原型。但实际上,包括服务端在内的所有环境都支持它,因此我们使用它是非常安全的。
Prototype是一个对象的属性,prototype 属性是继承成员被定义的地方。Object 之間可以互相成為各自的 Prototype,被繼承的 Object 將會繼承父 Object 的 Prototype 所有屬性。
原因在于,继承的属性和方法是定义在 prototype 属性之上的(你可以称之为子命名空间 (sub namespace) )——那些以 Object.prototype. 开头的属性,而非仅仅以 Object. 开头的属性。prototype 属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中。于是 Object.prototype.watch()、Object.prototype.valueOf() 等等成员,适用于任何继承自 Object() 的对象类型,包括使用构造器创建的新的对象实例。Object.is()、Object.keys(),以及其他不在 prototype 对象内的成员,不会被“对象实例”或“继承自 Object() 的对象类型”所继承。这些方法/属性仅能被 Object() 构造器自身使用。
Notes:
[[Prototype]] Object 內部的特殊屬性,用來將对象写入到 prototype。
__proto__
由ES6 開始成為Object的原生屬性,直接對 [[Prototype]] 進行讀寫。
prototype 是一個Object。如果你要用新建一个对象,那么當 new 一個 instance 時會被用作指向 __proto__
作為 instance 繼承的屬性。(let a = new F
时,F.prototype
就给a的[[Prototype]]赋值。)
prototype 只存在於 constructor functions,在 instance 上并不存在。相反__proto__
則出現在所有对象中。
"prototype" 属性仅在设置了一个构造函数(constructor function),并通过 new 调用时,才具有这种特殊的影响。
在常规对象上,prototype 没什么特别的:
let user = {
name: "John",
prototype: "Bla-bla" // 这里只是普通的属性
};
默认情况下,所有函数都有 F.prototype = {constructor:F},所以我们可以通过访问它的 "constructor" 属性来获取一个对象的构造器。