类方法的函数声明或表达式?

Cra*_*xal 2 methods function-declaration typescript

我和我的一个同事已经多次讨论过这个问题。定义类方法有两种方法。第一种方法是使用函数声明

class Action {
    public execute(): void {
        this.doSomething();
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

函数声明往往更容易阅读。的每个实例仅使用一个函数对象Action,因此它们也更节省内存。

第二个是函数表达式

class Action {
    public execute: () => void = () => {
        this.doSomething();
    };
    ...
}
Run Code Online (Sandbox Code Playgroud)

函数表达式需要更多的输入(尤其是类型定义),更难阅读,并且会为 的每个实例生成一个新的函数对象Action。如果您生成大量对象,那就很糟糕了。

然而,函数表达式有一个小好处:无论谁调用它们,它们都会保留上下文this(即实例):Action

var instance = new Action();
setTimeout(instance.execute);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,声明为函数表达式的方法按预期工作。函数声明会严重失败,但可以通过这样做轻松修复它们:

var instance = new Action();

setTimeout(() => instance.execute());
// or
setTimeout(instance.execute.bind(instance));
Run Code Online (Sandbox Code Playgroud)

那么,一种做法是否被认为比另一种更好,或者这纯粹是情况/偏好?

Nit*_*mer 6

在我看来,只有当您确定可以使用不同的上下文调用该函数this(例如,如果它作为事件处理程序传递)并且您希望避免使用Function.prototype时,才应将箭头函数用作类方法.绑定

造成这种情况的原因有很多,如您所写,包括代码可读性,但主要原因是继承。如果您使用箭头函数,那么您只需将一个函数作为成员分配给实例,但该函数不会添加到原型中:

// ts
class A {
    fn1() {}

    fn2 = () => {}
}

// js
var A = (function () {
    function A() {
        this.fn2 = function () { };
    }
    A.prototype.fn1 = function () { };
    return A;
}());
Run Code Online (Sandbox Code Playgroud)

那么如果你想扩展这个类并重写这个fn2方法会发生什么呢?
因为它是一个属性而不是原型的一部分,所以您需要执行以下操作:

class B extends A {
    private oldFn2 = this.fn2;

    fn2 = () => {
        this.fn2();
    }
}
Run Code Online (Sandbox Code Playgroud)

与以下相比,这看起来很糟糕:

class A {
    fn1() {}

    fn2() {}
}

class B extends A {
    fn2() {
        super.fn2();
    }
}
Run Code Online (Sandbox Code Playgroud)

bind相比匿名函数,更喜欢使用该方法有几个原因。我发现它更加隐式,因为它是完全相同的函数,但绑定到特定的this. 另一方面,在匿名函数中,除了调用实际函数之外,您还可以添加更多代码。

另一件事是,该bind函数不仅允许您绑定将被视为 的对象this,还可以绑定参数:

function fn(one, two, three) {}
fn.bind(null, 1, 2)(3);
fn(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

这里的两次调用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);
}
Run Code Online (Sandbox Code Playgroud)