es5继承和es6继承中静态方法、静态属性的差异

5,229 阅读3分钟

最近在看vue的源码,其中有一部分牵扯到了继承,看到es5中静态方法的"继承"是这样写的

      const Sub = function VueComponent (options) {
        this._init(options)
      }
      Sub.prototype = Object.create(Super.prototype)
      Sub.prototype.constructor = Sub
        ...
      Sub.extend = Super.extend
      Sub.mixin = Super.mixin
      Sub.use = Super.use

es5的继承大家都知道(通过原型链实现原型属性的继承,通过构造函数实现实例属性的继承),但代码中静态方法是直接从父类赋给子类的,这就想到的一个问题?静态方法没有继承吗?带着这个问题研究了一下静态方法在es5继承中和es6继承中的差异

es5中静态方法的继承

    // 定义父类
    function Super(name, age) {
      this.name = name;
      this.age = age;
    };
    Super.prototype.sayName = function() {
      return this.name;
    };
    // 定义属性
    Super.num = 1;
    // 定义静态方法
    Super.sayWord = function(word) {
      return word;
    };
    // 定义子类
    function Sub(name, age, sex) {
      // 继承实例属性
      Super.call(this, name, age);
      this.sex = sex;
    }
    // 继承父类的原型方法+原型属性
    Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;
    
    var instance = new Sub('张三', '18', '男');

分别打印出来看下结果:

    console.log(Super.sayWord('hello world'));
    // print TypeError: Sub.sayWord is not a function
    
    console.log(Sub.sayWord('hello world'));
   // print TypeError: Sub.sayWord is not a function
    
    console.log(instance.sayWord('hello world'));  //  实例是无法获取到静态方法的
    // print instance.sayWord is not a function

静态属性和静态方法类似,大家可以自行验证,通过上面的我们可以得出下面的结论: es5的类继承:子类是无法继承父类的静态属性和静态方法的,只是通过传递

其中有个需要注意的地方:静态方法没办法获取到this,由于JavaScript中的类也是一个对象,静态方法相当于是这个类对象的一个属性方法,但是this指的是当前实例对象而不是类对象,所以是无法取到的,下面看下es6中的

es6中静态方法的继承

class Super {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayName() {
    return this.name;
  }

  static sayWord(word) {
    return word;
  }

  static get num() {
    return this._num; // 因为类也是一个对象 所以_num 只是这个类上的属性 并非实例属性
  }

  static set num(n) {
    this._num = n;
  }
}

class Sub extends Super {
  constructor(name, age, sex) {
    super(name, age);
    this.sex = sex;
  }
}

const instance = new Sub('张三', '18', '男');

下面我们分别看下静态方法和静态属性

console.log(Super.sayWord('hello world'));
// print hello world
console.log(Sub.sayWord('hello world'));
// print hello world
console.log(instance.sayWord('hello world'));
// print TypeError: instance.sayWord is not a function

可以看出es6继承中,静态方法被继承下来,这点与es5还是有差异的,再看静态属性, es6中的静态属性可以直接[类名].[属性名]这样的定义,也可以如上面那样定义(get,set),只是get,set这样的定义我们可以更好进行的控制

Super.num = 3;
console.log(Super.num);
// print 3
console.log(Sub.num);
// print 3

看的出,静态属性也被继承了,但是这样也有问题,当静态属性是引用类型时,子类和父类指向的同一个地址,父类如果发生变化子类也会跟着变化,这是我们不希望看到的

Super.num = [];

Sub.num.push(1);
console.log(Super.num);
// print [1]
console.log(Sub.num);
// print [1]

Super.num.push(2);
console.log(Super.num);
// print [1,2]
console.log(Sub.num);
// print [1,2]

我们修改下静态属性num的get方法,使其只获取自己当前类的值,而非继承的值

  static get num() {
    return this.hasOwnProperty('_num') ? this._num : undefined;
  }

再测试一下:

Super.num = [];
// Sub.num.push(1);
// print TypeError: Cannot read property 'push' of undefined 因为Sub.num得到的是 undefined
Sub.num=1;
console.log(Super.num);
// print []
console.log(Sub.num);
// print 1
Super.num.push(2);
console.log(Super.num);
// print [2]
console.log(Sub.num);
// print 1

可以得出:es6中的静态方法和静态属性都可以被继承下来的,只是静态属性的继承需要稍作处理,否则就被共享了

下面我们再总结下:

  1. es5和es6实现继承的方式是不同的,前者通过原型链实现对父类原型方法、原型属性的继承,通过构造函数的调用实现对实例方法,实例属性的调用,后者通过extends关键字实现继承
  2. es5中静态方法、静态属性是无法通过继承下来的,只能通过赋值传递,如最开始的demo,但es6中则是可以的