不看不知道,一看吓一跳,已整整一个月没有更新 underscore 源码解读系列文章了。前面我们已完成了 Object ,Array,Collection 上的扩大要领的源码理会,本文最先来解读 Function 上的扩大要领。
完全的 underscore 源码解读系列文章请移步 https://github.com/hanzichi/u…,以为还阔以的话,给个 star 勉励下楼主呗 ^_^
bind 简介
Ok,本日要讲的恰是 bind。关于 bind,能够先移步楼主之前写的文章 ECMAScript 5(ES5) 中 bind 要领简介备忘,有个基础的观点。
bind() 要领会建立一个新函数,当这个新函数被挪用时,它的 this 值是传递给 bind() 的第一个参数, 它的参数是 bind() 的其他参数和其底本的参数。
语法是如许模样的:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg 当绑定函数被挪用时,该参数会作为原函数运行时的 this 指向。当运用 new 操作符挪用绑定函数时,该参数无效。
arg1, arg2, … (可选)当绑定函数被挪用时,这些参数加上绑定函数自身的参数会根据递次作为原函数运行时的参数。
参数
bind 的第一个参数会作为原函数运行时的 this 指向,不多说;而第二个最先的参数是可选的,当绑定函数被挪用时,这些参数加上绑定函数自身的参数会根据递次作为原函数运行时的参数。怎样明白?
function fn(a, b, c) {
return a + b + c;
}
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30); // 60
fn 函数须要三个参数,_fn 函数将 10 作为默许的第一个参数,所以只须要传入两个参数即可,假如你不小心传入了三个参数,宁神,也只会取前两个。
function fn(a, b, c) {
return a + b + c;
}
var _fn = fn.bind(null, 10);
var ans = _fn(20, 30, 40); // 60
这有啥用呢?假如某些函数,前几个参数已 “内定” 了,我们便能够用 bind 返回一个新的函数。也就是说,bind() 能使一个函数具有预设的初始参数。这些参数(假如有的话)作为 bind() 的第二个参数跟在 this 背面,以后它们会被插进去到目的函数的参数列表的最先位置,传递给绑定函数的参数会跟在它们的背面。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
new
运用 bind 返回的效果照样个 function,是个 function 就能够被 new 运算符挪用,那末效果呢?范例中说的很清晰了,当运用 new 操作符挪用绑定函数时,bind 的第一个参数无效。
function Person(name, age) {
this.name = name;
this.age = age;
}
var _Person = Person.bind({});
var p = new _Person('hanzichi', 30); // Person {name: "hanzichi", age: 30}
平常我们不会去这么用,然则假如要写个 bind 的 polyfill(http://caniuse.com/#search=bind),照样须要斟酌用 new 挪用的状况。
我们也能够设置默许值(参考上一小节),本来供应的那些参数依然会被前置到组织函数挪用的前面。
function Person(name, age) {
this.name = name;
this.age = age;
}
var _Person = Person.bind(null, 'hanzichi');
var p = new _Person(30); // Person {name: "hanzichi", age: 30}
合营 setTimeout
什么时候轻易丧失 this 指向?恩,setTimeout 是一个场景,很轻易把 this 指向 window,固然,setInterval 也是一样。当运用对象的要领时,须要 this 援用对象,你能够须要显式地把 this 绑定到回调函数以便继承运用对象。
var canvas = {
render: function() {
this.update();
this.draw();
},
update: function() {
// ...
},
draw: function() {
// ...
}
};
window.setInterval(canvas.render, 1000 / 60);
用 canvas 写殊效或许做游戏时经常会遇到相似的问题。上面的代码是有问题的,render 要领中的 this 实在被指向了 window!我们能够用 bind,显式地把 this 绑定到回调函数以便继承运用该对象。
window.setInterval(canvas.render.bind(canvas), 1000);
相似的状况另有 dom 的事宜监听,一不小心能够 this 就被指向了 dom 元素。能够参考下之前做 bigrender 时写的这部份代码 https://github.com/hanzichi/h…。
tip
bind 还能做一些有意思的事变。
一般来讲,将一个类数组转为数组,我们会用 slice(ie9- 不支持)。参考 https://github.com/hanzichi/u…
var slice = Array.prototype.slice;
// slice.apply(arguments);
// slice(arguments, 1);
bind 能让挪用变的越发简朴。
// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);
// ...
slice(arguments);
// slice(arguments, 1);
再举个相似的例子,比如说我们要增加事宜到多个节点,for 轮回固然没有任何问题,我们还能够 “抄袭” forEach
要领:
Array.prototype.forEach.call(document.querySelectorAll('input[type="button"]'), function(el){
el.addEventListener('click', fn);
});
更进一步,我们能够用 bind 将函数封装的更好:
var unboundForEach = Array.prototype.forEach
, forEach = Function.prototype.call.bind(unboundForEach);
forEach(document.querySelectorAll('input[type="button"]'), function (el) {
el.addEventListener('click', fn);
});
一样相似的,我们能够将 x.y(z) 变成 y(x,z) 的情势:
var obj = {
num: 10,
getCount: function() {
return this.num;
}
};
var unboundBind = Function.prototype.bind
, bind = Function.prototype.call.bind(unboundBind);
var getCount = bind(obj.getCount, obj);
console.log(getCount()); // 10
再举个栗子。每隔一秒在控制台打印 1-5,看起来是道考核闭包的典范问题。
for(var i = 1; i <= 5; i++) {
!function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}(i);
}
ES6 下能用 let
:
for(let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
也能够用 bind,霎时逼格提拔:
for(var i = 1; i <= 5; i++) {
setTimeout(console.log.bind(console, i), i * 1000);
}
Read more
关于 bind 的引见就到这里,下一篇文章将连系 underscore 来讲讲怎样完成一个 bind 的 polyfill。