关于匿名函数的执行上下文和call / apply / bind

时间:2014-01-14 21:11:50

标签: javascript

我正试图深入了解并理解而不是反刍代码。我知道bind, call and apply通过修改this指向的内容来更改执行上下文。我不明白的是,这些方法必不可少和/或导致更短的代码。请考虑以下事项:

var person = {
            firstName: 'john',
            lastName: 'doe',
            fullName: function () {
                console.log(this.firstName + " " + this.lastName);
            }
        }
//#1, binder is an html button
$('#binder').click(person.fullName.bind(person)); //john doe

//#2, anonymous is an html button
$('#anonymous').click(function () {
    person.fullName(); //john doe
});

//seems strange I see this application a lot 'borrowing' a method
//why?
var o = { firstName: 'obj', lastName: 'ect' };
person.fullName.call(o); //obj ect to console

我想知道一些关于何时使用调用和应用(以及在不使用匿名函数和函数#1之外绑定)的良好实践和/或节省时间的一般范例

2 个答案:

答案 0 :(得分:2)

要专注于每个功能必不可少的地方,我会说

  • apply在处理可变参数函数时最有用。它允许您将值数组转换为参数列表。

    function logwrapper(f){
      return function(){
        console.log("called");
        return f.apply(this, arguments);
      }
    }
    
    var bar = function(){ ... }
    var foo = logwrapper(bar);
    
  • bind在您希望将方法传递到只需要一个函数的代码时最有用。一个常见的例子是settimeout和其他期望回调的函数:

    setTimeout(function(){ obj.meth(); }, 100); //handwritten callback
    
    setTimeout(obj.meth.bind(obj), 100);        //Function.prototype.bind
    
    setTimeout(bind(obj, "meth"), 100);         //Some JS libraries have functions that
                                                //let you write the object only once.
    

    请记住,IE< = 8不支持本机Function.prototype.bind。在这种情况下,您可能需要使用polyfill或helper库。

  • 正如您已经注意到的,
  • call对于借用方法非常有用。如果这对你的特定用例有用或者很大,那么一个非常常见的重要用法就是在arguments上使用数组方法。由于历史原因,arguments没有任何常用的数组方法(切片,地图等),因此您需要借用它们:

    function myvariadic(x){
       var rest = [].slice.call(x, 1);
    }
    

    您可能会看到另一个示例是hasOwnProerty方法:

    for(k in obj){
       if(Object.prototype.hasOwnProperty.call(obj, k)){
          ....
       }
    }
    

    这使您可以调用真正的hasOwnProperty方法,即使对象使用自己的hasOwnProperty键对其进行阴影处理。

答案 1 :(得分:1)

坦率地说,我不会对好的或坏的做法进行两分钱的折腾 如果你的目标是了解任何事情,你最好忘掉它们。

这就是说,要了解何时使用call / applybind,您必须了解的是 闭包 ,以及 this 的具体结束。

我在这里使用了广泛的闭包定义。不是您通常会在JavaScript技术聊天中获得的,我们通常会谈论 词汇 闭包

为了这个小帖子的目的,让我们假设一个闭包将为任何变量(this包含)提供一个值,它存在于你关注的函数的当前范围之外。

bindcallapply基本上可以为this提供值(以及其他一些参数作为选项)。

它只对两件事有用:

  • 将给定类的函数或方法调用到不同类的对象(或根本不调用任何类)。

稍后再说

  • 在当前关闭不符合您需求的情况下为this提供值。

例如,传递对方法的引用会丢失与底层对象实例的连接。或者使用类原型函数。或者在事件处理程序的上下文中调用,其中JS已将此设置为捕获事件的DOM元素。

电话&应用

call只会将this设置为该特定函数执行的第一个参数的值。

由于在JS中创建对象非常简单,因此您可能希望执行以下操作:

Worker = function ()
{
    this.things_done = 0;
}

Worker.prototype = {
    do_something: function (count)
    {
        this.things_done += count;
    }
}

var worker= new Worker();                  // Worker object
var wanabee_worker = { things_done: 100 }; // classless object

Etvoilà!您刚刚创建了与Worker没有类关系的东西,但仍然可以使用Worker方法,因为它已经定义了所有必需的属性。

worker.do_something.call (wanabee_worker, 10);

允许wanabee_worker借用不相关的对象Worker的方法。

也可以使用相反的方法:

function reset_work_count ()
{
    this.things_done = 0;
}

reset_work_count.call (worker);

这里我们有一个与Worker没有任何关系的普通函数,除了它使用相同的属性。 call允许将其应用于Worker对象。

apply而言,

this完全相同。唯一的区别是传递其他参数的方式。

结合

bind将创建一个内部闭包并返回一个新的包装函数,该函数将使用传递给bind的参数作为this的值。

典型示例:将事件处理程序绑定到特定对象。

$('#binder').click(person.fullName.bind(person));

在JQuery goo下面,代码最终会做什么

binder.addEventListener ('click', person.fullName.bind(person), false);

如果处理程序被简单地定义为person.fullName,则会调用它,并将this设置为捕获事件的DOM元素。

在这种特殊情况下,JS引擎提供的this的闭包不适合我们的需要,因此我们使用bind提供另一种选择。

而不是person.fullName.bind(person),您可以使用:

function() { person.FullName(); }

除了

  • lambda函数很麻烦并且代表了代码,
  • bind是一个内部构造,可以更有效地完成闭包。

您还可以想象用于处理事件的对象将由某个代理函数动态分配/计算。在这种情况下,使用bind将是无用的,因为我们想要的是访问允许我们将lambda对象用作工作类之一的方法。

function event_handler (param)
{
    var obj = compute_lambda_obj ();
    Worker.prototype.do_something.call (ojb, param);
}