我的问题很简单:我将一个函数传递给其他函数,以便稍后调用(示例回调函数),问题是什么时候、为什么和什么是最佳实践。
示例:我有xxx()函数,我必须传递它,如下面的window.onload事件所示。
什么是最佳实践?为什么?有任何性能方面,或者为什么要选择使用调用或绑定来调用此函数?
function xxx(text)
{
var div = document.createElement("div");
div.innerHTML = text + " - this: " + this.toString();
document.body.appendChild(div)
}
function callFunction(func)
{
func("callFunction");
}
function callUsingCall(func)
{
func.call(this, ["callUsingCall"]);
}
function callUsingBind(func)
{
func.call(this, ["callUsingCall"]);
}
window.onload = function(){
callFunction(xxx);
callUsingCall(xxx);
callUsingBind(xxx.bind(document));
}谢谢,
塞巴斯蒂安P.
发布于 2012-07-17 13:02:00
我不认为有什么“最佳”的做法。
如果调用的函数关心this是什么,就使用this。
如果要确保只能使用指定的bind值调用函数,则可以使用this。
这两者都有一些开销,即至少有一个深度的函数调用/作用域
否则你只需要调用这个函数。
(例句:)
发布于 2012-07-17 13:18:44
this对象是函数的上下文。就像你做了一台机器一样,this对象就是机器工作的地方,就像你的房子。你可以随意移动它。
我们有4种设置this对象的方法。
调用非方法的函数:
fn(someArguments)这样,this对象被设置为null,或者可能设置为window对象。
作为方法调用函数的:
someObject.fn(someArguments)在这种情况下,this对象将指向someObject,并且它是可变的。
使用函数的call 或 apply 方法调用。
fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])在本例中,this对象将指向此处的someObject。当调用它时,您正在强迫它具有另一个上下文。
绑定a函数
var fn2 = fn.bind(anotherObject, someArguments)
这将创建另一个绑定到我们给它的this对象(anotherObject)的函数。无论您如何称呼它,this对象都将是相同的。
用例
现在,你可以做一些棘手的事情知道这一点。我们之所以在这里使用它(我认为它首先来自C++)是因为对象的方法需要访问它们的父对象。this对象提供访问。
var coolObject = {
points : ['People are amazing'],
addPoint : function (p) { this.points.push(p) }
}因此,如果您执行以下操作,它将无法工作:
var addPoint = coolObject.addPoint;
addPoint('This will result in an error');这个错误将被抛出,因为这个对象不再是我们的coolObject,并且没有points属性。所以在这样的时候,你可以这样做:
var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');这是没有意义的,但是这个函数会正常工作,甚至this对象也不是它应该的样子。
var anotherCoolObject = {
points : ['Im a thief!'],
addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');如果您这样调用它,则函数仍然可以工作,因为this对象将指向具有points属性的anotherCoolObject。
我见过的最流行的用例是切片参数对象:
function returnHalf() {
return [].slice.call(arguments, 0, arguments.length / 2);
}
returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']所以你看,参数对象不是数组的实例。如果我们做arguments.slice(...),那么你就会被编译器杀死。但是这里我们在参数对象上使用数组的方法,因为它类似于数组。
有时,您不希望您的函数上下文被更改,或者您希望添加您自己的参数,则使用bind。
例如,当您为带有jquery的事件添加侦听器时,当jquery调用您的函数时,这个对象将是元素。但有时你想做一些棘手的事情并改变它:
var myElement = {
init : function () {
$(this.element).click(this.listener.bind(this));
},
view : "<li>${Name}</li>",
name : 'ed',
element : $('#myelement'),
listener : function () {
this.element.append($.tmpl( this.view, this ));
}
}
myElement.init();因此,在这里,将其绑定到myElement,这样就可以访问对象属性来呈现视图。另一个例子是:
for (var i = 0; i < 10; i++) {
setTimeout(function () {console.log(i)}, 10)
}
// All of them will be 10.
for (var i = 0; i < 10; i++) {
setTimeout((function () {console.log(this.i)}).bind({ i : i }, 10)
}如果在循环中放置了异步函数调用,则在调用回调时,循环完成,计数器到达末尾时,可以使用bind将当前计数器干净地绑定到回调。
我经常使用的另一个很好的用例是将带参数的函数传递给async模块,而不创建闭包。
async.parallel({
writeFile : function (cb) {
fs.writeFile('lolz.txt', someData, cb);
},
writeFile2 : function (cb) {
fs.writeFile('lolz2.txt', someData, cb);
}
}, function (err){
console.log('finished')
});
async.parallel({
writeFile : fs.writeFile.bind(fs, 'lolz.txt', someData),
writeFile2 : fs.writeFile.bind(fs, 'lol2z.txt', someData),
}, function (err){
console.log('finished')
});这两个实现是相同的。
性能
看看这些:
http://jsperf.com/bind-vs-call2
http://jsperf.com/js-bind-vs-closure/2
http://jsperf.com/call-vs-closure-to-pass-scope/10
与其他类型的调用相比,bind具有很大的性能开销,但请确保在预成熟优化时不要牺牲性能和可维护性。
此外,您还可以查看这的文章。
https://stackoverflow.com/questions/11523012
复制相似问题