Call
call,强制将this的指向修改为传入的第一个参数,剩余的参数将作为调用函数的参数传入
原生call使用
const person = {
name: 'aaa'
};
function sayName(a, b, c) {
console.log(this.name);
console.log(a);
console.log(b);
console.log(c);
}
sayName.call(person, 'a', 'b', 'c'); // aaa a b c
注意点:
- call必须要是function才能调用,否则报错
- 调用call后,该函数会立即执行,并且将this指向为第一个参数,剩余参数作为该函数的参数传入
模拟实现:
Function.prototype.myCall = function(context) {
// this: 当前调用的函数; context: 传入的参数
// 如果调用者不是函数,报错
if (typeof this !== 'function') {
throw new Error('call必须要函数调用');
}
// 兜底
context = context || window;
// 定义返回值
let result = null;
// 获取剩余参数,除了第一个剩下都作为调用者的参数传入
let args = [...arguments].slice(1);
// 将函数绑到传入的对象上,这样做能自由使用对象中的所有数据
context.fn = this;
// 执行函数并赋值给返回的结果,因为函数有返回值
result = context.fn(...args);
// 执行完毕后删除掉该属性,还原对象回原本的样式
delete context.fn;
// 返回结果
return result;
}
Apply
使用方式和call一样,唯一区别就是除第一个改变this指向的参数外,apply的剩余作为一个数组整体传入
模拟实现:
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new Error('call必须要函数调用');
}
context = context || window;
let result = null;
let args = [...arguments].slice(1);
context.fn = this;
result = args ? context.fn(...args) : context.fn();
delete context.fn;
return result;
}
Bind
bind与call和apply的区别是bind回返回一个函数,执行该函数会修改this的指向
模拟实现:
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new Error('bind必须要函数才能调用');
}
const _this = this;
// 获取剩余参数
const args = [...arguments].slice(1);
// 设置返回函数
const FnBind = function(...args2) {
// 返回修改this指向的函数,如果this指向本身,this指向不变,否则就指向传入的对象
return _this.apply(
this instanceof FnBind ? this : context,
[...args, ...args2] // 拼接参数
)
}
// 继承原本函数的原型
FnBind.prototype = this.prototype;
// 返回这个新函数
return FnBind;
}