一、call、apply、bind 究竟搞啥的?
- call:改变函数 this 指向,并立即执行。参数按顺序传递。
- apply:同样改变 this 指向,但参数必须是数组。
- bind:改变 this 指向,但返回新函数,并能预绑定部分参数。需要手动调用才会执行。
二、手写 call:面试官最爱考的基本功
实现原理
- 把要调用的函数临时挂到 context(目标对象)上,避免 this 被污染。
- 执行这个临时函数。
- 删除这个临时属性,避免污染 context 对象。
- 返回执行结果。
Function.prototype.myCall = function (context, ...args) {
context = context || globalThis; // 兼容非对象 / 全局
const fnSymbol = Symbol('fn'); // 避免重名
context[fnSymbol] = this; // 绑定this到context
const result = context[fnSymbol](...args); // 调用函数
delete context[fnSymbol]; // 删除临时属性
return result; // 返回结果
};
// 用法展示
function greet(greeting, punctuation) {
console.log(greeting, this.name, punctuation);
}
greet.myCall({ name: 'Alice' }, 'Hello', '!'); // Hello Alice !
面试加分点:用
Symbol防止属性冲突,体现细心!
三、手写 apply:思路如 call,参数变换而已
区别就是参数不是一串,而是一个数组!
Function.prototype.myApply = function(context, args) {
context = context || globalThis;
const fnSymbol = Symbol('fn');
context[fnSymbol] = this;
let result;
if (Array.isArray(args)) {
result = context[fnSymbol](...args);
} else {
result = context[fnSymbol]();
}
delete context[fnSymbol];
return result;
};
// 用法展示
function show(name, age) {
console.log('My name is', name, 'and I am', age);
}
show.myApply(null, ['Jack', 22]); // My name is Jack and I am 22
四、手写 bind:面试场的王炸题,考你闭包思维+继承意识!
实现难度升级:“返回一个新函数,并可以预先绑定参数”。还要兼容构造函数(使用 new 时)!
普通版(不考虑 new)
Function.prototype.myBind = function(context, ...bindArgs) {
const self = this;
return function(...callArgs) {
return self.apply(context, [...bindArgs, ...callArgs]);
}
};
// 用法
function sum(a, b, c) {
console.log(this.x, a, b, c);
}
const boundSum = sum.myBind({x: 100}, 7, 8);
boundSum(9); // 100 7 8 9
升级版(完美兼容 new)
- 如果用 new,this 还得指向新对象,而不是 context。
- 保证原型链正确。
Function.prototype.myBind = function(context, ...bindArgs) {
const self = this;
function bound(...callArgs) {
// 用于 new 关键字时,this 指向新对象
return self.apply(this instanceof bound ? this : context, [...bindArgs, ...callArgs]);
}
bound.prototype = Object.create(self.prototype); // 继承原型
return bound;
}
// 用法
function Person(name, age) {
this.name = name;
this.age = age;
}
const BoundPerson = Person.myBind(null, 'Tom');
const p = new BoundPerson(20);
console.log(p.name, p.age); // Tom 20
关键考点:
this instanceof bound判断是否用 new 调用- 原型链继承必须用
Object.create
如果你觉得这篇文章有用,记得点赞、收藏、分享,关注我查看更多前端干货!