这里,我们使用原型方法给 Function 对象添加了 myCall、myApply 和 myBind 方法。
myCall 和 myApply 非常相似,它们的不同之处在于参数传递方式。
myCall 函数使用剩余参数语法 ...args 来传递参数,而 myApply 函数接受一个数组作为参数。
myBind 函数返回一个新的函数,该函数接受一个参数,并将其与 myBind 中传递的参数合并,然后调用原始函数。
对于 myCall 和 myApply,我们首先将传入的 context 参数与 window 对象进行比较,如果 context 是空的,
则默认为全局 window 对象。然后,我们使用 Symbol 函数创建一个唯一的标识符 fnSymbol,并将原始函数存储为 context[fnSymbol] 的属性。
我们然后调用函数,将结果存储在 result 变量中,并使用 delete 关键字从 context 对象中删除函数属性。最后,我们返回函数的结果。
对于 myBind,我们首先将 this 存储在 fn 变量中,然后返回一个新的函数,
该函数使用剩余参数语法将 myBind 中传递的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数。
// 实现 call 方法
Function.prototype.myCall = function (context, ...args) {// 如果 context 参数为空,则默认为 window 对象context = context || window;// 使用 Symbol 函数创建一个唯一的标识符const fnSymbol = Symbol();// 将原始函数存储为 context 对象的属性context[fnSymbol] = this;// 调用函数并将结果存储在 result 变量中const result = context[fnSymbol](...args);// 删除 context 对象的属性delete context[fnSymbol];// 返回函数的结果return result;
};// 实现 apply 方法
Function.prototype.myApply = function (context, args) {// 如果 context 参数为空,则默认为 window 对象context = context || window;// 使用 Symbol 函数创建一个唯一的标识符const fnSymbol = Symbol();// 将原始函数存储为 context 对象的属性context[fnSymbol] = this;// 调用函数并将结果存储在 result 变量中const result = context[fnSymbol](...args);// 删除 context 对象的属性delete context[fnSymbol];// 返回函数的结果return result;
};// 实现 bind 方法
Function.prototype.myBind = function (context, ...args) {// 将 this 存储在 fn 变量中const fn = this;// 返回一个新的函数,该函数将传入的参数与新函数的参数合并,并在新的上下文中使用 apply 调用原始函数return function (...newArgs) {return fn.apply(context, [...args, ...newArgs]);};
};// 测试数据
const obj = {name: 'obj.name',tryCall: function(...arg) {console.log(`Hello, my name is ${this.name},from:${arg}`);},tryApply:function(...args){console.log(`Hello, my name is ${this.name},from:${args}`);},tryBind:function(...args){console.log(`arg[0]:${args[0]},arg[1]:${args[1]}`);}
};
const person = {name: 'person.name'
};
// obj.tryCall('obj');
// obj.tryCall.call(person,'call');
// obj.tryCall.myCall(person,'mycall'); // obj.tryApply('obj','aaa');
// obj.tryApply.apply(person,['apply','aaa']);
// obj.tryApply.myApply(person,['myapply','bbb']);const oldBind = obj.tryBind.bind(person);
oldBind('London','UK');
const newBind = obj.tryBind.myBind(person);
newBind('London','UK');