记录一下手写 call、apply、bind
三者的区别
1)三者都可以显式绑定函数的 this 指向;
2)三者第一个参数都是 this 要指向的对象,若该参数为 undefined 或 null,this 则默认指向全局 window;
3)传参不同:apply 是数组、call 是参数列表,而 bind 可以分为多次传入,实现参数的合并;
4)call、apply 是立即执行,bind 是返回绑定 this 之后的函数,如果这个新的函数作为构造函数被调用,那么 this 不再指向传入给 bind 的第一个参数,而是指向新生成的对象。
首先是 call
Function.prototype.myCall = function (context, ...args) {
//边界判断,传空则将window赋给context
if (context === undefined || context === null) {
context = window;
}
//创建唯一的key值,防止命名重复
let key = Symbol();
//这个this的指向是调用call方法的构造函数,也就是将调用call的函数赋给context的key
context[key] = this;
// context 调用 key 来执行方法, 那么this指向的是context,也就是调用call的函数的this指向context,运行该函数,获得返回值
let res = context[key](...args);
//删除副作用,还原context
// delete context[key];
Reflect.deleteProperty(context, key); // 删除属性
//返回 返回值
return res;
};
let obj = {
name: "小李",
};
function people(height, weight) {
console.log(this); //{name: '小李', Symbol(): ƒ}
console.log(`${this.name}、身高${height}cm、体重${weight}kg`); //小李、身高180cm、体重75kg
}
people.myCall(obj, "180", "75");
其次是 apply,apply 和 call 类似,只是接受的第二个参数为数组。
Function.prototype.myApply = function (context, args) {
//边界判断,传空则将window赋给context
if (context === undefined || context === null) {
context = window;
}
//创建唯一的key值,防止命名重复
let key = Symbol();
//这个this的指向是调用call方法的构造函数,也就是将调用call的函数赋给context的key
context[key] = this;
// context 调用 key 来执行方法, 那么this指向的是context,也就是调用call的函数的this指向context,运行该函数,获得返回值
let res = context[key](...args);
//删除副作用,还原context
delete context[key];
//返回 返回值
return res;
};
let obj = {
name: "小李",
};
function people(height, weight) {
console.log(this); //{name: '小李', Symbol(): ƒ}
console.log(`${this.name}、身高${height}cm、体重${weight}kg`); //小李、身高180cm、体重75kg
}
people.myCall(obj, "180", "75");
最后是 bind,它可以分为多次传入,实现参数的合并,可以理解为柯里化形式传参,返回的是一个待执行的函数。
Function.prototype.myBind = function (context, ...args) {
// bind要考虑返回的函数,作为构造函数被调用的情况
if (context === undefined || context === null) {
context = window;
}
let fn = this;
let key = Symbol();
const result = function (...args1) {
if (this instanceof fn) {
// result如果作为构造函数被调用,this指向的是new出来的对象
// this instanceof fn,判断new出来的对象是否为fn的实例
this[key] = fn;
let res = this[f](...args, ...args1);
delete this[f];
return res;
} else {
// bind返回的函数作为普通函数被调用时
context[key] = fn;
let res = context[key](...args, ...args1);
delete context[key];
return res;
}
};
// 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
// 实现继承的方式: 使用Object.create
result.prototype = Object.create(fn.prototype);
return result;
};
let obj = {
name: "小李",
};
function people(height, weight) {
console.log(this); //{name: '小李', Symbol(): ƒ}
console.log(`${this.name}、身高${height}cm、体重${weight}kg`); //小李、身高180cm、体重75kg
}
let func1 = people.myBind(obj, "180");
let func2 = func1.myBind(obj, "75");
func2();