出处:掘金
原作者:金泽宸
call
:立即执行 + 传参列表apply
:立即执行 + 传参数组bind
:返回一个新函数,不立即执行;绑定 this 和前几个参数
为什么要手写这几个函数?
- 它们是 JavaScript 中 Function 原型链上非常核心的函数
- 实际开发中经常用于改变函数的执行上下文(
this
) - 面试高频必考,考你理解
this
、本质执行流程、原型继承
核心思路
fn.call(obj, arg1, arg2)
// 等价于:
obj.fn = fn
obj.fn(arg1, arg2)
delete obj.fn
手写 call
Function.prototype.myCall = function (context, ...args) {
// 1. 处理 context 的边界情况(null/undefined/原始值)
if (context === null || context === undefined) {
context = globalThis; // 非严格模式下,默认指向全局对象
} else if (typeof context !== 'object') {
context = new Object(context); // 原始值转换为包装对象(如数字 1 → Number 对象)
}
// 2. 用 Symbol 创建唯一属性,避免覆盖 context 原有属性
const uniqueThis = Symbol('unique');
context[uniqueThis] = this; // this 是当前函数
// 3. 调用函数,此时函数内部的 this 指向 context
const result = context[uniqueThis](...args);
// 4. 删除临时属性,避免污染 context
delete context[uniqueThis];
// 5. 返回函数执行结果
return result;
};
手写 apply
Function.prototype.myApply = function (context, args) {
// 1. 处理 context 的边界情况(null/undefined/原始值)
if (context === null || context === undefined) {
context = globalThis; // 非严格模式下,默认指向全局对象
} else if (typeof context !== 'object') {
context = new Object(context); // 原始值转换为包装对象(如数字 1 → Number 对象)
}
// 2. 用 Symbol 创建唯一属性,避免覆盖 context 原有属性
const uniqueThis = Symbol('unique');
context[uniqueThis] = this; // this 是当前函数
// 3. 调用函数,此时函数内部的 this 指向 context
const result = Array.isArray(args)
? context[uniqueThis](...args)
: context[uniqueThis]();
// 4. 删除临时属性,避免污染 context
delete context[uniqueThis];
// 5. 返回函数执行结果
return result;
};
手写 bind
基础版
Function.prototype.myBind = function (context, ...outArgs) {
// 1. 判断 context 类型
if (context === undefined || context === null) {
context = globalThis;
} else {
context = Object(context);
}
// 2.唯一属性保存
const uniquePrototype = Symbol("unique");
context[uniquePrototype] = this;
// 3.返回调用的函数
return function (...innerArgs) {
return context[uniquePrototype](...outArgs, ...innerArgs);
};
};
缺陷:
- 没删除
Symbol("unique")
,不能删除,删除后返回的函数无法重复执行 - 不支持构造函数绑定:与原生的
bind
不同,这个自定义的实现不支持通过new
调用绑定函数以创建对象。如果尝试使用new
来实例化绑定函数,会导致错误或意外的行为 - 不支持原型继承: 当使用原生的
bind
函数绑定函数时,绑定函数和原函数之间共享原型。然而,这个自定义的实现并没有处理这种情况,导致原型继承相关的特性无法正确传递 - 不支持
length
属性: 在JavaScript
中,函数对象具有一个length
属性,它返回函数定义的参数数量。使用原生的bind
函数绑定后的函数会保留正确的length
属性,但这个自定义实现并没有处理length
属性,它会返回原始函数的参数数量,而不是绑定函数的参数数量 - 不支持取消绑定: 原生的
bind
函数返回的绑定函数是可以取消绑定的(通过使用Function.prototype.unbind
或 ES6 的Function.prototype.@@unbound
),但是这个自定义的实现并没有提供取消绑定的方法
进阶版
Function.prototype.myBind = function (context, ...outArgs) {
const self = this;
function boundFunction(...innerArgs) {
// 如果是用 new 调用
if (this instanceof boundFunction) {
return new self(...outArgs, ...innerArgs);
}
return self.apply(context, [...args, ...restArgs]);
}
// 原型继承
boundFunction.prototype = Object.create(this.prototype);
// 添加 unbind 方法,返回原始函数
boundFunction.unbind = () => self;
return boundFunction;
};
常见易错点
错误理解 | 正确做法 |
---|---|
bind 会立即执行 | ❌,它返回一个函数,不会立即执行 |
没有处理 new 场景 | ✅ 用 this instanceof 区分 |
覆盖对象属性名 | ✅ 用 Symbol 解决 |