[英]Javascript Function Calls: Regular call vs Call vs Bind Call
我的问题很简单:我将函数传递给其他函数以便稍后调用(示例回调函数),问题是何时,为什么以及最佳实践是什么。
示例:我有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));
}
谢谢,
塞巴斯蒂安P.
我不认为有任何“最好”的做法。
如果您正在呼叫的功能关心this
是什么,您可以使用call
。
如果要确保只能使用指定的this
值调用该函数,请使用bind
。
[两者都有一些开销,即至少一个函数调用/范围的深度]
否则你只需要调用该函数。
简单:)
在this
对象是该函数的上下文。 这就像你为你制造一台机器, this
物体就像机器一样,就像你的房子一样。 您可以随意移动它。
我们有4种方法来设置this
对象。
调用不是方法的函数:
fn(someArguments)
这样, this
对象设置为null或可能是窗口对象。
将函数作为方法调用:
someObject.fn(someArguments)
在这种情况下, this
对象将指向someObject
并且它是可变的。
通过call
或apply
函数的方法call
。
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
属性的String对象。
我见过的最流行的用例是切片参数对象:
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
其他类型的调用相比, bind
具有很大的性能开销,但请确保您不会因为使用预先成熟的优化而牺牲性能和可维护性。
您也可以查看这篇文章。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.