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

Methods 类方法的函数声明或表达式?,methods,typescript,function-declaration,Methods,Typescript,Function Declaration,我和我的一位同事已经讨论过好几次了。定义类方法有两种方法。第一种方法是使用函数声明: 函数声明往往更易于阅读。每个动作实例只使用一个函数对象,因此它们对内存更友好 第二个是函数表达式: 函数表达式需要更多的类型,尤其是类型定义,更难阅读,并且为每个操作实例生成一个新的函数对象。如果你正在生成很多很多的对象,那就不好了 但是,函数表达式有一个小小的好处:它们保留了该实例的上下文,即操作实例,无论谁调用它们: var instance = new Action(); setTimeout(insta

我和我的一位同事已经讨论过好几次了。定义类方法有两种方法。第一种方法是使用函数声明:

函数声明往往更易于阅读。每个动作实例只使用一个函数对象,因此它们对内存更友好

第二个是函数表达式:

函数表达式需要更多的类型,尤其是类型定义,更难阅读,并且为每个操作实例生成一个新的函数对象。如果你正在生成很多很多的对象,那就不好了

但是,函数表达式有一个小小的好处:它们保留了该实例的上下文,即操作实例,无论谁调用它们:

var instance = new Action();
setTimeout(instance.execute);
在本例中,声明为函数表达式的方法按预期工作。函数声明失败得很惨,但这样做很容易修复它们:

var instance = new Action();

setTimeout(() => instance.execute());
// or
setTimeout(instance.execute.bind(instance));

那么,是一个被认为比另一个更好的实践,还是这纯粹是情境性的/优先的?

在我看来,只有当您确定如果函数作为事件处理程序传递,并且希望避免使用时,才应该使用arrow函数作为类方法

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

// ts
class A {
    fn1() {}

    fn2 = () => {}
}

// js
var A = (function () {
    function A() {
        this.fn2 = function () { };
    }
    A.prototype.fn1 = function () { };
    return A;
}());
那么,如果您想要扩展这个类并重写fn2方法,会发生什么呢? 因为它是一个属性,而不是原型的一部分,所以您需要执行以下操作:

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

    fn2 = () => {
        this.fn2();
    }
}
相比之下,这看起来很糟糕:

class A {
    fn1() {}

    fn2() {}
}

class B extends A {
    fn2() {
        super.fn2();
    }
}
与匿名函数相比,有几个理由更喜欢使用bind方法。我发现它更含蓄,因为它是完全相同的函数,但绑定到一个特定的函数。另一方面,在匿名函数中,除了调用实际函数之外,还可以添加更多代码

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

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);
}

在我看来,箭头函数应该作为类方法使用,前提是您确实知道,如果它作为事件处理程序传递,则可能会使用不同的上下文来调用该函数,例如,您希望避免使用

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

// ts
class A {
    fn1() {}

    fn2 = () => {}
}

// js
var A = (function () {
    function A() {
        this.fn2 = function () { };
    }
    A.prototype.fn1 = function () { };
    return A;
}());
那么,如果您想要扩展这个类并重写fn2方法,会发生什么呢? 因为它是一个属性,而不是原型的一部分,所以您需要执行以下操作:

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

    fn2 = () => {
        this.fn2();
    }
}
相比之下,这看起来很糟糕:

class A {
    fn1() {}

    fn2() {}
}

class B extends A {
    fn2() {
        super.fn2();
    }
}
与匿名函数相比,有几个理由更喜欢使用bind方法。我发现它更含蓄,因为它是完全相同的函数,但绑定到一个特定的函数。另一方面,在匿名函数中,除了调用实际函数之外,还可以添加更多代码

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

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);
}

杰出的我甚至没有考虑过继承/原型场景。即使某个方法可以用作事件处理程序,我认为大多数情况下都可以使用上述匿名函数包装器来缓解。@Craxal我更喜欢使用绑定函数而不是匿名函数,但在这两种情况下都不太麻烦。首选绑定有什么好处吗?例如,我想你不必担心显式处理和传递参数。我甚至没有考虑过继承/原型场景。即使某个方法可以用作事件处理程序,我认为大多数情况下都可以使用上述匿名函数包装器来缓解。@Craxal我更喜欢使用绑定函数而不是匿名函数,但在这两种情况下都不太麻烦。首选绑定有什么好处吗?例如,我想您不必担心显式处理和传递参数。