JavaScript绑定vs匿名函数
这个代码来自mongoose深度填充
async.parallel([ User.create.bind(User, {_id: 1, manager: 2, mainPage: 1}), Comment.create.bind(Comment, {_id: 3, user: 1}), ... ], cb)
使用Function.prototype.bind
来确保当在不同的上下文中执行callback函数User.create
时, User.create
绑定到适当的对象。 这相当于
async.parallel([ function() { User.create({_id: 1, manager: 2, mainPage: 1}) }, function() { Comment.create({_id: 3, user: 1}) }, ], cb)
?
如果是这样,在什么情况下bind
一个更好的语法比使用匿名函数?
这两者在你的例子中是不一样的,因为你使用的是不变的值,但是考虑如下:
function mul(num, times) { return num * times; } function fn1() { let num = 3; let cb = function(times) { return mul(num, times); } num = 5; console.log(`num is now: ${ num }`); return cb; } function fn2() { let num = 3; let cb = mul.bind(null, num); num = 5; console.log(`num is now: ${ num }`); return cb; }
当你跑这两个你会得到不同的结果:
let a1 = fn1()(5); // a1 === 25 let a2 = fn2()(5); // s2 === 15
两者的区别在于,在使用bind
,将当前值绑定到函数(作为参数),而在使用匿名函数时,使用的值将是调用该函数时存在的值。
在某些情况下,执行该函数时甚至可能面临undefined
的情况:
var a = ["zero", "one", "two", "three", "four", "five"]; function fn(value, index) { console.log(value, index); } // doesn't work as i is undefined when the function is invoked for (var i = 0; i < a.length; i++) { setTimeout(() => { fn(a[i], i); }, 45); } // works because the value of i and the value of a[i] are bound for (var i = 0; i < a.length; i++) { setTimeout(fn.bind(null, a[i], i), 45); }
(如果您使用let
而不是var
那么使用匿名函数的示例将会起作用)
当你想传递一个调用另一个函数的结果时,也会发生同样的情况:
let counter = { _current: 0, get: function() { return this._current++; } } let map = { _items: Object.create(null), set: function(key, value, index) { this._items[key] = { index: index, value: value } } } // using anonymous functions the index in most cases won't reflect the real order setTimeout(function() { map.set("one", 1, counter.get()); }, Math.floor(Math.random() * 1500) + 100); setTimeout(function() { map.set("two", 2, counter.get()); }, Math.floor(Math.random() * 1500) + 100); setTimeout(function() { map.set("three", 3, counter.get()); }, Math.floor(Math.random() * 1500) + 100); // using bind, the index will always be correct setTimeout(map.set.bind(map, "one", 1, counter.get()), Math.floor(Math.random() * 1500) + 100); setTimeout(map.set.bind(map, "two", 2, counter.get()), Math.floor(Math.random() * 1500) + 100); setTimeout(map.set.bind(map, "three", 3, counter.get()), Math.floor(Math.random() * 1500) + 100);
它的工作方式不同的原因是,绑定counter.get()
之前, bind
函数被调用,所以正确的返回值被绑定。
当使用匿名函数时, counter.get()
仅在函数执行时被求值,并且匿名函数被调用的顺序是未知的。