前端面试经常会遇到的一个面试题:手写bind
方法
这篇文章就ECMAScript中bind Polyfill来详解bind方法,详见MDN
首先,bind()
call()
apply()
与javascript中this
息息相关,他们有个共同的作用,就是对this对象进行显式绑定,至于他们的区别不在这里做深入讨论
bind()
是干什么用的
bind()
方法会==创建一个新函数==,在bind()
被调用时,新函数的this
被指定为bind()
的第一个参数,而其余参数将作为新函数得参数,供调用时使用。
const obj = {
name: 'lily',
getName: function() {
return this.name
}
}
let fn = obj.getName
console.log(fn()) // undefined 因为此时fn中this指向 [object Window]
let fn2 = fn.bind(obj, 1, 2, 3)
console.log(fn2()) // 'lily' 通过bind()将this显式绑定在obj上
bind()
语法
fn.bind(obj, arg1, arg2 )
除了更改this指向外,bind()还有其他用途
预设参数
这里有另外一个点,bind
第一个参数为null
或者undefined
时,调用的函数this
在非严格模式下指向window
,严格模式下将会报错 TypeError: this is undefined
function preset() {
return Array.prototype.slice.call(arguments)
}
let list1 = list(1, 2, 3) // [1, 2, 3]
let presetList = list.bind(null, 666)
let list2 = presetList() // [666]
let list3 = presetList(777, 888, 999) // [666, 777, 888, 999]
bind()
Polifill
这种写法不可在构造函数new funcA.bind(thisArg, args)
使用,因为new
操作符也可改变this
指向,并且优先级最高
if (!Function.prototype.bind) (function() { // 是否有bind
let slice = Array.prototype.slice // slice 返回新数组
Function.prototype.bind = function() {
let thatFunc = this, // 调用bind的方法
thatArg = arguments[0], // 目标对象
args = slice.call(arguments, 1) // 预设参数
if (typeof thatFunc !== 'function') { // 调用bind()的是否是函数,因为bind只能被函数调用
throw new TypeError('only Function can be bound')
}
return function(){
let funcArgs = args.concat(slice.call(arguments)) // 拼接参数,因为bind返回的是一个函数,所以调用的时候有可能会传参数进来
return thatFunc.apply(thatArg, funcArgs);
}
}
})()
下面写法可用于new funcA.bind(thisArg, args)
if (!Function.prototype.bind) (function(){ // 是否有bind
var ArrayPrototypeSlice = Array.prototype.slice; // slice 返回新数组
Function.prototype.bind = function(otherThis) {
if (typeof this !== 'function') { // 调用bind()的是否是函数,因为bind只能被函数调用
throw new TypeError('only Function can be bound');
}
let baseArgs= ArrayPrototypeSlice .call(arguments, 1), // 目标对象
baseArgsLength = baseArgs.length, // 预设参数长度
fToBind = this, // 调用bind的方法
fNOP = function() {}, // 字面量创建一个函数
fBound = function() { //
baseArgs.length = baseArgsLength; // reset to default base arguments
baseArgs.push.apply(baseArgs, arguments); // 拼接参数
return fToBind.apply(
// 是否被new 操作符调用,是的话就用新创建的this替换bind的this
fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
);
};
if (this.prototype) {
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP(); // 防止new了函数后改变原型而导致原函数的原型也被修改
return fBound;
};
})();