定义ES6类方法的差异

原文请戳这里Differences in Defining ES6 Class Methods

在ES6中,有两种普遍的方式用来定义class methods,但他们的行为完全不同。

第一种方式是将class methods定义为标准的类函数。

注意:这种方式,方法定义在类的原型

class A {
    foo() {  
      console.log('foo from A')
    }
  }

但是你也可以把它们定义为实例的具体函数

注意:这种定义方式不是ES6标准语法,但被transform-class-properties支持,因此广为流行。

注意:这种定义方式,方法其实是定义在构造函数内的,即直接定义在实例上的,而不是构造函数的原型上。

class A {
    foo = () => {
        console.log('foo from A')
    }
}

如果你知道ES6的箭头函数那么第一个区别很明显:它将函数调用的this变量绑定到了函数定义范围中的this变量。因此使用第二种方式定义,this被绑定到了当前的类的实例对象上。

我们可以不使用箭头函数,使用标准的class method也能实现相同的功能,只是需要在构造函数内进行额外的绑定。

class A { 
    constructor() {
      this.foo = this.foo.bind(this)
    }

    foo() {
        console.log('foo from A')
    }
}

您现在可能会认为以下类定义具有相同的行为:


定义ES6类方法的差异_第1张图片
0_3jvz23ydPSbkA2r5.png

然而,当我们使用javascript的类继承的时候,两者的行为完全不同。

类继承产生的不同结果:

让我们运行下面的代码:

class A { 
  constructor() {
      this.foo = this.foo.bind(this)
  }

  foo() {
      console.log('foo from A')
  }
}

class B extends A {
    foo() {
      super.foo();
      console.log('foo from B')
    }
}
new B().foo()

预期结果如下:

foo from A
foo from B

现在我们尝试使用箭头函数:

class A { 
    foo = () => {
      console.log('foo from A')
    }
}

class B extends A {
    foo = () => {
        super.foo();
        console.log('foo from B')
    }
}
new B().foo()

我们得到错误:

Cannot read property 'call' of undefined
定义ES6类方法的差异_第2张图片
0_EIbiqVDV59cHNOSg.png

这里发生了什么?

无论何时JavaScript中,当你调用了super.someFunction时,都会在__.proto__中查找someFunction关键词——这就是JavaScript类继承的实现方式。之所以我们在这得到了一个错误,
是因为foo不在new B()实例对象的__proto__上。

这告诉我们,不能在子类中使用箭头函数。箭头函数总是只能通过类实例属性来定义。那么,我们的class与transform-class-properties实际上都做了些什么:当具体的class instance被创建时,首先会在构造函数内附加函数。

 //第二种方式定义方法的本质是在构造函数内附加一个箭头函数,因此this会自动绑定了实例对象。
class A {
    constructor() {
          this.foo = () => {
          console.log('foo from A')
        }
     }
}  

当然,这样做,foo不会出现在__proto__且使得foo在扩展的对象中不可用。如果你需要一个方法在派生类中可以被调用(同时你希望使用ES6的class语法),可以使用class method的简写形式定义。

class A { 
  constructor() {
      this.foo = this.foo.bind(this)
  }

  foo() {
      console.log('foo from A')
  }
}

class B extends A {
    foo() {
         super.foo();
        console.log('foo from B')
    }
}
new B().foo()

最初发表于 cmichel.io

你可能感兴趣的:(定义ES6类方法的差异)