本文最后更新于235 天前,其中的信息可能已经过时,如有错误请发送邮件到1986413837@qq.com
前置知识
1.Symbol数据类型
Symbol 的值具有唯一性,可避免在对象上挂载函数时覆盖原有属性(解决 ” 污染 context” 问题)
2.Object.defineProperty方法
用于直接在对象上定义新属性或修改现有属性,并精确控制这些属性的行为
Object.defineProperty(obj, prop, descriptor)
3.修改this指向
myCall需要让函数在调用时,this指向我们指定的 ctx对象
实现这一点的唯一方法就是将函数作为 ctx的方法调用(即 ctx.method()的形式)
注意事项
对上下文处理
ctx = (ctx === null || ctx === undefined) ? globalThis : Object(ctx);
校验调用者
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.myCall called on non-function');
}
清理属性
delete ctx[key];
不要忘了返回值
const res = ctx[key](...args)
return res
最终代码
Function.prototype.myCall = function (ctx, ...args) {
// 特殊情况处理上下文
ctx = (ctx === null || ctx === undefined) ? globalThis : Object(ctx);
// 校验调用者
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.myCall called on non-function');
}
// 创建唯一key
// 避免在对象上挂载函数时覆盖原有属性
const key = Symbol('temp');
// 定义临时方法
// ctx[key] = this直接赋值创建的属性默认是可枚举的
// 修改了函数调用时this的指向
Object.defineProperty(ctx, key, {
enumerable: false,
// 设为不可枚举,避免污染对象的属性枚举结果
value: this
});
// 执行并保存结果
const res = ctx[key](...args);
// 清理
delete ctx[key];
return res;
}
function test(a, b) {
console.log(this, a, b);
return a + b;
}
// 基本测试
console.log(test.myCall({ x: 1 }, 2, 3));
// { x: 1 } 2 3
// 5