class 方法的函数声明或表达式?
Function declarations or expressions for class methods?
我和我的一个同事已经讨论过好几次了。有两种方法可以定义 class 方法。第一种方法是使用函数 declaration:
class Action {
public execute(): void {
this.doSomething();
}
...
}
函数声明往往更易于阅读。 Action
的每个实例仅使用一个函数对象,因此它们对内存更友好。
第二个是函数表达式:
class Action {
public execute: () => void = () => {
this.doSomething();
};
...
}
函数表达式需要更多类型(尤其是类型定义),更难阅读,并且会为 Action
的每个实例生成一个新的函数对象。如果您要生成很多很多对象,那就不好了。
然而,函数表达式有一个小好处:它们保留 this
的上下文(即 Action
实例),无论谁调用它们:
var instance = new Action();
setTimeout(instance.execute);
声明为函数表达式的方法在这种情况下按预期工作。函数声明惨遭失败,但可以通过以下方式轻松修复:
var instance = new Action();
setTimeout(() => instance.execute());
// or
setTimeout(instance.execute.bind(instance));
那么,一个被认为比另一个更好的做法,还是纯粹是 situational/preferential?
在我看来,箭头函数应该用作 class 方法,只有当您确定该函数可能会在 this
的不同上下文中调用时(如果它作为事件传递处理程序,例如)并且您更愿意避免使用 Function.prototype.bind.
这有几个原因,包括如您所写的代码可读性,但主要原因是继承。
如果你使用箭头函数,那么你只需将一个函数作为成员分配给实例,但该函数不会添加到原型中:
// ts
class A {
fn1() {}
fn2 = () => {}
}
// js
var A = (function () {
function A() {
this.fn2 = function () { };
}
A.prototype.fn1 = function () { };
return A;
}());
那么,如果您想扩展此 class 并覆盖 fn2
方法会怎样?
因为它是 属性 而不是原型的一部分,所以您需要执行以下操作:
class B extends A {
private oldFn2 = this.fn2;
fn2 = () => {
this.fn2();
}
}
与以下相比,这看起来很糟糕:
class A {
fn1() {}
fn2() {}
}
class B extends A {
fn2() {
super.fn2();
}
}
有几个理由更喜欢使用 bind
方法而不是匿名函数。我发现它更隐含,因为它是 完全 相同的函数,但绑定到特定的 this
。另一方面,在匿名函数中,除了调用实际函数之外,还可以添加更多代码。
另一件事是 bind
函数不仅可以让您绑定哪个对象将被视为 this
,还可以绑定参数:
function fn(one, two, three) {}
fn.bind(null, 1, 2)(3);
fn(1, 2, 3);
这里fn
的两次调用是一样的
您可以使用匿名函数来做到这一点,但并非总是如此:
var a = ["zero", "one", "two", "three", "four", "five"];
function fn(value, index) {
console.log(value, index);
}
// works
a.forEach((item, index) => {
setTimeout(() => {
fn(item, index);
}, 45);
});
// works
for (let i = 0; i < a.length; i++) {
setTimeout(() => {
fn(a[i], i);
}, 45);
}
// doesn't work as i is undefined when the function is invoked
for (var i = 0; i < a.length; i++) {
setTimeout(() => {
fn(a[i], i);
}, 45);
}
// works because the value of i and the value of a[i] are bound
for (var i = 0; i < a.length; i++) {
setTimeout(fn.bind(null, a[i], i), 45);
}
我和我的一个同事已经讨论过好几次了。有两种方法可以定义 class 方法。第一种方法是使用函数 declaration:
class Action {
public execute(): void {
this.doSomething();
}
...
}
函数声明往往更易于阅读。 Action
的每个实例仅使用一个函数对象,因此它们对内存更友好。
第二个是函数表达式:
class Action {
public execute: () => void = () => {
this.doSomething();
};
...
}
函数表达式需要更多类型(尤其是类型定义),更难阅读,并且会为 Action
的每个实例生成一个新的函数对象。如果您要生成很多很多对象,那就不好了。
然而,函数表达式有一个小好处:它们保留 this
的上下文(即 Action
实例),无论谁调用它们:
var instance = new Action();
setTimeout(instance.execute);
声明为函数表达式的方法在这种情况下按预期工作。函数声明惨遭失败,但可以通过以下方式轻松修复:
var instance = new Action();
setTimeout(() => instance.execute());
// or
setTimeout(instance.execute.bind(instance));
那么,一个被认为比另一个更好的做法,还是纯粹是 situational/preferential?
在我看来,箭头函数应该用作 class 方法,只有当您确定该函数可能会在 this
的不同上下文中调用时(如果它作为事件传递处理程序,例如)并且您更愿意避免使用 Function.prototype.bind.
这有几个原因,包括如您所写的代码可读性,但主要原因是继承。 如果你使用箭头函数,那么你只需将一个函数作为成员分配给实例,但该函数不会添加到原型中:
// ts
class A {
fn1() {}
fn2 = () => {}
}
// js
var A = (function () {
function A() {
this.fn2 = function () { };
}
A.prototype.fn1 = function () { };
return A;
}());
那么,如果您想扩展此 class 并覆盖 fn2
方法会怎样?
因为它是 属性 而不是原型的一部分,所以您需要执行以下操作:
class B extends A {
private oldFn2 = this.fn2;
fn2 = () => {
this.fn2();
}
}
与以下相比,这看起来很糟糕:
class A {
fn1() {}
fn2() {}
}
class B extends A {
fn2() {
super.fn2();
}
}
有几个理由更喜欢使用 bind
方法而不是匿名函数。我发现它更隐含,因为它是 完全 相同的函数,但绑定到特定的 this
。另一方面,在匿名函数中,除了调用实际函数之外,还可以添加更多代码。
另一件事是 bind
函数不仅可以让您绑定哪个对象将被视为 this
,还可以绑定参数:
function fn(one, two, three) {}
fn.bind(null, 1, 2)(3);
fn(1, 2, 3);
这里fn
的两次调用是一样的
您可以使用匿名函数来做到这一点,但并非总是如此:
var a = ["zero", "one", "two", "three", "four", "five"];
function fn(value, index) {
console.log(value, index);
}
// works
a.forEach((item, index) => {
setTimeout(() => {
fn(item, index);
}, 45);
});
// works
for (let i = 0; i < a.length; i++) {
setTimeout(() => {
fn(a[i], i);
}, 45);
}
// doesn't work as i is undefined when the function is invoked
for (var i = 0; i < a.length; i++) {
setTimeout(() => {
fn(a[i], i);
}, 45);
}
// works because the value of i and the value of a[i] are bound
for (var i = 0; i < a.length; i++) {
setTimeout(fn.bind(null, a[i], i), 45);
}