Javascript 原型拓扑
Javascript 是一个「基于原型」的开发语言。它的内部有一些很有趣的机制,例如原型和闭包。为了弄清楚原型(prototpye)的工作原理,我花了点时间理清了一下思路,记于此文。
¶Basic
Javascript 里最重要的两个核心成员「对象/Object」和「函数/Function」,它们的关系非常紧密,我们可以用 instanceof 做一个简单的测试:
> Object instanceof Object
true
> Function instanceof Function
true
> Object instanceof Function
true
> Function instanceof Object
true
哈,Object 是 Function 的实例,而 Function 又是 Object 实例,如此纠结的关系究竟是怎样的呢?来看看 instanceof 的简单定义:
A instanceof B ,若通过 A 的原型链能追溯到 B 的原型,则 A 是 B 的实例。
经过一些梳理,我们很快可以得到如下结构:
注意到 Object.prototype.__proto__ 为 null,所以 Object.prototype 处于整个原型链的最顶端,其它所有 prototype 都是它的后代。
有意思的是 Object.__proto__ 与 Function.__proto__ 同时指向另一个 prototype 。这个 prototype 给函数对象开僻了另一个属性空间,可以用于存放静态属性及静态方法等。
在此继续探索之前,需要先理解一下几个概念:
- constructor: 指向与此原型对象关联的构造函数
- prototype: 构造函数创建实例时,将实例的 __proto__ 指向该 prototype
- __proto__: 对象通过该字段访问原型链
¶Create Object
当我们创建一个 Object 的时候
var foo = new Object(); // 或 foo = {}
实际上创建了一个 foo 对象,并将它的 __proto__ 指向了 Object.prototype :
// pseudo code
foo = {
__proto__: Object.prototype
}
¶Create Function
当我们创建一个 Function 的时候:
var Bar = new Function(); // 或 Bar = function(){}
实际上创建了一个 Bar 对象和一个 prototype 对象,并作了如下处理:
// pseudo code
Bar = {
__proto__: Function.prototype,
}
Bar.prototype = {
__proto__: Object.prototype
constructor: Bar;
}
¶Create Object from Constructor
当我们实例化一个自定义对象时:
var bar = new Bar();
仍旧很简洁:
// pseudo code
bar = {
__proto__: Bar.prototype
}
¶Simple Inheritance
如果要实现一个简单的继承呢:
var Baz = function(){};
Baz.prototype = new Bar();
Baz.prototype.constructor = Baz;
var baz = new Baz();
我们可以看到,一个 Bar 的实例替代了 Baz 原本的 prototype 对象。于是新创建出来的 baz 实例可以通过 __proto__ 访问经过 Bar.prototype 的原型链,从而继承来自 Bar 的属性和方法。
这个继承是否有效呢,我们可以通过 instanceof 检验一下:
> baz instanceof Baz
true
> baz instanceof Bar
true
> baz instanceof Object
true
很好,本次探索暂时告一段落。