木匣子

Web/Game/Programming/Life etc.

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 的实例。

经过一些梳理,我们很快可以得到如下结构:

prototype.png

本文的 UML 图使用 [Dia](http://dia-installer.de/) 绘制。

注意到 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
}

prototype_foo.png

Create Function

当我们创建一个 Function 的时候:

var Bar = new Function(); // 或 Bar = function(){}

实际上创建了一个 Bar 对象和一个 prototype 对象,并作了如下处理:

// pseudo code

Bar = {
    __proto__: Function.prototype,    
}

Bar.prototype = {
    __proto__: Object.prototype
    constructor: Bar;
}

prototype_bar.png

Create Object from Constructor

当我们实例化一个自定义对象时:

var bar = new Bar();

仍旧很简洁:

// pseudo code

bar = {
    __proto__: Bar.prototype
}

prototype_bar_instance.png

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 的属性和方法。

prototype_baz.png

这个继承是否有效呢,我们可以通过 instanceof 检验一下:

> baz instanceof Baz
true
> baz instanceof Bar
true
> baz instanceof Object
true

很好,本次探索暂时告一段落。