我的问题很简单: 我正在将一个函数传递给其他函数以便稍后调用(示例回调函数),问题是何时,为什么以及最佳实践是什么。
样品: 我有 xxx()函数,我必须传递它,正如我在window.onload事件中向您展示的那样。
最佳做法是什么?为什么?有任何性能方面或为什么我应该选择使用call或bind来调用此函数
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));
}
谢谢,
Sebastian P。
答案 0 :(得分:4)
我认为没有“最佳”做法。
如果您调用的函数关注的是call
,则使用this
。
如果您希望确保仅使用指定值bind
调用该函数,则使用this
。
[两者都有一些开销,即至少一个函数调用深度/范围]
否则你只需要调用该函数。
简单:)
答案 1 :(得分:3)
this
对象是函数的上下文。这就像你为自己制作一台机器,而this
对象就是机器工作的地方,就像你的房子一样。你可以随意移动它。
我们有4种方式设置this
个对象。
调用非方法的函数:
fn(someArguments)
这样this
对象设置为null或者可能是窗口对象。
将该功能称为方法:
someObject.fn(someArguments)
在这种情况下,this
对象将指向someObject
并且它是可变的。
使用此功能的call
或apply
方法进行通话。
fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])
在这种情况下,this
对象将在此处指向someObject
。在调用它时,你强迫它有另一个上下文。
绑定功能
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
属性的-CoolObject。
我见过的最流行的用例是切片参数对象:
function returnHalf() {
return [].slice.call(arguments, 0, arguments.length / 2);
}
returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']
所以你看,arguments对象不是一个instanceof数组。如果我们arguments.slice(...)
那么你就会被编译器杀死。但是这里我们在arguments对象上使用数组的方法,因为它的数组就像。
有时你不希望你的函数上下文被改变,或者你想要添加自己的参数,你使用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
具有很大的性能开销,但请确保您不会因为过早优化而牺牲性能和可维护性。
另外,您可以查看this文章。