夜深风竹敲秋韵,万叶千声皆是恨。浏览器
原型链对于JavaScript来讲是个很核心的概念。JavaScript不是基于类模板的面向对象语言;反而,它的面向对象机制是基于原型的。咱们不可能说某个对象属于什么类,但却能够获得某个对象的原型对象。原型对象至关于一个父级代理,当属性在某个对象中找不到时,就会委托该对象的原型去查找。函数
JavaScript的每一个对象,均可以有一个隐式的连接(名为__proto__
),指向它的原型对象。此次,我冒天下之大不韪(__proto__
是私有属性,不能直接对它进行操做),显示地定义几个对象和它们的原型关系。this
a = {k1: 'a1'}; b = {k1: 'b1', k2: 'b2'}; c = {k1: 'c1', k2: 'c2', K3: 'c3'}; a.__proto__ = b; b.__proto__ = c; c.__proto__ = undefined;
上面的例子中,咱们首先定义了三个对象a,b,c。接下来构造它们的原型关系。其中a的原型是b,b的原型是c,c没有原型。a、b、c造成了一条链式结构,这条链式结构在c处终止。这样一条链式结构就是原型链。es5
原型链的做用主要在于对象的取值操做。当咱们根据属性名从对象中取值时,首先会在当前对象中查找。若是在当前对象中查找不到,就会上升到该对象的原型中继续查找。若是仍然查找不到,就会继续上升到原型的原型……这个过程会一直持续下去,直到在某一次查找到或者原型链终止。因此下面的返回结果是显然的:prototype
a.k1 //=> 'a1' a.k2 //=> 'b2' a.k3 //=> 'c3' a.k4 //=> undefined
然而对象的设值和删值就不会参考原型链了,它只是对当前对象的操做。下面的例子具备必定的启发性:代理
delete a.k2 //无效,a中没有k2属性 a.k2 //=> 'b2' a.k2 = 'a2' //只会影响对象a,不会影响对象b a.k2 //=> 'a2' delete a.k2 //有效,删除a的k2属性 a.k2 //=> 'b2' a的k2属性被删除,b的k2属性暴露了出来
原型链的做用主要有如下两个方面。code
若是多个对象共享一些属性和方法,那就让这些对象指向同一个原型,在原型中定义这些属性和方法。这样共享的属性和方法只用在原型处一次定义,而无需在每一个对象中重复定义。对象
当对象a想要继承b的属性和方法时,只需简单地将b定义为a的原型便可。继承
关于继承,还有一点补充。除了原型继承以外,将对象b的属性和方法拷贝到a中去也能实现继承。这里b的属性和方法就直接存在于a中,而不是经过原型获取。ip
构造器函数能够帮助咱们构建原型链。构造函数的特点以下:
//通常构造器函数首字母大写 function Foo(name) { this.name = name; //通常不用返回任何值 }
构造器函数中,this绑定的是一个新建的对象,而且函数默认会返回这个对象。当定义构造器函数时,不要显示地返回一个值,除非你知道本身是在作什么。
每一个函数都有一个名为prototype连接,它指向一个对象。当函数做为普通函数调用时,这个连接没什么用处。只有当它做为一个构造器函数调用时,它才与原型链构成联系。其实很简单,构造器函数新建的对象,其原型就是该函数的prototype连接的对象。因此必定有下面的关系:
new Foo().__proto__ === Foo.prototype;
关于属性共享和继承的策略,能够对应到构造器函数中去。因为Foo.prototype就是new Foo()的原型,因此将共享属性和方法放到Foo.prototype中去就能够了。
function Foo(name) { this.name = name; //对象的示例属性要绑定到this上 } //对象的共享属性和方法绑定到Foo.prototype上 Foo.prototype.attr = 'attr'; Foo.prototype.foo = function() {};
继承的实现其实有不少种方式,但很难找出直接的方式。例如咱们有构造器函数A,B,C,如今想要C继承自B,B继承自A,能够经过下面的方式实现:
function A() {} function B() {} function C() {} A.prototype = new B(); B.prototype = new C();
不是很直观,但确实作到了继承。老实说,这不是最好的方式,由于中间对象采用new B()和new C()的方式构造,将B和C的实例变量引入到了原型链中来了。
为了行文上的方便,我在上面多处提到了__proto__
。可是不要经过__proto__
隐式连接去处理原型,__proto__
不是JavaScript的标准,不一样浏览器对于它们有不一样的解释。也就是说,咱们不能像下面这样作:
a.__proto__ = b; //=>不能像这样构造原型关系 a.__proto__; //=>不能像这样获得对象的原型
ES5(EMCAScript5)中Object对象增长了两个新的方法,分别是create和getPrototypeOf,分别实现上面的两种效果。上面的例子就能够像下面这样改写了。
a = Object.create(b); //返回一个以b做为原型的新对象 Object.getPrototypeOf(a); //返回a的原型对象
方法参考: