ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
搞懂原型对象和原型链其实就是搞懂 prototype
、__proto__
和 constructor
之间的相互关系。
我们通过示例并附加图例阐述这种复杂的相互关系。
图例
__proto__
属性指向prototype
属性指向constructor
属性在 JavaScript 中,这三者之间依附在不同的引用对象类型上。
__proto__
和 constructor
是对象独有的。prototype
是函数独有的。但是函数也是对象,所以函数也有 __proto__
和 constructor
。显式原型对象 prototype
由函数所独有,它是从一个函数指向另一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象。由此可知:foo.__proto__ === Foo.prototype
,它们两个完全一样。
那 prototype
属性作用又是什么呢?它的作用就是包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数的实例化对象们都可以找到公用的属性和方法。
任何函数在创建的时候,其实会默认同时创建该函数的 prototype
对象。
在 JavaScript 中的对象中都有一个 __proto__
属性,从上图可以看出一个对象指向另一个对象,即指向相对应的对象的原型对象。这个原型对象称为隐式原型对象。
隐式原型对象的作用在于,当访问一个对象的属性或方法时,如果该对象内部不存在这个属性,那么就会从它的 __proto__
属性所指向的(原型)对象中寻找(原型也是对象,也有它自己的原型),如果原型对象中也找不到,就会继续在该原型对象的原型对象中找,以此类推,直到找到属性或方法为止,或者查找到顶层原型对象 null
,就结束查找,返回 undefined
。
整个查找过程中,从当前对象出发沿着原型对象(__proto__
)构成的链条查找相关属性和方法直到结束,这些相互关联的对象组成的链条就是原型链。
属性 constructor
也是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合 __proto__
属性查看会更清楚点),从上图中可以看出 Function 这个对象比较特殊,它的构造函数就是它自己(因为 Function 可以看成是一个函数,也可以是一个对象),所有函数和对象最终都是由 Function 构造函数得来,所以 constructor
属性的终点就是 Function 这个函数。
原型对象即为当前实例对象的父对象
显式原型对象 | 隐式原型对象 |
---|---|
属性 prototype | 属性 __proto__ |
函数独有 | 对象独有(函数也是对象,因此函数也有该属性) |
定义函数时被自动赋值,值默认为 {} | 在创建实例对象时被自动添加,并赋值为构造函数的 prototype 值 |
用于实现基于原型的继承与属性的共享 | 构成原型链,同样用于实现基于原型的继承 |
🌰 代码示例:访问原型链中的原型对象
const Foo = function () {};const foo = new Foo();// 构造函数 Foo {} 是函数 foo 的原型对象console.log(foo.__proto__);// 构造函数 Object {} 是函数 Function 的原型对象console.log(foo.__proto__.__proto__);// 原型链顶层console.log(foo.__proto__.__proto__.__proto__);// null
原型链中查找属性方法和访问原型链中的原型对象的区别
undefined
,表示原型链中没有该属性或方法。Object.prototype
的值为 null
。__proto__
的指向取决于对象创建时的实现方式。
当通过字面量方式创建对象时,它的原型就是 Object.prototype
。
虽然我们无法直接访问内置属性 __proto__
,但我们可以通过 Object.getPrototypeOf()
或对象的属性 __proto__
获取对象的原型。
const foo = {};console.log(foo.__proto__ === Object.prototype);// trueconsole.log(Object.getPrototypeOf(foo) === Object.prototype);// true
const Foo = function () {};const foo = new Foo();console.log(foo.__proto__ === Foo.prototype);// trueconsole.log(Object.getPrototypeOf(foo) === Foo.prototype);// true
通过 Object.create()
方式创建的对象会以传入的对象参数为对象的原型。
const foo = {};const bar = Object.create(foo);console.log(bar.__proto__ === foo);
通过 instanceof
操作符可以确定原型与实例的关系。
// 假设 instanceof 操作符左边是 L 右边是 RL instanceof R// instanceof 运算时 通过判断 L 的原型链是否存在 R.prototypeL.__proto__.__proto__... === R.prototype
⚠️ 注意:instanceof
操作符会递归查找 L 的原型链,即 L.__proto__.__proto__.__proto__...
直到找到了或者到达顶层为止。
构造器 Function 的构造器是它自身:
Function.constructor === Function; // true
构造器 Object 的构造器是 Function(因此可得所有构造器的 constructor 都指向 Function)
Object.constructor === Function; // true
构造器 Function 的 [[Prototype]]
是一个特殊的匿名函数
console.log(Function.__proto__); // function(){}
这个特殊的匿名函数的 [[Prototype]]
指向 Object 的 prototype 原型。
Function.__proto__.__proto__ === Object.prototype; // true
Object 的 [[Prototype]]
指向 Function 的 prototype 也就是上述的特殊的匿名函数
Object.__proto__ === Function.prototype; // trueFunction.prototype === Function.__proto__; // true
内置对象 Function 和 Object 的关系:
Function instanceof Object; // trueObject instanceof Function; // true
其他标准内置对象的字面量原型链关系:
console.log(true.__prto__.__proto__ === Object.prototype);console.log((123).__proto__.__proto__ === Object.prototype);console.log('String'.__proto__.__proto__ === Object.prototype);console.log([].__proto__.__proto__ === Object.prototype);console.log({}.__proto__ === Object.prototype);
总结:
__proto__
指向 Object.prototype参考资料: