今天看到微博上大家在讨论一个JavaScript的小问题,问题虽小,还是有思考的价值。我看到不少人对其展开了讨论,有很多答案,也有很多有意思的观点。学JavaScript的人很多,但是学好真不容易,哪怕就是基础的部分。
先看一下下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var value = 500;
var obj = {
value: 0,
increment: function () {
this .value++;
var innerFunction = function () {
alert( this .value);
}
innerFunction();
}
}
obj.increment();
|
好,在往下阅读以前,想一想,你觉得会打印的结果是多少?
————————————————-思考———————————————————
不少人都觉得是1,因为obj对象的value初始值是0,打印的“this.value”在“this.value++”以后就应该变成1了。
其实答案是500,语句”innerFunction()”,正是使用了Function Invocation Patterns这种方式去调用一个方法,这个方法调用没有显式指定“this”,那么如果你按照一些其它编程语言的思路去思考的话,没有显式指定this,this就应该是当前对象吧……那你就错了。这正是JavaScript设计中一个很糟糕的部分,或者说,是一个坑——在没有显式指定this的时候,这个this其实是一个global对象,在这里就是window。
还有很多人解释说这是“闭包”的一个应用,有人又说不是,我来分析一下:闭包简单说就是“包含自由变量的代码块”——也就是说,一个条件是代码块,这点毫无疑问满足;另一个条件是自由变量,这就有争议了,代码中的那个全局变量value算不算所谓的“自由变量”,如果算,那就符合闭包的要求,如果不算(很多人认为global变量不能算作闭包的自由变量),那就不算闭包。
解决方法呢,其中的一个比较简单的办法就是持有一个当前正确this的引用,在这里保存到一个名为“that”的变量里,再让目标方法来调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var value = 500;
var obj = {
value: 0,
increment: function () {
var that = this ;
that.value++;
var innerFunction = function () {
alert(that.value);
}
innerFunction();
}
}
obj.increment();
|
这样结果就是1了。
还没完,如果我把程序改成这样(即把原来的“this.value++”改成“value++”):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var value = 500;
var obj = {
value: 0,
increment: function () {
value++;
var innerFunction = function () {
alert( this .value);
}
innerFunction();
}
}
obj.increment();
|
结果又是多少呢?
————————————————-思考———————————————————
结果应该是501。因为没有使用this.value的时候,“value++”这一行的这个value是全局的value。
那我把全局的value屏蔽掉吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
var obj = {
value: 0,
increment: function () {
value++;
var innerFunction = function () {
alert( this .value);
}
innerFunction();
}
}
obj.increment();
|
这样的结果又是多少,这回总该是1了吧?
————————————————-思考———————————————————
不,这回会报错:Uncaught ReferenceError: value is not defined。因为在这样使用的时候,obj对象的value对increment方法内部来说直接不可见。
其实,在JavaScript中,有这样几种函数调用模式:
- 方法调用:Fethod Invocation
- 函数调用:Function Invocation
- 构造器调用:Constructor Invocation
- Apply调用:Apply Invocation
这些模式其实你都用过,如果你想知道更多,请阅读这篇文章,上面的例子代码也是从它里面引用来的。
如果你对上面说到的都理解了,那么看看这段代码,结果应该是多少?
1
2
3
4
5
6
7
8
9
|
var value = 500;
var obj = function (){
var value = 0;
return function () {
value++;
alert(value);
}
}
obj()();
|
————————————————–思考——————————————————–
结果应该是1(其实这道题只是一个简单的闭包使用而已,并没有Function Invocation Patterns,我挖了个坑让你跳,嘿嘿),你对了吗?
最后一题,把典型的闭包和Function Invocation Patterns结合起来,你要毫无压力地挺住啊:
1
2
3
4
5
6
7
8
9
10
11
12
|
var value = 500;
var obj = function (){
var value = 0;
var getValue = function (){
return this .value;
};
return function () {
value++;
alert( getValue(value) );
}
}
obj()();
|
结果是多少呢?
————————————————–思考——————————————————–
正确结果是500,这次你对了吗?
文章系本人原创,转载请注明作者和出处(http://www.raychase.net)
注:本博客已经迁移到个人站点 http://www.raychase.net/ ,欢迎大家访问收藏,本ITEye博客在数日后将不再更新。
分享到:
相关推荐
客户端与服务端的代码 RMI全名叫做 remote method invocation
NULL 博文链接:https://kdisk-sina-com.iteye.com/blog/258942
解决axis2-CodegenWizardPlugin的BUG,java.lang.reflect.InvocationTargetException 带有相关jar 包及相关文件
CL_Invocation.ps1
MyEclipse6.0下axis2插件的安装! 解决java.lang.reflect.InvocationTargetException本人亲自测试,完美使用!
MyEclipse axis2 wsdl java.lang.reflect.invocationtargetexception code gen 大家要注意一定要仔细,这个问题基本上缺少包引起的,而且一定要clean 如果需要axis2插件 以及这个plugins中的包在我的其他资源里面有
odi-sdk-invocation.jar
远程方法调用 -- Remote Method Invocation-
6 Encapsulating Invocation: the Command Pattern 191 7 Being Adaptive: the Adapter and Facade Patterns 235 8 Encapsulating Algorithms: theTemplate Method Pattern 275 9 Well-managed Collections: the ...
主要介绍了JQuery报错"Uncaught TypeError: Illegal invocation"的处理方法,需要的朋友可以参考下
Asynchronous Web Service Invocation with JAX-WS 2.0
主要介绍了Ajax方式上传文件报错"Uncaught TypeError: Illegal invocation",非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
Invocation生命周期,Session生命周期,Server生命周期
invocation-chain-monitor RPC调用链监控
在apache上下载的axis2的eclipse插件,使用axis2-eclipse-codegen-wizard时,最后老是报InvocationTargetException异常。 现在上传的版本已经修正,和原版功能完全一样
JDK9-JSE- Java Remote Method Invocation API Guide-2
JDK12-java-remote-method-invocation-api-guide