call 的初窥
【摘要】 call 和 apply 是 Function 构造函数原型对象上的方法,所有的函数都可以调用 call 和 apply,作用是可以改变调用 call 和 apply 函数内部的 this 指向,并执行函数。call 的使用方法function fn() { console.log(this, arguments);}fn.call();// Window {postMessage: ƒ,...
call 和 apply 是 Function 构造函数原型对象上的方法,所有的函数都可以调用 call 和 apply,作用是可以改变调用 call 和 apply 函数内部的 this 指向,并执行函数。
call 的使用方法
function fn() {
console.log(this, arguments);
}
fn.call();
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
// Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
不指定替换的 this,则调用 call 的函数在运行时决定 this 指向,当前案例中在浏览器中运行,则指向 window 对象。
function fn() {
console.log(this, arguments);
}
fn.call('hello', '1', '2');
// String { "hello" }
// Arguments(2) ["1", "2", callee: ƒ, Symbol(Symbol.iterator): ƒ]
在上面案例中,call 将 fn 内部的 this 更改为 hello 的基本包装类(对象),而 1 和 2 作为 fn 的参数,以 arguments 的形式被打印出来。
function fn1() {
console.log(this, arguments);
}
function fn2() {
console.log(this, arguments);
}
fn1.call.call(fn2, '1', '2');
// String { "1" }
// Arguments ["2", callee: ƒ, Symbol(Symbol.iterator): ƒ]
由于 call 是函数原型的方法,当然也可以被 call 自己调用,在上面的案例中,第一个 call 内部的 this 为调用者 fn1,通过第二个 call 将第一个 call 内部的 this 由 fn1 更改为 fn2,1 和 2 作为参数传递给第一个 call,而 1 又作为第一个 call 内部 this 指向的 fn2 内部的 this,2 作为 fn2 的参数,最后执行 fn2,固有上面执行结果。
call 的实现原理
根据 call 方法的特点,来模拟实现一个自己封装的 call 方法,代码如下。
// context 参数为要替换的 this
Function.prototype.call = function (context) {
// 将传入的 this 转换成对象,若没传则使用 window 作为 this
context = context ? Object(context) : window;
// 将调用 call 的函数作为属性赋值给传入的 this
context.fn = this;
var args = [];
// 将传递给调用 call 函数的参数转化成字符串取值的形式
for (var i = 1; i < arguments.length; i++) {
// args ['arguments[1]', 'arguments[2]']
args.push('arguments[' + i + ']');
}
// 利用 eval 执行 context.fn,并利用数组转换字符串的 toString 去掉 [ ]
var result = eval('context.fn(' + args + ')');
// 删除 context 上多余的 fn 属性
delete context.fn;
return result;
}
上面的实现方式重点解决两个问题:
- 如何让调用
call函数内部的this指向传入的this,我们通过将传入this上加一个属性fn,值为调用call的函数,在执行函数时并不直接调用this,而是执行context.fn,这样内部的this指向了调用者context,即指向了传入要替换的this。
- 如何将
call调用时除第一个参数以外的参数列表作为调用call函数的参数依次传入,我们这里借用了eval提供执行环境,将要执行的代码拼接成字符串,这样就可以容易的将argument第二项后面的所有项通过循环的方式拼接。
给 context 添加的多余属性 fn,要在函数 context.fn 执行后删除。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)