# js的继承

# 第一种: 借助call

function Parent1(){ this.name = 'parent1'; } function Child1(){ Parent1.call(this); this.type = 'child1' } console.log(new Child1); 复制代码这样写的时候子类虽然能够拿到父类的属性值,但是问题是父类原型对象中一旦存在方法那么子类无法继承。那么引出下面的方法。

# 第二种: 借助原型链

  function Parent2() {
    this.name = 'parent2';
    this.play = [1, 2, 3]
  }
  function Child2() {
    this.type = 'child2';
  }
  Child2.prototype = new Parent2();

  console.log(new Child2());

看似没有问题,父类的方法和属性都能够访问,但实际上有一个潜在的不足。举个例子:

  var s1 = new Child2();
  var s2 = new Child2();
  s1.play.push(4);
  console.log(s1.play, s2.play);

可以看到控制台:

明明我只改变了s1的play属性,为什么s2也跟着变了呢?很简单,因为两个实例使用的是同一个原型对象。 那么还有更好的方式么?

# 第三种:将前两种组合

  function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
  }
  function Child3() {
    Parent3.call(this);
    this.type = 'child3';
  }
  Child3.prototype = new Parent3();
  var s3 = new Child3();
  var s4 = new Child3();
  s3.play.push(4);
  console.log(s3.play, s4.play);

可以看到控制台:

之前的问题都得以解决。但是这里又徒增了一个新问题,那就是Parent3的构造函数会多执行了一次(Child3.prototype = new Parent3();)。这是我们不愿看到的。那么如何解决这个问题?

# 第四种: 组合继承的优化1

function Parent4 () { this.name = 'parent4'; this.play = [1, 2, 3]; } function Child4() { Parent4.call(this); this.type = 'child4'; } Child4.prototype = Parent4.prototype; 这里让将父类原型对象直接给到子类,父类构造函数只执行一次,而且父类属性和方法均能访问,但是我们来测试一下:

  var s3 = new Child4();
  var s4 = new Child4();
  console.log(s3)

子类实例的构造函数是Parent4,显然这是不对的,应该是Child4。

# 第五种(最推荐使用): 组合继承的优化1

  function Parent5 () {
    this.name = 'parent5';
    this.play = [1, 2, 3];
  }
  function Child5() {
    Parent5.call(this);
    this.type = 'child5';
  }
  Child5.prototype = Object.create(Parent5.prototype);
  Child5.prototype.constructor = Child5;

这是最推荐的一种方式,接近完美的继承,它的名字也叫做寄生组合继承。 ES6的extends被编译后的JavaScript代码 ES6的代码最后都是要在浏览器上能够跑起来的,这中间就利用了babel这个编译工具,将ES6的代码编译成ES5让一些不支持新语法的浏览器也能运行。 那最后编译成了什么样子呢?

function _possibleConstructorReturn(self, call) {
    // ...
    return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
}

function _inherits(subClass, superClass) {
    // ...
    //看到没有
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}


var Parent = function Parent() {
    // 验证是否是 Parent 构造出来的 this
    _classCallCheck(this, Parent);
};

var Child = (function (_Parent) {
    _inherits(Child, _Parent);

    function Child() {
        _classCallCheck(this, Child);

        return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
    }

    return Child;
}(Parent));

核心是_inherits函数,可以看到它采用的依然也是第五种方式————寄生组合继承方式,同时证明了这种方式的成功。不过这里加了一个Object.setPrototypeOf(subClass, superClass),这是用来干啥的呢? 答案是用来继承父类的静态方法。这也是原来的继承方式疏忽掉的地方