手写实现 apply 方法
理解并手动实现 JavaScript 中的 apply 方法,掌握函数上下文绑定的原理
问题
apply 是 JavaScript 函数原型上的一个方法,用于调用函数并指定函数内部的 this 指向,同时以数组形式传递参数。我们需要手动实现一个 myApply 方法,模拟原生 apply 的行为。
要解决的问题:
- 改变函数执行时的
this指向 - 接收数组形式的参数并传递给函数
- 返回函数执行结果
- 处理边界情况(null/undefined 上下文、非数组参数等)
解答
/**
* 手写实现 apply 方法
* @param {Object} context - 要绑定的上下文对象
* @param {Array} args - 参数数组
* @returns {*} 函数执行结果
*/
Function.prototype.myApply = function(context, args) {
// 1. 处理 context,如果为 null 或 undefined,则指向全局对象
// 浏览器环境为 window,Node.js 环境为 global
context = context || (typeof window !== 'undefined' ? window : global);
// 2. 将 context 转换为对象类型(处理原始值的情况)
context = Object(context);
// 3. 创建唯一的属性名,避免覆盖原有属性
const fnSymbol = Symbol('fn');
// 4. 将当前函数作为 context 的方法
context[fnSymbol] = this;
// 5. 处理参数:确保 args 是数组
args = args || [];
// 6. 执行函数并获取结果
const result = context[fnSymbol](...args);
// 7. 删除临时添加的方法
delete context[fnSymbol];
// 8. 返回执行结果
return result;
};
使用示例
// 示例 1:基本使用
function greet(greeting, punctuation) {
return `${greeting}, 我是 ${this.name}${punctuation}`;
}
const person = { name: '张三' };
console.log(greet.myApply(person, ['你好', '!']));
// 输出: "你好, 我是 张三!"
// 示例 2:数学计算
function sum(a, b, c) {
return a + b + c;
}
console.log(sum.myApply(null, [1, 2, 3]));
// 输出: 6
// 示例 3:获取数组最大值
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.myApply(null, numbers);
console.log(max);
// 输出: 7
// 示例 4:对象方法调用
const calculator = {
base: 10,
add: function(a, b) {
return this.base + a + b;
}
};
const obj = { base: 100 };
console.log(calculator.add.myApply(obj, [5, 15]));
// 输出: 120
// 示例 5:构造函数场景
function Person(name, age) {
this.name = name;
this.age = age;
}
const obj2 = {};
Person.myApply(obj2, ['李四', 25]);
console.log(obj2);
// 输出: { name: '李四', age: 25 }
关键点
-
Symbol 的使用:使用
Symbol创建唯一属性名,避免污染原对象或覆盖已有属性 -
上下文处理:当
context为null或undefined时,需要指向全局对象;同时使用Object()包装原始值类型 -
临时方法模式:通过将函数临时挂载到目标对象上,利用对象方法调用时
this自动绑定的特性来改变this指向 -
参数展开:使用扩展运算符
...args将数组参数展开传递给函数,这是 ES6 的语法,等同于apply的参数传递方式 -
清理操作:执行完毕后必须删除临时添加的方法,保持对象的纯净性
-
返回值处理:确保返回函数的执行结果,保持与原生
apply行为一致 -
与 call 的区别:
apply接收数组参数,而call接收参数列表,这是两者的区别
目录