使用callback函数和闭包时,在JavaScript中维护对“this”的引用

我发现自己将“this”分配给一个variables,所以我可以很容易地在callback和封闭中使用它。

这是不好的做法? 有没有更好的方式来回溯到原来的function?

这是一个典型的例子。

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

你通常使用这种方法,或者你find了一个更清洁的解决scheme?

callback中有三种主要的方式来处理this问题:

1.创build一个词法范围的variables,就像你现在正在做的那样

这个新variables的两个最常见的名字是self 。 我个人更喜欢使用that因为浏览器有一个名为self的全局窗口属性,我的linter抱怨,如果我的影子。

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

这种方法的一个优点是,一旦代码被转换为使用that你可以添加尽可能多的内部callback,因为你想要的,他们将无缝工作,由于词法作用域。 另一个优点是它非常简单,即使在古老的浏览器上也能工作。

2.使用.bind()方法。

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

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

说到处理this ,bind方法对于需要添加包装函数的callback函数来说更加冗长:

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

bind的主要缺点是,如果你有嵌套的callback,那么你也需要调用bind 。 此外, IE <= 8和其他一些旧浏览器 ,本身并不实现bind方法,所以如果你仍然需要支持的话,你可能需要使用某种types的匀场库 。

3.如果您需要对函数作用域或参数进行更精细的控制,请回退到.call()和.apply()

.apply().apply()方法在Javascript中控制函数参数的原始方法,包括this 。 他们让你调用任何对象作为它的this和任何值作为其参数的函数。 apply对于实现可变参数函数特别有用,因为它将参数列表作为数组接收。

例如,下面是一个绑定的版本,它接收绑定为string的方法。 这让我们只写一次,而不是两次。

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

不幸的是,这是做到这一点的公认方法,虽然that是一个广泛的命名约定this “副本”。

你也可以尝试:

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

但是这种方法需要foo()findById()期望的完全相同的签名(在你的例子中,你正在跳过err )。

您可以使用以下命令为callback创build一个代理:

 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#Compatibility