JavaScript 中相当重要的 Bind

面试官:请你讲讲 js 中 Bind

本文翻译自:javascript

http://javascriptissexy.com/javascript-apply-call-and-bind-methods-are-essential-for-javascript-professionals/#

原本有三部份内容,关于 Bind, Call, Apply。可是咱们先拆解成三部分分开写,今天就先讲讲 Bind 方法。java

JavaScript 中相当重要的 Bind

咱们用 Bind() 来实如今指明函 数内部 this 指向的状况下去调用该函数, 换句话说, bind() 容许咱们很是简单的在函数或者方法被调用时绑定 this 到指定对象上.web

当咱们在一个方法中用到了 this, 而这个方法调用于一个接收器对象, 咱们会须要使用到 bind() 方法; 在这种状况下, 因为 this 不必定彻底如咱们所期待的绑定在目标对象上, 程序有时便会出错;面试

Bind 容许咱们明确指定方法中的 this 指向

当如下按钮被点击的时候, 文本输入框会被随机填入一个名字.数组

// <button>Get Random Person</button>
// <input type="text">

var user = {
    data        :[
        {name:"T. Woods"age:37},
        {name:"P. Mickelson"age:43}
    ],
    clickHandler:function(event{
        var randomNum = ((Math.random () * 2 | 0) + 1) - 1// random number between 0 and 1

        // 从 data 数组中随机选取一个名字填入 input 框内
        $("input").val(this.data[randomNum].name + " " + this.data[randomNum].age);
    }
}

// 给点击事件添加一个事件处理器
$("button").click(user.clickHandler);

当你点击按钮时, 会发现一个报错信息: 由于 clickHandler() 方法中的 this 绑定的是按钮 HTML 内容的上下文, 由于这才是 clickHandler 方法的执行时的调用对象.浏览器

在 JavaScript 中这种问题比较常见, JavaScript 框架中例如 Backbone.js, jQuery 都自动为咱们作好了绑定的工做, 因此在使用时 this 老是能够绑定到咱们所指望的那个对象上.微信

为了解决以前例子中存在的问题, 咱们利用 bind() 方法将 $("button").click(user.clickHandler); 换成如下形式:app

$("button").click(user.clickHandler.bind(user));

再考虑另外一个方法来修复 this 的值: 你能够给 click() 方法传递一个匿名回调函数, jQuery 会将匿名函数的 this 绑定到按钮对象上.框架

bind() 函数在 ECMA-262 第五版才被加入;它可能没法在全部浏览器上运行。你能够部份地在脚本开头加入如下代码,就能使它运做,让不支持的浏览器也能使用 bind() 功能。- MDNdom

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis{
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments1), 
        fToBind = this// 此处的 this 指向目标函数
        fNOP = function({},
        fBound = function({
          return fToBind.apply(this instanceof fNOP
            ? this // 此处 this 为 调用 new obj() 时所生成的 obj 自己
            : oThis || this// 若 oThis 无效则将 fBound 绑定到 this
            // 将经过 bind 传递的参数和调用时传递的参数进行合并, 并做为最终的参数传递
            aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // 将目标函数的原型对象拷贝到新函数中,由于目标函数有可能被看成构造函数使用
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

继续以前的例子, 若是咱们将包含 this 的方法赋值给一个变量, 那么 this 的指向也会绑定到另外一个对象上, 以下所示:

// 全局变量 data
var data = [
    {name:"Samantha"age:12},
    {name:"Alexis"age:14}
]

var user = {
    // 局部变量 data
    data    :[
        {name:"T. Woods"age:37},
        {name:"P. Mickelson"age:43}
    ],
    showData:function(event{
        var randomNum = ((Math.random () * 2 | 0) + 1) - 1// random number between 0 and 1

        console.log(this.data[randomNum].name + " " + this.data[randomNum].age);
    }

}

// 将 user 对象的 showData 方法赋值给一个变量
var showDataVar = user.showData;

showDataVar(); // Samantha 12 (来自全局变量数组而非局部变量数组)

当咱们执行 showDataVar() 函数时, 输出到 console 的数值来自全局 data 数组, 而不是 user 对象. 这是由于 showDataVar() 函数是被当作一个全局函数执行的, 因此在函数内部 this 被绑定位全局对象, 即浏览器中的 window 对象.

来, 咱们用 bind 方法来修复这个 bug.

// Bind the showData method to the user object
var showDataVar = user.showData.bind(user);

Bind 方法容许咱们实现函数借用

在 JavaScript 中, 咱们能够传递函数, 返回函数, 借用他们等等, 而 bind() 方法使函数借用变得极其简单. 如下为一个函数借用的例子:

 // cars 对象
var cars = {
    data:[
        {name:"Honda Accord"age:14},
        {name:"Tesla Model S"age:2}
    ]

}

// 咱们从以前定义的 user 对象借用 showData 方法
// 这里咱们将 user.showData 方法绑定到刚刚新建的 cars 对象上
cars.showData = user.showData.bind(cars);
cars.showData(); // Honda Accord 14

这里存在一个问题, 当咱们在 cars 对象上添加一个新方法(showData)时咱们可能不想只是简单的借用一个函数那样, 由于 cars 自己可能已经有一个方法或者属性叫作 showData 了, 咱们不想意外的将这个方法覆盖了. 正如在以后的 Apply 和 Call 方法 章节咱们会介绍, 借用函数的最佳实践应该是使用 Apply 或者 Call 方法.

Bind 方法容许咱们柯里化一个函数

柯里化的概念很简单, 只传递给函数一部分参数来调用它, 让它返回一个函数去处理剩下的参数. 你能够一次性地调用 curry 函数, 也能够每次只传一个参数分屡次调用, 如下为一个简单的示例.

var add = function(x{
  return function(y{
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

如今, 咱们使用 bind() 方法来实现函数的柯里化. 咱们首先定义一个接收三个参数的 greet() 函数:

function greet(gender, age, name{
    // if a male, use Mr., else use Ms.
    var salutation = gender === "male" ? "Mr. " : "Ms. ";

    if (age > 25) {
        return "Hello, " + salutation + name + ".";
    }
    else {
        return "Hey, " + name + ".";
    }
}

接着咱们使用 bind() 方法柯里化 greet() 方法. bind() 接收的第一个参数指定了 this 的值:

// 在 greet 函数中咱们能够传递 null, 由于函数中并未使用到 this 关键字
var greetAnAdultMale = greet.bind (null"male"45);

greetAnAdultMale("John Hartlove"); // "Hello, Mr. John Hartlove."

var greetAYoungster = greet.bind(null""16);
greetAYoungster("Alex"); // "Hey, Alex."
greetAYoungster("Emma Waterloo"); // "Hey, Emma Waterloo."

当咱们用 bind() 实现柯里化时, greet() 函数参数中除了最后一个参数都被预约义好了, 因此当咱们调用柯里化后的新函数时只须要指定最后一位参数.

因此小结一下, bind() 方法容许咱们明确指定对象方法中的 this 指向, 咱们能够借用, 复制一个方法或者将方法赋值为一个可做为函数执行的变量. 咱们以能够借用 bind 实现函数柯里化.


本文分享自微信公众号 - 人生代码(lijinwen1996329ken)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索