21 Aug 2014

JavaScript Object Layout

注:本文为译文,原文出处Javascript Object Layout

有趣的观点

  • 所有实例都继承自创建它们的函数的原型属性.

  • Mozilla/Konqueror 中包含一个属性名为__proto__的特殊实现, 这个属性指向其构造器函数的原型属性(构造器函数通常用于创建此类型的任意实例).

  • 无论__proto__属性是否存在, 通常来讲, 所有对象都使用原型属性指向对象的构造器函数. 这个prototype属性是Javascript标准的一部分. 原型属性上, 默认有一个构造器属性指回到原型属性对应的函数.

  • 原型属性仅作用于继承自通过该函数创建的对象/实例上. 该函数本身不使用相关联的原型属性(但是由于函数本身也是一个对象, 它也继承了构造器函数的原型属性, 通常是JavaScript的系统“Function”对象).
function Foo() { } ;
var f1 = new Foo();

Foo.prototype.x = "hello";
f1.x   //=> hello
Foo.x //=> undefined

注意, 函数Foo创建的对象, 需要通过Foo.prototype给来设置属性, 并不是通过f1.prototype设置f1的属性. 这是非常需要注意的一点.


  • 默认的原型属性可以替换成另一个用户自定义对象. 如果这么做了, 必须手动设置构造器属性, 以复制javascript运行时环境在此场景下的对于默认的原型属性对象的所有操作,.
function foo() { } ; var f1 = new foo();
f1.constructor === foo.prototype.constructor === foo
//replace the default prototype object
foo.prototype = new Object();
//create another instance l
f1 = new foo();
//now we have:
f1.constructor === foo.prototype.constructor === Object
//so now we say:
foo.prototype.constructor = foo
//all is well again
f1.constructor === foo.prototype.constructor === foo

  • 每个原型属性对象都是用构造器Object()创建的(默认情况), 因此原型属性的原型为Object.prototype. 因此所有实例都继承了Object.prototype的属性.

  • 所有对象会自动读取原型链的属性, 就像那些属性是对象自身定义的.
    在对象上设置相同的属性会隐藏实例的原型上的对应属性.
function foo() { }
f1 = new foo();
f2 = new foo();
foo.prototype.x = "hello";

f1.x  => "hello"
f2.x  => "hello";

f1.x = "goodbye";   //setting f1.x hides foo.prototype.x

f1.x  => "goodbye"  //hides "hello" for f1 only
f2.x  => "hello"

delete f1.x
f1.x  => "hello";   //foo.prototype.x is visible again to f1.

设置对象原型的属性会导致所有实例的变化.

foo.prototype.x = "goodbye";
//now
f1.x  => "goodbye"
f2.x  => "goodbye";

更加有趣的观点

观察上图中的不同的箭头, 会看到javascript语言中一些有趣的关系。 (如果想试一试, 可以在javascript控制台输入以下代码样例)。

  • Function.__proto__ 指向 Function.prototype. 这导致:
    Function.constructor === Function
    也就是说: Function是它自己的构造器!

  • Object instanceof Object == true.
    这是因为: Object.__proto__.__proto__.constructor == Object
    注意, 与 Object instanceof Object 不同的是, Foo instanceof Foo == false.
    这是因为: Foo并没有作为一个构造器存在于它自身的原型链中。

  • Function.prototype.toString 是一个内置方法(built-in method), 并与另一个内置方法存在明显区别: Object.prototype.toString
f1.toString() 调用:
Object.prototype.toString
输出结果: [object ...]

而:

Foo.toString() 优先查找:
Function.prototype.toString()
输出结果: [Function foo...]

如果这样:

delete Function.prototype.toString
Foo.toString()
输出结果: [object ...]

Tags:
0 comments