简体   繁体   English

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

[英]Maintaining the reference to “this” in Javascript when using callbacks and closures

I find myself assigning "this" to a variable so I can easily use it in callbacks and closures. 我发现自己将“this”分配给变量,因此我可以在回调和闭包中轻松使用它。

Is this bad practice? 这是不好的做法吗? Is there a better way of referring back to the original function? 有没有更好的方式回顾原始功能?

Here is a typical example. 这是一个典型的例子。

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) {

};

Do you normally use this approach or have you found a cleaner solution? 您通常使用这种方法还是找到了更清洁的解决方案?

There are three main ways to deal with this in callbacks: 在回调中有三种主要方法可以解决this问题:

1. Create a lexically-scoped variable, as you are currently doing 1.创建一个词法范围的变量,就像您目前正在做的那样

The two most common names for this new variable are that and self . 对于这个新变量的两种最常见的名字是thatself I personally prefer using that because browsers have a global window property called self and my linter complains if I shadow it. 我个人更喜欢使用that因为浏览器有一个名为self的全局窗口属性,如果我影响它,我的linter会抱怨。

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

One advantage of this approach is that once the code is converted to using that you can add as many inner callbacks as you want and they will all seamlessly work due to lexical scoping. 这种方法的一个优点是,一旦将代码转换为使用that您可以根据需要添加尽可能多的内部回调,并且由于词法范围,它们将无缝地工作。 Another advantage is that its very simple and will work even on ancient browsers. 另一个优点是它非常简单,即使在古老的浏览器上也能工作。

2. Use the .bind() method. 2.使用.bind()方法。

Javascript functions have a .bind() method that lets you create a version of them that has a fixed this . Javascript函数有一个.bind()方法,可以让你创建一个修复this

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

When it comes to handling this , the bind method is specially useful for one-of callbacks where having to add a wrapper function would be more verbose: 当涉及到处理this ,bind方法对于一个回调特别有用,其中必须添加包装函数会更详细:

setTimeout(this.someMethod.bind(this), 500);

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

The main disadvantage of bind is that if you have nested callbacks then you also need to call bind on them. bind的主要缺点是,如果你有嵌套的回调,那么你还需要在它们上调用bind Additionally, IE <= 8 and some other old browsers , don't natively implement the bind method so you might need to use some sort of shimming library if you still have to support them. 另外, IE <= 8和其他一些旧浏览器本身并没有实现bind方法,所以如果你仍然需要支持它们,你可能需要使用某种shimming库

3. If you need more fine-grained control of function scope or arguments, fall back to .call() and .apply() 3.如果需要对函数作用域或参数进行更细粒度的控制,请回退到.call()和.apply()

The more primitive ways to control function parameters in Javascript, including the this , are the .call() and .apply() methods. 更原始的方式来控制在Javascript函数的参数,包括this ,是.call().apply()方法。 They let you call a function with whatever object as their this and whatever values as its parameters. 他们让你调用与任何对象作为自己的一个函数this和任何值作为参数。 apply is specially useful for implementing variadic functions, since it receives the argument list as an array. apply对于实现可变参数函数特别有用,因为它接收参数列表作为数组。

For example, here is a version of bind that receives the method to bind as a string. 例如,这是一个bind版本,它接收绑定为字符串的方法。 This lets us write down the this only once instead of twice. 这让我们记下this唯一的一次,而不是两次。

function myBind(obj, funcname){
     return function(/**/){
         return obj[funcname].apply(obj, arguments);
     };
}

setTimeout(myBind(this, 'someMethod'), 500);

Unfortunately this is the well-established way to do this, although that is a widespread naming convention for this "copy". 不幸的是,这是完成此操作的成熟方法,尽管thatthis “副本”的广泛命名约定。

You can also try: 你也可以尝试:

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

But this approach requires foo() to have exactly the same signature as the one expected by findById() (in your example you are skipping err ). 但是这种方法要求foo()具有与findById()期望的完全相同的签名(在您的示例中,您正在跳过err )。

You could create a proxy for the callback with: 您可以使用以下命令为回调创建代理:

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

Using this, you could do the following: 使用此功能,您可以执行以下操作:

db.User.findById('ABCD', createProxy(function(err, user)) {
  this.foo(user);
}, this));

jQuery does something similar with: $.proxy jQuery做了类似的事情: $ .proxy

And, as others have noted using bind , have a look here if compatibility is an issue: 并且,正如其他人已经注意到使用bind ,如果兼容性是一个问题,请看看这里:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM