原文请戳这里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')
}
}
您现在可能会认为以下类定义具有相同的行为:
然而,当我们使用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
这里发生了什么?
无论何时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