一. 题目
英文原文地址:http://dmitry.baranovskiy.com/post/91403200
其中有五段小代码,用来测试是否理解 JavaScript 的核心(core),闭包(closures)和作用域(scopes)。先尝试回答每段代码中alert语句的结果,然后再作实践检查。
if (!("a" in window)) { var a = 1; } alert(a);
var a = 1, b = function a(x) { x && a(--x); }; alert(a);
function a(x) { return x * 2; } var a; alert(a);
function b(x, y, a) { arguments[2] = 10; alert(a); } b(1, 2, 3);
function a() { alert(this); } a.call(null);
二. 解析
解析参考:http://bbs.csdn.net/topics/390300541
首先会解析所有函数,其次是var声明的变量,但是不会赋值。因为JavaScript没有块(block)的概念。像for(var i in array)这里的i依然是全局变量,这点非常的不同于Java。因此,这几行的代码执行顺序是:
- var a; // 声明一个变量,但是不会赋值!
- if...语句:因为步骤1已经定义了a变量(没赋值),所以"a" in window为真,取反为假。所以不会执行大括号里面的赋值语句。
- alert(a); // 显然,结果为undefined
第二题解析:
我们可以用多个逗号将变量分开定义,只用一个var。另外函数表达式类似于局部变量,不会被全局作用域中访问到。执行步骤:
- 声明两个局部变量(因为有var限定符)a,b;并给他们赋值a=1,b=function a(){...};这里的function a是局部变量,外部无法访问到。
- alert(a); // 结果为1
JavaScript永远是先解析声明式函数,再解析变量。所以,它的执行顺序为:
- 解析函数a;
- 声明变量var a;因为a此时没有被赋值,所以它还是指向原来的值。即function a;
- alert(a); // 那就是function a(x){...}
在函数内部可以引用一个对象,它是arguments类似数组,但不是数组。它代表了函数实际接收参数的集合。可以通过下标对相应参数进行访问。如果修改此对象某些属性,如arguments[index],则被传进来的第index(如果有的话,下标从0开始)变量的值也会被修改。它的执行顺序为:
- 声明一个函数b;
- 执行函数b(1,2,3);因为这里arguments[2]与变量a引用的是一个值,所以当arguments[2]改变时,a也随之改变。结果为10。
注意1:形参优先级高于arguments
function fn(arguments){ alert(arguments); } fn('hello'); // -> "hello"
注意2:在JavaScript中如果定义了2个或2个以上的相同名字的函数,则只有最后定义的函数才有效。
function b(x, y, a) { arguments[2] = 10; alert(x); } function b(x, y, a, b) { arguments[2] = 10; alert(y); } b(1, 2, 3); // -> 2
call方法接受多个参数,其作用是借用别人的方法当作自己的方法。这样能保证执行的时候this能够指向自己。
call方法的第二个参数到最后一个参数是传给借用过来函数的。第一个参数是借用的对象,如果这个对象为空,那么将会作为全局window对象调用。即函数中的this指向window,结果为[object Window]。
经验:a()/a.call()/a.call(null)无区别。