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)