笔记 02 JS 原型

原文地址
prototypejs

原型链 – 实现类继承 (ES5)

注意: 简单的使用 Bar.prototype = Foo.prototype
将会导致两个对象共享相同的原型。
因此,改变任意一个对象的原型都会影响到另一个对象的原型,
在大多数情况下这不是希望的结果。

注意: 不要使用 Bar.prototype = Foo,
因为这不会执行 Foo 的原型,而是指向函数 Foo。
因此原型链将会回溯到 Function.prototype
而不是 Foo.prototype,
因此 method 将不会在 Bar 的原型链上。

function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// 设置Bar的prototype属性为Foo的实例对象
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor为Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 创建Bar的一个新实例

// 原型链
test [Bar的实例]
    Bar.prototype [Foo的实例] 
        { foo: 'Hello World' }
        Foo.prototype
            {method: ...};
            Object.prototype
                {toString: ... /* etc. */};

/**
 需要注意的是 new Bar() 不会创造出一个新的 Foo 实例,
 而是 重复使用它原型上的那个实例;
 因此,所有的 Bar 实例都会共享相同的 value 属性。
*/

1 属性查找

当查找一个对象的属性时,
JavaScript 会向上遍历原型链,
直到找到给定名称的属性为止。
到查找到达原型链的顶部 – 也就是 Object.prototype – 但是仍然没有找到指定的属性,
就会返回 undefined

2 原型属性

当原型属性用来创建原型链时,可以把任何类型的值赋给它(prototype)。

  • 原子类型赋给 prototype 的操作将会被忽略。
  • 对象赋值给 prototype,将会动态的创建原型链。
function Foo() {}
Foo.prototype = 1; // 无效

// 下面有效
function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

3 性能 (属性查找)

如果一个属性在原型链的上端,则对于查找时间将带来不利影响。
特别的,试图获取一个不存在的属性将会遍历整个原型链。

当使用 for in 循环遍历对象的属性时,
原型链上的所有属性都将被访问

4 扩展内置类型的原型 (错误特性)

  • 扩展 Object.prototype
  • 扩展 其他内置类型的原型对象 (Date 增加format函数)

破坏封装(技术名称: monkey patching)

虽然它被广泛的应用到一些 JavaScript 类库中 (如:Prototype)

0.0 不认为 为内置类型添加一些非标准的函数是个好主意 (静静地看着)

扩展内置类型的唯一理由是为了和新的 JavaScript 保持一致,比如 Array.forEach。

这是编程领域常用的一种方式,称之为 Backport,也就是将新的补丁添加到老版本中

总结

  • 充分理解原型链继承的工作方式是每个 JavaScript 程序员必修的功课

    要提防原型链过长带来的性能问题,并知道如何通过缩短原型链来提高性能。

  • 绝对不要扩展内置类型的原型,除非是为了和新的 JavaScript 引擎兼容