JavaScript 绑定与匿名函数
JavaScript bind vs anonymous function
This code 来自 mongoose-deep-populate
async.parallel([
User.create.bind(User, {_id: 1, manager: 2, mainPage: 1}),
Comment.create.bind(Comment, {_id: 3, user: 1}),
...
], cb)
使用 Function.prototype.bind
确保当回调函数 User.create
在不同的上下文中执行时 this
绑定到正确的对象。这是否等同于
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
函数被调用之前被评估,因此绑定了正确的 return 值。
当使用匿名函数时,counter.get()
仅在函数执行时计算,并且调用匿名函数的顺序是未知的。
This code 来自 mongoose-deep-populate
async.parallel([
User.create.bind(User, {_id: 1, manager: 2, mainPage: 1}),
Comment.create.bind(Comment, {_id: 3, user: 1}),
...
], cb)
使用 Function.prototype.bind
确保当回调函数 User.create
在不同的上下文中执行时 this
绑定到正确的对象。这是否等同于
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
函数被调用之前被评估,因此绑定了正确的 return 值。
当使用匿名函数时,counter.get()
仅在函数执行时计算,并且调用匿名函数的顺序是未知的。