《悟透javascript》学习笔记:X、深入继承

来源:互联网 发布:最好看的步兵 知乎 编辑:程序博客网 时间:2024/05/20 05:04

引言

 

      JavaScript不是按面向对象的思想设计的程序语言,所以它不具备像现有的面向对象的语言那样的功能,但是面向对象的思想是如此的深入人心,以至于JavaScript也削尖了脑袋“面向对象”。果真,通过一些特殊的处理,JavaScript也具有了基本的面向对象的功能。

 

functionnewprototypeconstructor

 

      看代码:

      <script language="javascript">

function Person(name){  

      this.name=name;  

      this.showMe=function(){  

         alert(this.name);  

      }  

};  

var one=new Person('JavaScript');  

one.showMe();

</script>

 

很多人见到了久违的new操作符,于是就叫Person为“类”,可是又没有关键字class的出现,觉得叫“类”有点勉强。于是退而求其次叫Person为类的构造函数。这些概念好像都没有错,之所以出现这样的情况,可能是因为大家都学习了传统的面向对象语言(c++c#java等),还有一种思维定势吧。为了让javascript也面向对象,要在javascript中找到与传统面向对象语言的影子。可是按照javascript的说法,function定义的这个Person就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。这个区别就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性

 

prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。有点头晕,看下图吧:

 

 

      下面举例证明:

<script language="javascript">

function Person(name){  

      this.name=name;  

      this.showMe=function(){  

         alert(this.name);  

      }  

};  

var one = new Person('JavaScript'); 

//结果undefined,证明new出的对象无prototype属性

alert(one.prototype);  

//结果object,证明function定义的对象有prototype属性

alert(typeof Person.prototype);     

//结果true,证明function定义的对象prototypeconstructor属性指向的对象就是function定义的对象

alert(Person.prototype.constructor == Person)

</script>

 

对象创建三部曲与__proto__属性

 

      看代码:

<script language="javascript">

function Person(name){  

      this.name=name;  

      this.showMe=function(){  

         alert(this.name);  

      }  

}

Person.prototype.say = function(){alert("i'm from prototype");}

 

var one = new Person('JavaScript');

one.showMe();

one.say(); 

</script>

 

      上面可以看到,不论是showMe还是say方法,我们都可以调用到。要解释这个结果就要仔细研究一下new这个操作符了,实际上对象的创建可以分为三个步骤:

1.       var one = {};  这是创建对象one

2.       one.__proto__ = Person.prototype;  这是将步骤1所创建的对象的“隐式链”设置成person的原型对象。“__proto__”被称为“隐式链”,不对外开放,但firefox浏览器对外开放,如果要实验请到firefox上实验。隐式链上的方法和属性是通过对象名直接访问的,所有对象都有“隐式链”,通过这一步,one对象就拥有了Person.prototype下的所有属性和方法,可以通过one直接访问。

3.       Person.call(one);  这是将步骤1所创建的对象one作为person构造函数的this并调用person函数。这样one对象的this就拥有了person构造函数中的所有属性和方法。

 

其中第二步中出现了一个新名词就是“隐式链”(__proto__属性, __proto__指向了函数Personprototype对象。在personprototype对象中出现的任何属性或者函数都可以在one对象中直接使用,这个就是javascript中的原型继承了。又头晕了,上图吧!

 

 

 

这样one对象通过内置的原型对象 (__proto__属性)就可以直接访问Personprototype对象中的任何属性与方法了。这也就解释了上面的代码中为什么one可以访问from函数了。因为prototype对象中有一个constructor属性,那么one也可以直接访问constructor属性。代码:

 

<script language="javascript">

function Person(name){  

      this.name=name;  

      this.showMe=function(){  

         alert(this.name);  

      }  

}

Person.prototype.say = function(){alert("i'm from prototype");}

 

var one = new Person('JavaScript');

//结果true,证明上面所说的one.__proto__ = Person.prototype;是成立的

alert(one.constructor == Person.prototype.constructor);

alert(one.constructor);

alert(Person.prototype.constructor);

//结果“JavaScript”,证明上面所说的Person.call(one);是成立的

alert(one.name);

</script>

 

继承的实现

 

      看代码:

<script language="javascript">

function person(name){  

      this.name=name;  

      this.showMe=function(){  

         alert(this.name);  

      }  

}

person.prototype.say = function(){alert("i'm from prototype");}

 

function employee(){

}

employee.prototype = new person();

 

var bill = new employee();

bill.say();

//结果为true,原因是bill.__proto__=person.prototype

alert(bill.constructor == person.prototype.constructor);

alert(bill.constructor);

</script>

 

继承的实现很简单,只需要把子类的prototype设置为父类的一个对象即可。注意这里说的可是对象哦!(就是必须用new那么通过prototype属性实现继承的原理是什么呢?还是先看图形说明,然后编写代码进行验证。

 

 

 

      ­­­­__proto__是隐式链,所有对象都有__proto__属性,__proto__所属对象可以直接访问其下的属性和方法,__proto__属性的指向决定了对象可以访问哪里方法和属性。

 

      上图不知道大家是否能够理解,最重要的是中间两个的关系似乎不那么好理解,因为之前所说的new可以理解为:

1          employee.prototype.__proto__ = person.prototype

2          person.call(employee.prototype)

我们最重要的是改变了employee.prototype__proto__属性,关键就在这里,__proto__指向person.prototypeemployee.prototype做拥有的所有东西都会丢失,取而代之的是person.prototype,或者说new person()对象替代了employee.prototype对象更好理解。反过来也可以说只要__proto__的指向改变了就说明使用了new

 

      好了,下面就解释为什么这样就模拟了面向对象的类的继承。首先来看bill对象,它是由employee构造函数创建而来,但它却可以使用personprototype下的方法,我们根据上图来一步步查找。首先,在bill对象中查找,然后到bill对象的__proto__所指向的对象employee.prototype对象(new person()对象)查找,再到employee.prototype对象的__proto__所指向的personprototype对象中查找,结果say方法就在personprototype对象中找到了,然后调用它。如果划分为步骤则如下:

1          bill = new employee(); //就先在employee()构造函数中查找

2          bill.__proto__ = employee.prototype = new person(); //employee.prototype中查找,也就是在new person()对象中查找

2.1        employee.prototype.__proto__ = person.prototype  //person.prototype中查找

2.2        person.prototype.__proto__ = object.prototype; //object.prototype中查找

 

这样的一个过程不就是模拟了类的继承吗?也许是我自己的思路还不够清晰,所以我的描述也只能到此为止了。