标签:
JavaScript 中的每个对象都有一个内部的链接指向另一个对象,这个对象就是原对象的原型(prototype)。这个原型对象也有自己的原型,直到对象的原型为 null
为止(也就是没有原型)。这种一级一级的链结构就称为原型链(prototype chain)。
JavaScript 对象是动态的装着许多属性的“袋子”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链接。当试图访问一个对象的属性时,不仅会试图访问该在对象上的,而且访问该对象的原型上的,以及该对象的原型的原型上的,依此类推,直到找到一个名字匹配的属性或达到了原型链的末尾。
根据 ECMAScript 标准,someObject.[[Prototype]]
符号是用于指派 someObject
的原型。这个等同于 JavaScript 的 __proto__
属性(现已弃用)。从 ECMAScript 6 开始, [[Prototype]]
可以用Object.getPrototypeOf()
和Object.setPrototypeOf()
访问器来访问。
下面的代码则演示了当访问一个对象的属性时发生的行为:
var o = {a: 1};
// o这个对象继承了Object.prototype上面的所有属性
// 所以可以这样使用 o.hasOwnProperty(‘a‘).
// hasOwnProperty 是Object.prototype的自身属性。
// Object.prototype的原型为null。
// 原型链如下:
// o ---> Object.prototype ---> null
var a = ["yo", "whadup", "?"];
// 数组都继承于Array.prototype
// (indexOf, forEach等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){
return 2;
}
// 函数都继承于Function.prototype
// (call, bind等方法都是从它继承而来):
// f ---> Function.prototype ---> Object.prototype ---> null
使用构造方法创建对象
在 JavaScript 中,构造方法其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。
function Graph() {
this.pens= [];
}
Graph.prototype = {
addPen: function(v){
this.pens.push(v);
}
};
var g = new Graph();
// g是生成的对象,他的自身属性有‘pens‘.
// 在g被实例化时,g.[[Prototype]]指向了Graph.prototype.
使用 Object.create 创建对象
ECMAScript 5 中引入了一个新方法:Object.create()
。可以调用这个方法来创建一个新对象。新对象的原型就是调用 create
方法时传入的第一个参数:
var a = {a: 1};
// 原型链如下:
// a ---> Object.prototype ---> null
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)
var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null
var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype
// 假定有一个对象 o, 其自身的属性(own properties)有 a 和 b:
// {a: 1, b: 2}
// o.[[Prototype]]有属性 b 和 c:
// {b: 3, c: 4}
// 最后, o.[[Prototype]].[[Prototype]] 是 null.
// 这就是原型链的末尾,即 null,
// 根据定义,null 没有[[Prototype]].
// 综上,整个原型链如下:
// {a:1, b:2} ---> {b:3, c:4} ---> null
console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为1
console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为2
// o.[[Prototype]]上还有一个‘b‘属性,但是它不会被访问到.这种情况称为"属性遮蔽 (property shadowing)".
console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.
// c是o.[[Prototype]]的自身属性吗?是的,该属性的值为4
console.log(o.d); // undefined
// d是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.
// d是o.[[Prototype]]的自身属性吗?不是,那看看o.[[Prototype]].[[Prototype]]上有没有.
// o.[[Prototype]].[[Prototype]]为null,停止搜索,
// 没有d属性,返回undefined
prototype 和 Object.getPrototypeOf
函数 A 有一个特殊的属性叫做原型。这种特殊的属性与JavaScript 的new 运算符一起工作。参考原型对象复制到新实例的内部[[Prototype]]属性。例如,当你这样做: var a1 = new A(), JavaScript 就会这样做:a1.[[Prototype]] = A.prototype(在内存中创建对象后,而在运行 this 绑定的函数 A()之前)。然后当您访问实例的属性,JavaScript首先检查它们是否直接在该对象上存在(即是否是该对象的自身属性),如果不是,它可能是在[[Prototype]]上。这意味着,你在原型定义的东西是有效地由所有实例共享,你甚至可以在以后更改原型的部分,而改变的部分会现在所有存在的实例里。
像上面的例子一样,如果你做 var a1 = new A(); var a2 = new A(); 那么 a1.doSomething 就会指向Object.getPrototypeOf(a1).doSomething,和你所定义的 A.prototype.doSomething 一样。比如:Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething。
简而言之, prototype 是用于类型的,而 Object.getPrototypeOf() 是用于实例的(instances),两者功能一致。
[[Prototype]] 看起来就像递归一样, 如a1.doSomething,Object.getPrototypeOf(a1).doSomething,Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething 等等, 直到它找到 doSomething 这个属性或者 Object.getPrototypeOf 返回 null。
因此,当你这样的时候:
var o = new Foo();
JavaScript 实际上是这样的:
var o = new Object(); o.[[Prototype]] = Foo.prototype; Foo.call(o);
(或者类似上面这样的),之后当你这样做的时候:
o.someProp;
它会检查是否存在 someProp 属性。如果没有,它会检查 Object.getPrototypeOf(o).someProp 并且如果仍旧没有,它就会检查Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp ,如此这般一直做下去,直到它找到这个属性 或者 Object.getPrototypeOf() 返回 null 的时候。
如果你喜欢,请观看原文https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
原型链
标签:
原文地址:http://www.cnblogs.com/jianpingHuang/p/5019000.html