原型

每一个 JavaScript 对象(null 除外)都和另一个对象相关联。“另一个” 对象即我们所熟知的原型,每一个对象都从原型继承属性。

所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过 Object.prototype 获得对原型对象的引用。通过关键字 new 和构造函数调用创建的对象的原型就是构造函数的 prototype 属性的值。因此,同使用字面量创建对象一样,通过 new Object 创建的对象也继承自 Object.prototype。这一系列链接的原型对象就是所谓的 “原型链(prototype chain)”。

可通过 Object.getPrototypeOf() 查询对象的原型。

原型的继承

对象除了 “自有属性(own property)”,还有一些属性是从原型对象继承来的。

属性访问细节:假设要查询对象 o 的属性 x,如果 o 中不存在 x,那么会继续在 o 的原型对象中查询属性 x。如果原型对象中也没有 x,但这个原型对象也有原型,那么继续在这个原型对象的原型上查询,直到找到 x 或者查找到一个原型是 null 的对象为止。由上可知,对象的原型属性构成了一个 “链”,通过这个 “链” 可以实现属性的继承。

“继承” 意味着拷贝操作,而 JavaScript 补考呗对象属性。相反,JavaScript 在两个对象之间建立链接,一个对象实质上可以将对象、函数的访问 “委托” 到另一个对象上。

1. 小心继承

var anotherObject = {
	a: 2
};

var myObject = Object.create(anotherObject);

anotherObject.a; // 2
myObject.a; // 2

anotherObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("a"); // false

myObject.a++; // oops, implicit shadowing!

anotherObject.a; // 2
myObject.a; // 3

myObject.hasOwnProperty("a"); // true

虽然 myObject.a++ 看起来应当通过委托查询并原地递增 anotherObject.a 的属性,但 ++ 操作符实际上等价于 myObject.a = myObject.a + 1,导致为 myObject 增加了 a 属性。

原型链

每个函数都有一个 prototype 属性,其指向一个对象,而这个对象就是函数(构造函数)创建的实例的原型;而每个对象都有一个 __proto__ 属性,除了 null(虽然 typeof nullobject,但这是一个错误结果),这个属性会指向该对象的原型:

function Foo() {}

const f1 = new Foo();
f1.__proto__ === Foo.prototype; // true
Object.getPrototypeOf(foo) === Foo.prototype; // true

因为一个构造函数可以 new 出多个实例,很明显不存在原型指向实例,仅存在原型通过 constructor 指向构造函数:

Foo.prototype.constructor === Foo; // true

在继承中了解到,查找属性时会顺着原型链一直找,直到 null。而原型也是个对象,那么它指向谁呢,可以利用 Object 创建最基础的对象:

Foo.prototype.__proto__ === Object.prototype; // true
Foo.prototype.__proto__.constructor === Object; // true

Object 的原型往上则是 null 了,毕竟路是有“终点”的:

Object.prototype.__proto__ === null; // true
prototype-chains

1. 存在的问题

Function instanceof Object; // true
Object instanceof Function; // true

那究竟是谁在更上一层级呢?在规范中虽言及 ”函数是可调用的对象“,但个人认为它们是同时出现的:

Object.getPrototypeOf(Function) === Object.getPrototypeOf(Object); // true

更进一步:

Function.__proto__ === Function.prototype; // true
Object.__proto__ === Function.prototype; // true
Foo.__proto__ === Function.prototype; // true
prototype-chains-function