call / apply 漫谈介绍和使用

发布于 2022-07-22 23:10:10 字数 2534 浏览 223 评论 5

在 JavaScript 中,call/apply 是函数原型上的方法,作用是指定函数的 context 也就是所谓的 this 变量。JavaScript 中 this 的指向 不明 饱受诟病然而,实际上 this 的指向十分清晰,this 永远指向函数的调用者,而 call/apply 的作用可以简单地说成了强行更改调用者。

不过,为什么好像其他常用的语言中没有出现 this 的混淆和 call/apply 这种函数呢?实际上,门门语言都有这个问题,因为从根本上说,this 本身就只能通过当做参数来传入,我们的计算机底层只能调用函数/过程,只是,其他语言都是会有隐式绑定,也就是类似于箭头函数的行为的。

下面是几个语言 this 实现举例,其中大都涉及到面向对象的实现机制:

Java 的实现

Java是个纯粹的面向对象语言,基于 class。使用 Java 的时候,要显式使用 this 的场景,只有函数形参或者局部变量和类的成员变量名冲突的时候。假设我们有这样一个类:

class Hello {
    Hello() {
    }
    public void sayHello () {
        say("Hello, world!");
    }
    private void say(String str) {
        System.out.println(str);
    }
    public static void main(String[] args) {
        Hello hello = new Hello();
        hello.sayHello();
    }
}

在这个简单的 java 程序中,由静态方法实例化了 Hello 类,之后调用了实例方法sayHello, 而实例方法sayHello又调用了另一个实例方法say。根据Java虚拟机规范(Java SE第八版本),大致调用过程如下:

  1. 静态方法 main 被传入第一个参数 args,此参数是一个数组的引用;
  2. main 中构造 Hello;
  3. main 中调用了 hello 的 sayHello,在 jvm 中实际进行的操作为,向 sayHello 传入 hello 作为第一个参数,也就是传入this,注意,this是被传入的~;
  4. 在 sayHello 中调用了另一个实例方法 say,jvm 中进行的操作为,向 say 传入 sayHello 接收的实参中的第一个参数作为say的this参数;

我们可以得出一个结论,Java语言中,一般(除了奇葩的构造器方法)只有实例方法拥有this,而且,这个this还是作为参数传递进来的。在实例方法中调用静态方法的时候,并没有向静态方法传入this,因此不能在静态方法中调用实例=。=

Objective-C 的实现

OC 是一个神奇的语言,完全与 C 兼容,实际上最终的编译也是转化成 C 的。OC 里面等价于 this 的东西是 self。OC 可以动态地添加方法:

#import <objc/runtime.h>
// 中間省略
void myMethodIMP(id self, SEL _cmd) {
    doSomething();
}
class_addMethod([MyClass class], @selector(myMethod), (IMP)myMethodIMP, "v@:");

可以清晰地看到,OC的runtime里面,真正干事儿的方法的C代码表示,接受的参数更加明显,第一个就是self,第二个参数甚至是SEL。

从上可以得知,OC的this也是通过传参实现的。

C++ 的实现

我并不懂 cpp。

但是,从查到的资料看,C++ 中,所有的 class 或者 struct 在编译完成之后,成员函数,都会丢失所有信息,其 this 指针作为函数首个参数传入。

从上述几个语言看来,this 实际上在底层都是作为参数传入的,也就是说 js 中的 call/apply 只是把底层暴露出来了而已!令人奇怪的是,另外几个语言都不能自定义 this,只能由编译器隐式传入。

引申一点,虽然 this 对于面向对象来说极其重要,但是最终编译的结果中,保留的信息只有成员变量。根本没有什么成员函数,只是一堆能够额外接受一个指针的普通函数。怪不得有人说面向对象是骗局…

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

温折酒 2022-05-04 08:10:07

我今天看了一下 <你不知道的 JavaScript 上> 里面讲 this 的时候讲到 在调用 apply/call 第一个参数是 undefined, null 的时候会使用 window, 但是这时候会造成歧义, 因为有可能影响到了 window, 他提出一个 DMZ 的东西... 即 Object.create(null)... 来表达 我希望是个空对象 :D

紫罗兰の梦幻 2022-05-04 05:55:39

这个名称取得好, 坐等文章更新

Smile简单爱 2022-05-03 21:50:14

@xiaoyu2er 我的标题有问题,其实应该叫“this漫谈”。最后一个问题确实不是什么问题,不过所有的行为应该都是有语义的,我觉得从语义上不能完全理解非严格模式无调用者时this的指向,所以胡诌了一下。

另外,最近认知有所增长,call/apply实现的东西叫“role oriented programming”。

千笙结 2022-05-03 21:39:11

感觉你说的最后一个问题, 貌似并不是问题, 和平常碰到的 this 的情形下一样的吧? 感觉并不是 call/apply 的问题.

如痴如狂 2022-05-03 19:20:46

至于为什么在非严格模式下,没有调用者,或者call/apply时传入null时,this会指向window。我觉得可能是因为在全局范围内,this本身就指向window,所以就访问到了外面的this?

~没有更多了~

关于作者

0 文章
0 评论
24 人气
更多

推荐作者

已经忘了多久

文章 0 评论 0

15867725375

文章 0 评论 0

LonelySnow

文章 0 评论 0

走过海棠暮

文章 0 评论 0

轻许诺言

文章 0 评论 0

信馬由缰

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文