首先明白call、apply、bind的作用与异同
1、相同点
1.三个都是用于改变this指向;
2.接收的第一个参数都是this要指向的对象,若不传入则默认指向window;
3.都可以利用后续参数传参。
2、不同点
1.call和bind传参相同,多个参数依次传入的;
2.apply只有两个参数,第二个参数为数组;
3.call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数
手写call、apply、bind代码
手写call:
// 手写 call 方法
Function.prototype.myCall = function (context) {
// 首先判断myCall的调用者是不是一个函数
// 若myCall的调用者不是一个函数则直接抛出异常
if (typeof this !== 'function') {
throw new TypeError('Error');
}
// 调用myCall时没有传参,那就默认篡改上下文对象为window
context = context || window;
// 保存this
context.fn = this;
// 保存参数 (保留索引为1至最后的元素,因为第一个元素是传入的this)
let args = Array.from(arguments).slice(1)
// 调用方法
let result = context.fn(...args);
delete context.fn;
return result;
}
手写apply(与call类似,主要区别在于传参方式的处理):
// 手写 apply 方法
Function.prototype.myApply = function (context) {
// 首先判断myApply的调用者是不是一个函数
// 若myApply的调用者不是一个函数则直接抛出异常
if (typeof this !== 'function') {
throw new TypeError('Error');
}
// 调用myApply时没有传参,那就默认篡改上下文对象为window
context = context || window;
// 保存this
context.fn = this;
// 定义result用于保存函数调用的结果
// 根据传递参数与否,决定如何调用函数(注意传入第二个参数为一个参数列表)
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
// 调用方法
delete context.fn;
return result;
}
验证myCall和myApply的正确性:
// 验证myCall和myApply的正确性
function sayAge(name = "wll", age = 21) {
this.name = name;
this.age = age;
console.log(this.age);
}
let obj = {
name: "haha",
age: 99
}
sayAge.myCall(obj, "haha", 88) //88
sayAge.myApply(obj, ["haha", 88]) //88
手写bind(判断是否是返回函数的实例这一点不好理解):
// 手写bind
Function.prototype.myBind = function (context) {
// 首先判断myBind的调用者是不是一个函数
// 若myBind的调用者不是一个函数则直接抛出异常
if (typeof this !== 'function') {
throw new TypeError('Error');
}
// 保存this
let _this = this;
// 保存参数
let args = Array.prototype.slice.call(arguments, 1)
// 返回函数(调用bind方法应该返回一个函数)
return function F() {
// 判断this是否属于F的实例
if (this instanceof F) {
// 无需篡改上下文,直接调用
return new _this(...args, ...arguments)
} else {
// 篡改上下文并调用
return _this.apply(context, args.concat(...arguments))
}
}
}
验证myBind的正确性:
//验证myBind的正确性:
function foo(name) {
this.name = name;
}
let obj1 = {};
let bar = foo.myBind(obj1);
bar('Jack');
console.log(obj1.name); // Jack
let alice = new bar('Rose');
console.log(obj1.name); // Jack
console.log(alice.name); // Rose