-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JavaScript深入之call和apply的模拟实现 #11
Comments
受益匪浅!学到了很多,谢谢前辈! |
哈哈,确实可以,没有注意到这点,感谢指出,(๑•̀ㅂ•́)و✧ |
arr.push('arguments['+i+']'); |
eval函数接收参数是个字符串定义和用法
语法:
简单来说吧,就是用JavaScript的解析引擎来解析这一堆字符串里面的内容,这么说吧,你可以这么理解,你把
|
@jawil 感谢回答哈~ var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
} 最终的数组为: var args = [arguments[1], arguments[2], ...] 然后 var result = eval('context.fn(' + args +')'); 在eval中,args 自动调用 args.toString()方法,eval的效果如 jawil所说,最终的效果相当于: var result = context.fn(arguments[1], arguments[2], ...); 这样就做到了把传给call的参数传递给了context.fn函数 |
apply郑航的实现,循环是不是应该从 i = 1 开始? |
@lz-lee call 的实现中,是通过 arguments 取各个参数,所以从 1 开始,省略掉为 0 的 context,而apply的实现中,arr 直接就表示参数的数组,循环这个参数数组,直接就从 0 开始。 |
粗心大意了。感谢提醒。@mqyqingfeng |
分析的很透彻,点个赞! |
赞赞赞 |
@lynn1824 @qianlongo 感谢夸奖,写的时候我就觉得模拟实现一遍 call 和 apply 最能让大家明白 call 和 apply 的原理 (~ ̄▽ ̄)~ |
var context = Object(context) || window; 这里有问题吗?context为null时Object(null)返回空对象,不会被赋值为window |
@lzr900515 没有什么问题哈,非严格模式下,指定为 null 或 undefined 时会自动指向全局对象,郑航写的是严格模式下的,我写的是非严格模式下的,实际上现在的模拟代码有一点没有覆盖,就是当值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。 |
不过这个方法似乎有点傻 |
@hujiulong 哈哈,有道理哈~ 确实会覆盖之前对象的方法,还好模拟实现 call 和 apply 的目的在于让大家通过模拟实现了解 call 和 apply 的原理,实际开发的时候还是要直接使用 call 和 apply 的~ |
实在是佩服! |
这一句可能造成误导。结果为:["arguments[1]", "arguments[2]"] |
编写的call2函数是作为目标函数(需要被修改this的函数)的方法调用的,我们知道当函数作为方法调用时,该函数内部this指向就是其调用者,在这里call2的调用者就是 那个需要被修改this的函数,个人理解🐻❄️ |
您好,您的邮件我已收到,会尽快回复!樊
|
您好,您的邮件我已收到,如果未能及时回复敬请谅解!
|
您好,您发给我的邮件已收到。
|
// 采用博主思路,完整的的版本,主要是不是用eval函数
|
您好,您发给我的邮件已收到。
|
我发现call 如果传入的是基本数据类型,会自动提升为包装对象,是否也需要处理一下 |
您好,您的邮件我已收到,会尽快回复!樊
|
您好,您发给我的邮件已收到。
|
您好,您发给我的邮件已收到。
|
您好,您的邮件我已收到,如果未能及时回复敬请谅解!
|
您好,您的邮件我已收到,会尽快回复!樊
|
您好,您发给我的邮件已收到。
|
您好,您的邮件我已收到,如果未能及时回复敬请谅解!
|
我也觉得有这个问题,然后问了gpt,发现这里可以用Symbol,很巧妙 // 将函数本身作为属性添加到 context 上
const fn = Symbol('fn');
context[fn] = this;
// 执行函数并保存结果
const result = context[fn](...args); |
您好,您发给我的邮件已收到。
|
您好,您的邮件我已收到,会尽快回复!樊
|
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
这里是错误的,Obejct始终返回Object类型,所以千万不要这样写,而是 |
您好,您发给我的邮件已收到。
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
您好,您的邮件我已收到,会尽快回复!樊
|
symbol都是es6的东西了 |
您好,您发给我的邮件已收到。
|
您好,您的邮件我已收到,会尽快回复!樊
|
这是来自QQ邮箱的假期自动回复邮件。你好,我最近正在休假中,无法亲自回复你的邮件。我将在假期结束后,尽快给你回复。
|
call
一句话介绍 call:
举个例子:
注意两点:
模拟实现第一步
那么我们该怎么模拟实现这两个效果呢?
试想当调用 call 的时候,把 foo 对象改造成如下:
这个时候 this 就指向了 foo,是不是很简单呢?
但是这样却给 foo 对象本身添加了一个属性,这可不行呐!
不过也不用担心,我们用 delete 再删除它不就好了~
所以我们模拟的步骤可以分为:
以上个例子为例,就是:
fn 是对象的属性名,反正最后也要删除它,所以起成什么都无所谓。
根据这个思路,我们可以尝试着去写第一版的 call2 函数:
正好可以打印 1 哎!是不是很开心!(~ ̄▽ ̄)~
模拟实现第二步
最一开始也讲了,call 函数还能给定参数执行函数。举个例子:
注意:传入的参数并不确定,这可咋办?
不急,我们可以从 Arguments 对象中取值,取出第二个到最后一个参数,然后放到一个数组里。
比如这样:
不定长的参数问题解决了,我们接着要把这个参数数组放到要执行的函数的参数里面去。
也许有人想到用 ES6 的方法,不过 call 是 ES3 的方法,我们为了模拟实现一个 ES3 的方法,要用到ES6的方法,好像……,嗯,也可以啦。但是我们这次用 eval 方法拼成一个函数,类似于这样:
这里 args 会自动调用 Array.toString() 这个方法。
所以我们的第二版克服了两个大问题,代码如下:
(๑•̀ㅂ•́)و✧
模拟实现第三步
模拟代码已经完成 80%,还有两个小点要注意:
1.this 参数可以传 null,当为 null 的时候,视为指向 window
举个例子:
虽然这个例子本身不使用 call,结果依然一样。
2.函数是可以有返回值的!
举个例子:
不过都很好解决,让我们直接看第三版也就是最后一版的代码:
到此,我们完成了 call 的模拟实现,给自己一个赞 b( ̄▽ ̄)d
apply的模拟实现
apply 的实现跟 call 类似,在这里直接给代码,代码来自于知乎 @郑航的实现:
下一篇文章
JavaScript深入之bind的模拟实现
重要参考
知乎问题 不能使用call、apply、bind,如何用 js 实现 call 或者 apply 的功能?
深入系列
JavaScript深入系列目录地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
The text was updated successfully, but these errors were encountered: