小谈js原型链和继承

原型(prototype)在js中但是担当着举足轻重的做用,原型的实现则是在原型链的基础上,理解原型链的原理后,对原型的使用会更加自如,也能体会到js语言的魅力。函数

 

本文章会涉及的内容this

  • 原型及原型对象
  • 原型链(JavaScript核心部分)
  • 类的继承
  • instanceof
  • constructor

 

咱们先用一个构造器来实现一个构造函数:spa

function A(){
    this.mark = "A";
    this.changeMark = function(){
        this.mark += "_changed";
    }
}

A.prototype.mark2 = "A2";
A.prototype.changeMark2 = function(){
    this.mark2 += "_changed";
}

var a = new A();
var a2 = new A();


//下面则说明构造函数实例化后,分配着不一样的实例对象,互不相关
console.log(a.mark);  //"A"
console.log(a2.mark); //"A"
    a.changeMark();   //使用实例对象中的方法
console.log(a.mark);  //"A_changed"
console.log(a2.mark); //"A"

//下面则说明了new操做符的一项做用,即将原型中的this指向当前对象,
//在a.changeMark2执行时,changMark2中的方法先找 this.mark2 的值,
//可是实例对象this中没有mark2值,则在原型链(后面会介绍)向上寻找,获得A原型对象中的mark2值,
//在赋值时,将修改后的值添加在了a实例中。
//总:虽然调用的是prototype方法,可是不会对prototype属性作修改,只会说是在实例中新增属性,可是在使用时,会最使用最近获得的属性(在后面原型链中能够加以理解)
console.log(a.mark2);  //"A2"
console.log(a2.mark2); //"A2"
    a.changeMark2();   //使用原型链中的方法
console.log(a.mark2);  //"A2_changed"
console.log(a2.mark2); //"A2"

 

 

为何a可使原型中的changeMark2方法?这就和js巧妙的原型链相关,在Firefox中咱们能够打印出对象并可查看到对象下面的__proto__。prototype

咱们把上面的过程用流程图来表示:code

 

 

只有构造函数才会有prototype属性,而实例化出来的对象会拥有__proto__,而不会有prototype对象

 

就像上图画的那样,两个实例化的对象都经过__proto__属性指向了A.prototype(即构造函数的原型对象blog

而原型对象的__proto__指向Object对象,就像a.toString()的toString方法就是存在于Object原型对象(Object.prototype)中。继承

 

so:当使用对象的方法或属性时,对象会在一步一步经过__proto__向上寻找,找到最近的则是最终的获取到的方法或属性ip

  ————这就是js中的原型链原型链

 

就像图上看到的同样,全部对象的原型链最终都指向了Object对象,而Object的原型对象(Object.prototype)是为数很少的不继承自任何属性的对象,即Object.prototype没有__proto__,是原型链的顶峰。

 

经过上面咱们能够了解到,当咱们对A.prototype或Object.prototype添加属性或方法时,在a和a2实例中都会查看到该属性或方法,由于这两个实例都经过原型链与A和Object的原型对象相连。

 

 再来看看原型对象和原型链在继承方面的实现:

再构造一个函数A和一个函数B,并让B继承A,以下:

function A(mark){
    this.mark = mark;
}
A.prototype.getMark = function(){
    return this.mark;
}

function B(mark){
  this.mark = mark
}

//var temp = new A("A");
//B.prototype = temp;
//上面语句和下语句做用相同

B.prototype = new A("A"); //实例化一个A,其赋值于B.prototype
                //咱们知道了原型链,这样作就是将B的原型对象与A的原型对象经过原型链(__proto__)链接起来
                //以实现继承
var b = new B("B"); console.log(b.mark); //B, 结果如上面原型链分析的那样,向上找到最近的属性,则为b实例中的mark:"B"

 

 

 

其中的结构示意大概以下图:

 

 

这时咱们能够看到,在B.prototype中是没有constructor的,由于B.prototype只是简单的new A("A")对象赋值的结果。

在js中的constructor有什么做用呢?如:

var arr = new Array();
arr instanceof Array;      //true
arr.constructor === Array; //true


function TEMP(){
}
var temp = new TEMP();
temp instanceof TEMP;      //true
temp.constructor === TEMP; //true

 

 

 引用《JavaScript权威指南》中的对于constructor的解释为:对象一般继承的constructor均指代它们的构造函数,而构造函数是类的“公共标识”。constructor可用来判断对象所属的类

 

在上面的小例子中,用instanceof也可判断对象的类,可是有自身的缺陷,instanceof的实现方法为:

instanceof不会去检查temp是否是由TEMP()构造函数初始化的,面是判断temp是否继承自TEMP.prototype,这样,范围就宽了不少。

如在上面的大例中,使用

b instaceof B //true 由于在b的原型链中能够找到B.prototype对象

b instaceof A //true 在b的原型链中也能够找到A.prototype对象

 

能够说instanceof是用来检测继承关系的。

而当

console.log(b.constructor) //function A()
//由于在b的原型链中,最近的constructor就是A.prototype中有constructor指向了构造函数A();

 

但咱们知道的b是属于B类的,那最后因此要作的就是:

B.prototype.constructor = B; //将constructor指向自身的构造函数


var new_b = new B("B");
console.log(new_b.constructor) //function B() 

 这样,一个完整的类继承才完成了。

 

 

最后附上一个完整继承后的结果图: 

 

 

第一次写长文,若有问题,还请你们指出。

转载请注明出处,谢谢。

 

Finish.

相关文章
相关标签/搜索