使用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