第 52 期(W3C 标准-ECMAScript-上下文环境):模拟call方法
wingmeng opened this issue · comments
Wing Meng commented
ECMAScript 中的 call()
方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
下面我们编写一个函数 myCall
来模拟 call
方法,以此加深对 call
的理解。
使用 ES6 实现,非常简单:
// 将 myCall 定义在 Function 原型链上,这样所有的函数都可以共享这个方法
// context 为当前上下文环境
Function.prototype.myCall = function(context, ...args) {
// 当传入 null 或 undefined 时,指向 window
context = context || window;
// 【重点】
// 在当前上下文环境下创建一个临时函数,赋值为 this(当前调用 myCall 的函数)
// 用以将当前调用 myCall 的函数“借用”到当前上下文环境中
context._fn = this;
// 将形参传入临时函数,获得执行结果
let result = context._fn(...args);
// 过河拆桥,删除临时函数
delete context._fn;
return result;
}
但 call
方法是 ES1.3 就有了的,用 ES6 模拟未免有点……所以:
使用 ES3 实现:
Function.prototype.myCall = function(context) {
// 创建一个数组来保存参数,即考虑 bar.call(obj, a, b, c) 这样存在多参数的场景
var args = [];
context = context || window;
context._fn = this;
// 接着要处理传参的问题,例如有多个参数的情况
// arguments 是函数中的隐式参数,保存着传递递给函数的实参,是一个伪数组(像数组却没有数组方法)
// 注意这里的 i 是从 1 开始的,因为 arguments[0] 是 context,排除在外
for (var i = 1; i < arguments.length; i++) {
// 以形参的形式将参数保存到 args 数组中,供下一步使用
args.push('arguments[' + i + ']');
}
// 【重点】
// 现在我们需要把临时函数和参数以字符串的形式拼接起来,然后用 eval 执行
// 为啥要用拼接的方式?因为 ES3 中无法给函数传递不定参数
var result = eval('context._fn(' + args + ')');
delete context._fn;
return result;
}