JavaScript:如何创建一个对'this'(例如'var _this = this')的引用,与 bind/call/apply有什么区别?

4

在错误的上下文中依赖this是一个常见的陷阱。以下是一个人为制造的例子:

function SomeClass() {
    this.prop = 42;
    document.body.addEventListener('click', function() {
        alert(this.prop); // will be undefined
    });
}
new SomeClass();

一种解决方法是使用 bind(或 callapply),例如:
function SomeClass() {
    this.prop = 42;
    document.body.addEventListener('click', (function() {
        alert(this.prop); // works
    }).bind(this));
}
new SomeClass();

另一个选项是通过闭包使this可用,通常使用_thisself

function SomeClass() {
    var _this = this;
    this.prop = 42;
    document.body.addEventListener('click', function() {
        alert(_this.prop); // works
    });
}
new SomeClass();

我更喜欢后一种方法,因为我只需要声明一次,而不是用大量的bind()函数和嵌套回调(虽然这不是理想的,但在实际开发中很常见)变得更简单。但我隐约感到:要么a) 这种方法存在陷阱,或者b) 我必须首先这样做或使用bind,这意味着我做错了。你有什么想法吗?

2个回答

3
你解决“这个问题”的方法是可行的:

function SomeClass() {
    this.prop = 42;
    document.body.addEventListener('click', (function() {
        alert(this.prop); // works
    }).bind(this));
}
new SomeClass();

如果你发现需要这样做,那么你并没有做错什么,但根据上下文,在担心性能的情况下,也许你不想创建新的函数(这就是 .bind 内部做的事情,以改变 this)。

除了进行绑定之外,你可以使用作用域变量,就像你的第二个例子:


function SomeClass() {
    var _this = this;
    this.prop = 42;
    document.body.addEventListener('click', function() {
        alert(_this.prop); // works
    });
}
new SomeClass();

这里唯一剩下的问题,就是代码可读性,但我认为这只是个人口味问题。针对这种情况,我会这样做:


function SomeClass() {
    var prop = 42;
    document.body.addEventListener('click', function() {
        alert(prop); // works
    });
}
new SomeClass();

我只是提高了可读性和可能稍微提高了性能,但我并不意味着这是“正确”的方法,我想说的是你应该知道如何使用词法作用域和闭包,并在你认为必须使用它们(或不使用)的地方/时间使用它们(当然一切都取决于上下文)。
编辑:等等!我将 this.prop = 42 赋值是因为我需要暴露这个属性。在这种情况下,我认为从设计的角度来看,最佳实践是通过 getter 暴露它。

function SomeClass() {
    var prop = 42;
    document.body.addEventListener('click', function() {
        alert(prop); // works
    });
    this.getProp = function() {
      return prop;
    };

    this.setProp = function(newValue) { // prop is supposed to be modifiable ?
      prop = newValue;
    };
}
new SomeClass();

关于设计方面的另一个注意事项与“this”问题无关,从类中与全局对象进行交互并不合适,我会这样做:


function SomeClass(receiver) {
    var prop = 42;
    receiver.addEventListener('click', function() {
        alert(prop); // works
    });
}
new SomeClass(document.body);

是的,众所周知 bind 由于“不寻常”的规范要求而较慢,但通常在使用 bind 的地方性能不应该成为问题。 - Bergi
在我看来,可读性足以成为避免使用bind的正当理由。 - dseminara
1
很多人会争论使用 bind 可以增加可读性... 当然,你的 var prop 是一个不错的特定情况的改进,但当 prop 必须是一个属性时,OP 的两种模式都可以。 - Bergi
同意!这取决于上下文。我认为一般规则是避免反模式“使用此来保存东西”,当然,如果用于设计目的,这是可以的,但对于所有其他情况,也许最好使用一个具有适当名称的变量(我的个人意见)。 - dseminara

2

使用这种方法有什么需要注意的地方吗?

没有,没问题。我甚至会认为与 bind 相比(及其 ES3 不支持),这种方法要更少出现陷阱。有时可能会犯一个错误,就是忘记用 var (如果没有启用严格模式,就引入全局变量),但是如果你理解了闭包,那也不是问题。

或者说,我必须首先这样做或者绑定,这是否意味着我做错了。

不是的,在 JavaScript 中这就是调用使用 this 的方法的方式。我并不是说错误的设计不能需要异常频繁地使用 self/bind,而好的设计同样也包含这种模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接