繁体   English   中英

Javascript函数调用:常规调用vs调用与绑定调用

[英]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并且它是可变的。

通过callapply函数的方法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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM