ES5和ES6中对于继承的实现方法

来源:互联网 发布:网络测试工程师面试题 编辑:程序博客网 时间:2024/04/30 03:28

在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方。

原型链的特点和实现已经在之前的一篇整理说过了,就是通过将子类构造函数的原型作为父类构造函数的实例(sub.prototype=new super),这样就连通了子类-子类原型-父类,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层。

一个基本的基于原型链的继承过程大概是这样的:

//先来个父类,带些属性function Super(){    this.flag = true;}//为了提高复用性,方法绑定在父类原型属性上Super.prototype.getFlag = function(){    return this.flag;}//来个子类function Sub(){    this.subFlag = false;}//实现继承Sub.prototype = new Super;//给子类添加子类特有的方法,注意顺序要在继承之后Sub.prototype.getSubFlag = function(){    return this.subFlag;}//构造实例var es5 = new Sub;

原型链实现的继承主要有几个问题:

1、本来我们为了构造函数属性的封装私有性,方法的复用性,提倡将属性声明在构造函数内,而将方法绑定在原型对象上,但是现在子类的原型是父类的一个实例,自然父类的属性就变成子类原型的属性了;这就会带来一个问题,我们知道构造函数的原型属性在所有构造的实例中是共享的,所以原型中属性的改变会反应到所有的实例上,这就违背了我们想要属性私有化的初衷;
2、创建子类的实例时,不能向父类的构造函数传递参数

function Super(){    this.flag = true;}function Sub(){   this.subFlag = false;}Sub.prototype = new Super;var obj = new Sub();obj.flag = flase;  //修改之后,由于是原型上的属性,之后创建的所有实例都会受到影响var obj_2 = new Sub();console.log(obj.flag)  //false;

为了解决以上两个问题,有一个叫借用构造函数的方法
只需要在子类构造函数内部使用apply或者call来调用父类的函数即可在实现属性继承的同时,又能传递参数,又能让实例不互相影响

function Super(){    this.flag = true;}function Sub(){    Super.call(this)  //如果父类可以需要接收参数,这里也可以直接传递}var obj = new Sub();obj.flag = flase;var obj_2 = new Sub();console.log(obj.flag)  //依然是true,不会相互影响

结合借用构造函数和原型链的方法,可以实现比较完美的继承方法,可以称为组合继承

function Super(){    this.flag = true;}Super.prototype.getFlag = function(){    return this.flag;     //继承方法}function Sub(){    this.subFlag = flase    Super.call(this)    //继承属性}Sub.prototype = new Super;var obj = new Sub();Sub.prototype.constructor = Sub;Super.prototype.getSubFlag = function(){    return this.flag;}
function Box(age) {this.name = ['Lee', 'Jack', 'Hello']this.age = age;}Box.prototype.run = function () {return this.name + this.age;};function Desk(age) {Box.call(this, age); //对象冒充}Desk.prototype = new Box(); //原型链继承var desk = new Desk(100);alert(desk.run());


这里还有个小问题,Sub.prototype = new Super; 会导致Sub.prototype的constructor指向Super;
然而constructor的定义是要指向原型属性对应的构造函数的,Sub.prototype是Sub构造函数的原型,所以应该添加一句纠正:
Sub.prototype.constructor = Sub;

看完ES5的实现,再来看看ES6的继承实现方法,其内部其实也是ES5组合继承的方式,通过call借用构造函数,在A类构造函数中调用相关属性,再用原型链的连接实现方法的继承

class B extends A {  constructor() {    return A.call(this);  //继承属性  }}A.prototype = new B;  //继承方法

ES6封装了class,extends关键字来实现继承,内部的实现原理其实依然是基于上面所讲的原型链,不过进过一层封装后,Javascript的继承得以更加简洁优雅地实现

class ColorPoint extends Point {  constructor(x, y, color) {    super(x, y); // 等同于parent.constructor(x, y)    this.color = color;  }  toString() {    return this.color + ' ' + super.toString(); // 等同于parent.toString()  }}

通过constructor来定义构造函数,用super调用父类的属性方法

转至:http://www.jianshu.com/p/342966fdf816

0 0
原创粉丝点击