维护对“this”的引用在Javascript中使用回调和闭包时

Maintaining the reference to "this" in Javascript when using callbacks and closures

本文关键字:回调 闭包 Javascript this 引用 维护      更新时间:2023-09-26

我发现自己将"this"赋值给一个变量,这样我就可以很容易地在回调和闭包中使用它。

这种做法不好吗?是否有更好的方法返回到原始函数?

下面是一个典型的例子。

User.prototype.edit = function(req, res) {
  var self = this,
      db = this.app.db;
  db.User.findById('ABCD', function(err, user)) {
    // I cannot use this.foo(user)
    self.foo(user);
  });
};
User.prototype.foo = function(user) {
};

你通常使用这种方法还是你找到了一个更干净的解决方案?

有三种主要的方法来处理回调中的this:

1。创建一个词法作用域变量,就像您当前所做的那样

这个新变量最常用的两个名称是thatself。我个人更喜欢使用that,因为浏览器有一个全局窗口属性称为self和我的过滤器抱怨,如果我阴影它。

function edit(req, res) {
    var that = this,
    db.User.findById('ABCD', function(err, user){
        that.foo(user);
    });
};

这种方法的一个优点是,一旦代码转换为使用that,您可以添加尽可能多的内部回调,并且由于词法作用域,它们都将无缝地工作。另一个优点是它非常简单,即使在旧的浏览器上也能工作。

2。使用.bind()方法

Javascript函数有一个.bind()方法,可以让你创建一个具有固定this的版本。

function edit(req, res) {
    db.User.findById('ABCD', (function(err, user){
        this.foo(user);
    }).bind(this));
};

当涉及到处理this时,bind方法对于必须添加包装函数的回调之一特别有用:

setTimeout(this.someMethod.bind(this), 500);
var that = this;
setTimeout(function(){ that.doSomething() }, 500);

bind的主要缺点是,如果你有嵌套回调,那么你还需要对它们调用bind。另外,IE <= 8和其他一些老浏览器,并没有本地实现bind方法,所以如果你仍然需要支持它们,你可能需要使用某种shimming库。

3。如果您需要对函数范围或参数进行更细粒度的控制,请退回到.call()和.apply()

在Javascript中控制函数参数的更原始的方法,包括this,是.call().apply()方法。它们允许您以任意对象作为其this并以任意值作为其参数来调用函数。apply对于实现可变函数特别有用,因为它接收参数列表作为数组。

例如,下面是bind的一个版本,它以字符串形式接收要绑定的方法。这让我们只写一次this,而不是两次。

function myBind(obj, funcname){
     return function(/**/){
         return obj[funcname].apply(obj, arguments);
     };
}
setTimeout(myBind(this, 'someMethod'), 500);

尽管thatthis "copy"的广泛命名约定,但不幸的是,这是一种行之有效的方法。

你也可以试试:

db.User.findById('ABCD', this.foo.bind(this));

但是这种方法要求foo()具有与findById()所期望的完全相同的签名(在您的示例中,您跳过了err)。

您可以为回调创建一个代理:

var createProxy = function(fn, scope) {
  return function () {
    return fn.apply(scope, arguments);
  }; 
};

使用它,您可以做以下操作:

db.User.findById('ABCD', createProxy(function(err, user)) {
  this.foo(user);
}, this));
jQuery做了类似的事情:$.proxy

并且,正如其他人在使用bind时注意到的那样,如果兼容性存在问题,请查看此处:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind兼容性