call / apply 漫谈介绍和使用
在 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第八版本),大致调用过程如下:
- 静态方法 main 被传入第一个参数 args,此参数是一个数组的引用;
- main 中构造 Hello;
- main 中调用了 hello 的 sayHello,在 jvm 中实际进行的操作为,向 sayHello 传入 hello 作为第一个参数,也就是传入this,注意,this是被传入的~;
- 在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我今天看了一下 <你不知道的 JavaScript 上> 里面讲 this 的时候讲到 在调用 apply/call 第一个参数是 undefined, null 的时候会使用 window, 但是这时候会造成歧义, 因为有可能影响到了 window, 他提出一个 DMZ 的东西... 即 Object.create(null)... 来表达 我希望是个空对象 :D
这个名称取得好, 坐等文章更新
@xiaoyu2er 我的标题有问题,其实应该叫“this漫谈”。最后一个问题确实不是什么问题,不过所有的行为应该都是有语义的,我觉得从语义上不能完全理解非严格模式无调用者时this的指向,所以胡诌了一下。
另外,最近认知有所增长,call/apply实现的东西叫“role oriented programming”。
感觉你说的最后一个问题, 貌似并不是问题, 和平常碰到的 this 的情形下一样的吧? 感觉并不是 call/apply 的问题.
至于为什么在非严格模式下,没有调用者,或者call/apply时传入null时,this会指向window。我觉得可能是因为在全局范围内,this本身就指向window,所以就访问到了外面的this?