面试官:请你讲讲 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(arguments, 1),
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源创计划”,欢迎正在阅读的你也加入,一块儿分享。