call和apply都是Function
原型prototype
上的方法。
用于改变this指向。
两者不同点在于传参。
call需要传递散参,而apply需要传递一个参数数组。
这两个方法相比bind
有一个区别,在于这两个方法是直接调用的,而bind
方法是返回一个函数,而这个函数的this指向是明确的。
我们先来看看call
方法有哪些注意点。
function a(...args) {
console.log(this, args);
}
a.call({ a: 1 }, 1, 2, 3);
// { a: 1 } [ 1, 2, 3 ]
当然如果我们绑定的this
如果不是对象的话,那么其实call
方法也会转成对象
function a(...args) {
console.log(this, args);
}
a.call(1, 1, 2, 3); // [Number: 1] [ 1, 2, 3 ]
大概知道他的一些细节后,我们来手写一下call
方法
Function.prototype.myCall = function (_this, ...args) {
// call方法可以用来改变方法调用的this指向
const func = this;
if (typeof func !== "function") {
// 类型判断
throw TypeError("this is not a function");
}
if (typeof _this !== "object") {
// 如果绑定的this不是一个对象
_this = Object(_this);
}
_this.fn = func;
const result = _this.fn(...args);
// 这里在_this上添加了一个属性,需要删除
delete _this.fn;
return result;
};
这里我们测试一下方法的实现是否和call
方法一致
function a(...args) {
console.log(this, args);
}
a.myCall(1, 1, 2, 3);
// [Number: 1] { fn: [Function: a] } [ 1, 2, 3 ]
function a(...args) {
console.log(this, args);
}
a.myCall({ a: 1 }, 1, 2, 3);
// { a: 1, fn: [Function: a] } [ 1, 2, 3 ]
这里其实有一个小瑕疵,就是如果函数中有用到this中的一个属性fn那么就会有问题了,并且打印出来也会有问题
但是基本功能已经很好了
但如果你是一个完美主义的人
那我们不妨继续完善代码
Function.prototype.myCall = function (_this, ...args) {
// call方法可以用来改变方法调用的this指向
const func = this;
if (typeof func !== "function") {
// 类型判断
throw TypeError("this is not a function");
}
if (typeof _this !== "object") {
// 如果绑定的this不是一个对象
_this = Object(_this);
}
const random = Math.random().toString(16).substring(2);
_this["fn" + random] = func;
const result = _this["fn" + random](...args);
// 这里在_this上添加了一个属性,需要删除
delete _this['fn' + random];
return result;
};
生成一个随机字符串来防止属性重复
啥?还不完美吗?
Function.prototype.myCall = function (_this, ...args) {
// call方法可以用来改变方法调用的this指向
const func = this;
if (typeof func !== "function") {
// 类型判断
throw TypeError("this is not a function");
}
if (typeof _this !== "object") {
// 如果绑定的this不是一个对象
_this = Object(_this);
}
const random = Math.random().toString(16).substring(2);
Object.defineProperty(_this, "fn" + random, {
value: func,
enumerable: false,
});
const result = _this["fn" + random](...args);
// 这里在_this上添加了一个属性,需要删除
delete _this.fn;
return result;
};
function a(...args) {
console.log(this, args);
}
a.myCall({ a: 1 }, 1, 2, 3);
// { a: 1 } [ 1, 2, 3 ]
Function.prototype.myApply = function (_this, args) {
const func = this;
if (typeof func !== "function") {
// 类型判断
throw TypeError("this is not a function");
}
if (!Array.isArray(args)) {
throw TypeError("arg is not a array");
}
return func.myCall(_this, ...args);
};
我的github有更多的前端手写代码。