迹忆博客
当前位置: 主页 > 学无止境 > WEB前端 > 文章

Prototype Chain 原型链你知道多少

发布时间: 2016-03-03 作者: 迹忆 浏览次数:

原型链可以说是Javascript的核心特征之一,当然也是难点之一。学过其它面向对象的编程语言后再学习Javascript多少会感到有些迷惑。虽然Javascript也可以说是面向对象的语言,但是其实现面向对象是通过prototype-based的机制而不是class-based机制。它没有其它面向对象语言的继承,多态等,但是我们却可以通过prototype来实现继承。

下面我就带大家来了解一下原型链

原型链初接触

之前写过《你理解Javascript的闭包吗》这篇文章,在介绍闭包的时候首先是从其名字开始介绍的,通过名字大概了解其涉及的知识点。同样在这我们也先从这个名字开始谈起。
原型链,可以分成两部分——原型和链。

对于Javascript,可以看作是一个自上而下的链式结构。我们来看这样的一个例子

例一

function func(){
   this.a = '1';
   this.b = '2';
}
func.prototype.b='3';
func.prototype.c='4';
var f = new func();
console.log(f.a);
console.log(f.b);
console.log(f.c);

对于这个例子先用下图表示其各属性之间的关系

图一

此种链式结构首先会查找func()实例化对象,没有找到要找的数据的话就会再去查找其原型Prototype。所以我们来看上面的例子,f.a在对象f中存在,所以直接打印为1;f.c在对象f中不存在,所以再去查找原型Prototype,所以c打印为4;对于f.b,在对象f和原型Prototype中都存在,但是按照其查找顺序,首先找到的是对象f中的b,所以就直接打印为2。这就是Javascript链式结构的查找机制。

那对于Prototype中的b岂不是就浪费了,对于Prototype中的b我们稍后会解释它在什么情况下会被用到。

下面我们再来看一下原型的概念。

原型

对于原型——也就是Prototype,指的是生成对象的那个方法。在其他编程语言中我们可以理解成类,而Prototype可以认为是static静态属性,它是属于类而不是属于对象的,但是其又可以被对象调用。

既然叫原型,那它肯定是在类下面而不是对象,所以对于上例来说我们可以写成func.prototype.c = ‘4’ 却不可以写成 f.prototype.c = ‘4’,后者是有语法错误的,js调试器会报f.prototype is undefined错误。也就是说f.prototype是错误的,当然也不能说是错误,f.prototype可以看成是prototype是对象f的一个属性,和f.a、f.b没有任何的区别。

所以说从名称上面我们就可以大概了解其原理,从而避免一些不必要的错误。

原型链

上面分别介绍了原型和链两个概念以后,现在我们将二者结合起来看一下原型链以及如何构造一个原型链。

我们可以这样认为,Javascript对象就是一个属性包(当然这些属性都是其自身的属性),并且对象本身还有一条指向其原型的链。当试图访问对象的某个属性的时候,它不仅仅在对象本身查找,还会去对象指向的原型上面去查找,以及该对象原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性抑或是达到原型链的末尾。

还记得上面例一中提到的func.prototype.b这个属性吗,我们对例一进行一下改造

例二

function func(){
   this.a = '1';
   this.b = '2';
}
func.prototype.b = '3';
func.prototype.c = '4';
var f = new func();
 
function func2(){
   this.d = '5';
}
func2.prototype = Object.create(func.prototype);
func2.prototype.c = '6';
var f2 =new func2();
console.log(f.a);
console.log(f2.b);
console.log(f2.d);
console.log(f2.c);
console.log(f.c);

对于这个例子,我们先用一张图来表示其各部分关系

图二

结合上图按照Javascript访问某个对象属性的查找顺序我们可以推出上面各个值。

f.a 毫无疑问值为1;f2.b首先会查找对象f2,没有此属性然后再去查找其原型依然没有,接着再去查找其原型所指的原型发现b=3,所以f2.b打印值为3;f2.d 值为5,这个没有什么疑问;我们先说f.c,它和例一中的情况是一样的,在f对象所指的原型中定义为4,所以在本例中同样f.c的值为4;最后是f2.c,我们看f2的原型指向了f的原型,但是在下面有对f2的原型func2.prototype中的属性c进行了定义即func2.prototype.c=6 所以说f2.c首先在f2的原型中被找到从而打印出来,并不会继续向上一层找,所以f2.c打印值为6,如果说将func2.prototype.c=6注释掉,那f2.c的值其实就是f对象所指原型中的c的值了,也就是4;那虽然说f2的原型指向了f的原型,那对f2的原型增加属性会不会影响f的原型,答案是否定的,也就是说f指向的原型不会受影响。因为我们可以看作是Object.create(func.prototype)实例化了一个f指向的原型的一个对象并将其赋值给f2的原型,所以f的原型本身是不会受影响的。这一点从f.c的值也可以看出来,如果受影响的话那f.c的值不就成了6了吗,可是事实却不是这样的。

通过这个例子相信大家对原型链的概念应该有一个基本的认识吧!

原型链实际应用

我们可以使用原型链来模拟面向对象编程语言中的继承,例子如下

例三

function Action(){
   this.pro1 = 'a';
}
Action.prototype = {
   pro2:'b',
   operate:function(){
      console.log('action');
   }
}
function IndexAction(){
   this.pro1 = 'b';
}
IndexAction.prototype = Object.create(Action.prototype);
var index = new IndexAction();
index.operate();

这个例子可以看成是一个简单的继承关系,其中Action中的pro1是其私有的属性,不会被IndexAction所继承,但是其pro2和方法operate会被继承。而IndexAction中的pro2也是其自己的私有属性。

当然原型链还有在其他方面的应用,这里我们就举一个继承的例子来供大家理解。

原型链的性能

当访问某一对象的属性时,在原型链上查找该属性时比较费时间的,这堆性能有比较坏的影响。这在对性能要求比较苛刻的情况下是比较重要的。除此之外,当我们访问一个不存在的属性的时候,Javascript会遍历整个原型链。因此我们在用原型编写复杂代码前充分了解原型继承的构造是非常有必要的,并且掌握自己代码中原型链的长度在必要的时候结束原型链从而避免上面提到的性能问题。

此外对于Javascript中原生对象的原型不要在上面进行新功能的扩展,除非是为了Javascript的兼容性。

例如

Object.prototype = {
         pro:”pro”,
         operate:function(){}
}

Object是Javascript中原生的对象,我们要尽量避免上述代码出现在我们的程序中。

总之,对于原型链我们应该多在编程过程中去理解,否则即使当时明白,经过一段时间不去写代码的话慢慢的我们也就会淡忘。

除非注明转载,本站文章均为原创,欢迎转载,转载请以链接形式注明出处

本文地址: