通过JS手写call/apply的代码比较好理解,这里记录下自己练习手写bind的时候的一些记录。
查看MDN的定义,bind是创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
具体语法是function.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg: 是传入的this指向。
arg1, arg2, ...: 是返回函数被调用的时候,被预置入绑定函数的参数列表中的参数。
手写bind
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
// 判断调用者是否为函数,不是就不执行
return;
}
// 保存调用时候的this
const that = this
// 通过截取传入的参数,来获取除了传入this以外的其他参数
const args = [...arguments].slice(1)
// 返回一个函数
return function F () {
console.log(this instanceof F)
// 这里用instanceof来判断是否是作为构造函数使用
if (this instanceof F) {
return new that(...args, ...arguments)
}
// 如果不作为构造函数来使用,就直接返回一个更改了this指向的函数
return that.apply(context, args.concat(...arguments))
}
}
上面用到了使用instanceof来判断是否是作为构造函数来使用的方法,使用方法是A instanceof B
,如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false。
bind有两种用法:
- 能够使一个函数拥有预设的初始参数,当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
- 当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。
使用实例来鉴定写的bind
function foo(name) {
this.name = name;
}
foo.prototype.sayName = function () {
console.log(this.name)
}
var obj = {};
// bar 通过myBind将this绑定到了空obj上
var bar = foo.myBind(obj);
// 这里调用bar是我们上面返回的F函数,这里就要开始判断 this instanceof F
// 此时的this是windows,那么就返回的false,进入return that.apply(context, args.concat(...arguments)) 代码
// 执行的相当于 foo.apply(obj, ['Jack'])
bar('Jack');
// 上面的相当于对于obj操作,所以给obj添加了name属性
console.log(obj);
console.log(obj.name); // Jack
// 这里是bind第二种用法,返回函数当构造函数使用,那么这样使用的情况就会忽略掉之前设定好的this,这里指向原函数了
var alice = new bar('Alice');
console.log(alice)
console.dir(foo)
console.log(obj.name); // Jack
console.log(alice.name); // Alice
alice.sayName() // Alice