五、继承
有了原型链的概念,就可以进行继承。
function B() {}; |
这个时候产生了B的原型B.prototype
原型本身就是一个Object对象,我们可以看看里面放着哪些数据
B.prototype 实际上就是 {constructor : B , [[Prototype]] : Object.prototype}
因为prototype本身是一个Object对象的实例,所以其原型链指向的是Object的原型
B.prototype = A.prototype;//相当于把B的prototype指向了A的prototype;这样只是继承了A的prototype方法,A中的自定义方法则不继承 B.prototype.thisisb = "this is constructor B";//这样也会改变a的prototype |
但是我们只想把B的原型链指向A,如何实现?
第一种是通过改变原型链引用地址
B.prototype.__proto__ = A.prototype; |
ECMA中并没有__proto__这个方法,这个是ff、chrome等js解释器添加的,等同于EMCA的[[Prototype]],这不是标准方法,那么如何运用标准方法呢?
我们知道new操作的时候,实际上只是把实例对象的原型链指向了构造函数的prototype地址块,那么我们可以这样操作
B.prototype = new A(); |
这样产生的结果是:
产生一个A的实例,同时赋值给B的原型,也即B.prototype 相当于对象 {width :10 , data : [1,2,3] , key : "this is A" , [[Prototype]] : A.prototype}
这样就把A的原型通过B.prototype.[[Prototype]]这个对象属性保存起来,构成了原型的链接
但是注意,这样B产生的对象的构造函数发生了改变,因为在B中没有constructor属性,只能从原型链找到A.prototype,读出constructor:A
var b = new B; console.log(b.constructor);//output A |
所以我们还要人为设回B本身
B.prototype.constructor = B; //现在B的原型就变成了{width :10 , data : [1,2,3] , key : "this is A" , [[Prototype]] : A.prototype , constructor : B} console.log(b.constructor); //output B //同时B直接通过原型继承了A的自定义属性width和name console.log(b.data); //output [1,2,3] //这样的坏处就是 b.data.push(4); //直接改变了prototype的data数组(引用) var c = new B; alert(c.data); //output [1,2,3,4] //其实我们想要的只是原型链,A的自定义属性我们想在B中进行定义(而不是在prototype) //该如何进行继承? //既然我们不想要A中自定义的属性,那么可以想办法把其过滤掉 //可以新建一个空函数 function F(){} //把空函数的原型指向构造函数A的原型 F.prototype = A.prototype; //这个时候再通过new操作把B.prototype的原型链指向F的原型 B.prototype = new F; //这个时候B的原型变成了{[[Prototype]] : F.prototype} //这里F.prototype其实只是一个地址的引用 //但是由B创建的实例其constructor指向了A,所以这里要显示设置一下B.prototype的constructor属性 B.prototype.constructor = B; //这个时候B的原型变成了{constructor : B , [[Prototype]] : F.prototype} |
图示如下,其中红色部分代表原型链:
作为初学者浅陋的理解,本文目的在于更具象地去理解js的面向对象,疏漏之处请指正。