<script type="text/javascript">
function test(num) {
this.m_num = 5;
var func1 = function() {
alert(num);
alert(this.m_num);
}
this.func2 = function() {
func1();
}
this.func3 = func1;
}
new test(1).func2();
new test(1).func3();
</script>
运行结果:
1
undefined
1
5
这道题考了至少三个知识点:
1、函数的执行环境
2、this 绑定
3、匿名函数的特殊性
首先,每个函数在执行时都会创建一个属于自己的执行环境。每个执行环境都对应一个对象,这个对象包含了在当前执行环境中定义的所有变量和函数。在 Javascript 中,是没有面向对象语言中所谓的块作用域的,变量的作用域就是通过它所在函数的执行环境限定的。换句话说,是借助于执行环境的关联对象来实现的。当一个函数在另一个函数内部执行时,它的执行环境会嵌套在包含它的函数的执行环境中。此时,内部环境可以访问外部环境中的变量,但反过来则是不允许的。当有两个及以上函数嵌套时,这些函数的执行环境组成了一个作用域链。在解析一个变量时,解析器首先会去当前执行环境中查找是否有其定义,如有则停止查找并返回值;否则会沿着作用域链向上一层执行环境中查找,如发现定义则停止查找立即返回;如还未找到,解析器会再次沿着作用域链向更外一层执行环境中查找,直至找到为止;如遍历完最外层作用域之后仍未找到,此时解析器就会把它当成一个尚未定义的变量处理。在 Web 浏览器中,任何时候,尚未定义的变量都会被当作全局作用域中的变量,全局作用域即 window 域。同时,任何自定义函数的最外层执行环境的始终是全局执行环境。
任何函数都具有两个对象:this 和 arguments。在函数内部,可以通过 this 来访问其作用域中的变量。但 this 并不是指与函数执行环境关联的对象,而是指调用函数的对象。在浏览器中,任何函数都是作为某个对象的方法来调用的。我们可以通过函数的两个固有方法 apply() 和 call() 来指定函数的调用者,同时也改变了其执行环境。用法如下:
function showName(){
alert(this.name);
}var jeneral1 = {name:'zhangfei'};
var jeneral2 = {name:'zhaoyun'};showName.apply(jeneral1);
showName.apply(jeneral2);
所以,this 所指的对象并不能在函数声明时确定,而是和其具体的执行环境密切相关,是一个运行时的概念。
在函数表达式中,我们常用匿名函数作右值:
var init = function(){
…
};
匿名函数特殊的地方在于其 this 绑定于 window 对象,但仍然遵守作用域链的规则。匿名函数常用来模仿块级作用域,如下形式:
(function(args){
…
})(args)
好,有了上面的了解之后再回来看题。首先,原题可以写成如下的等价形式:
<script type="text/javascript">
function test(num) {
this.m_num = 5;
this.func2 = function() {
(function() {
alert(num);
alert(this.m_num);
})();
}
this.func3 = function() {
alert(num);
alert(this.m_num);
};
}
new test(1).func2();
new test(1).func3();
</script>
这下就直观多了!执行函数 func2() 的时候,由于函数体包含的是一个匿名函数,它可以访问外部环境中的对象变量,故为 1 。然而它的 this 却指向 window 对象,全局作用域中没有定义 m_num ,所以是 undefined。执行 func3() 时,由于它是调用对象的方法,因此 this 指向这个对象,故为 5。
完毕。