什么是原型
在 JavaScript 中,每个对象都有一个内部属性 __proto__
,这个属性指向另一个对象,这个对象就是该对象的原型。通过原型,对象可以继承原型对象的属性和方法。类似于 lua 中的元表(metatable
)。
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)的 prototype
属性上,而不是直接定义在对象本身上。
function doSomething() {}console.log(doSomething.prototype.constructor === doSomething); // trueconst obj = new doSomething();console.log(obj.__proto__ === doSomething.prototype); // true
可以看到,原型对象有一个自有属性 constructor
,这个属性指向该构造函数本身。
什么是原型链
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。 这种层层相连的原型对象就形成了所谓的“原型链”(prototype chain)。
在对象实例和它的构造器之间建立一个链接(它是 __proto__
属性,是从构造函数的 prototype
属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。
原型链的顶端是 Object.prototype
,它的原型是 null
。这意味着所有对象最终都继承自 Object.prototype
。
函数本身也是一种对象,所以函数也有自己的也有 __proto__
属性,它指向 Function.prototype
,而 Function.prototype
的原型又是 Object.prototype
。
function doSomething() {}console.log(doSomething.__proto__ === Function.prototype); // trueconsole.log(Function.prototype.__proto__ === Object.prototype); // trueconsole.log(Object.prototype.__proto__ === null); // true
doSomething ↓ (__proto__)Function.prototype ↓ (__proto__)Object.prototype ↓ (__proto__)null
特别的: Function.__proto__
与 Object.__proto__
console.log(Object instanceof Function); // trueconsole.log(Function instanceof Object); // true
二者都返回 true
说明:
Object
是由Function
构造的(因为Object
是函数)Function
也是由Object
构造的(因为Function
是对象)
所以 Object
和 Function
之间形成了一个循环引用。
分析:
Function.__proto__
指向Function.prototype
因为Function
是一个函数Object.__proto__
指向Function.prototype
因为Object
是一个函数。
console.log(Function.__proto__ === Function.prototype); // trueconsole.log(Object.__proto__ === Function.prototype); // true
于是整个原型链如下:
console.log(Function.__proto__ === Function.prototype); // trueconsole.log(Object.__proto__ === Function.prototype); // trueconsole.log(Function.prototype.__proto__ === Object.prototype); // trueconsole.log(Object.prototype.__proto__ === null); // true
Function ──────┐ ↓ (__proto__)Object ────→ Function.prototype ↓ (__proto__) Object.prototype ↓ (__proto__) null
实现 instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否存在于某个实例对象的原型链上。
function instanceOf(instance, constructor) { if ( (typeof instance !== "object" && typeof instance !== "function") || instance === null ) { return false; // 基础类型直接返回 false } let proto = Object.getPrototypeOf(instance); // 获取实例的原型 const prototype = constructor.prototype; // 获取构造函数的原型
while (proto !== null) { if (proto === prototype) { return true; } proto = Object.getPrototypeOf(proto); } return false;}
console.log(instanceOf([], Array)); // trueconsole.log(instanceOf([], Object)); // trueconsole.log(instanceOf({}, Array)); // falseconsole.log(instanceOf(function () {}, Function)); // trueconsole.log(instanceOf(function () {}, Object)); // trueconsole.log(instanceOf(null, Object)); // false
总结
所有函数(包括 Object
)都是 Function
的实例,因此它们的 __proto__
都指向 Function.prototype
。 所有对象(包括 Function
)都是 Object
的实例,因此它们的 __proto__
都指向 Object.prototype
。 Function.prototype
的原型是 Object.prototype
,而 Object.prototype
的原型是 null
。