Javascript 面试题——递归、上下文、函数、栈、变量提升
面试题
1. 以下代码输出了什么?
2. 整个过程产生了几个上下文?
1 console.log('global begin: ' + i); // ? 2 var i = 1; 3 foo(1); 4 function foo(i) { 5 if (i == 4) { 6 return; 7 } 8 console.log('foo() begin: ' + i); // ? 9 foo(i+1); 10 console.log('foo() end: ' + i); // ? 11 } 12 console.log('global end: ' + i); // ?
输出分析
第一行代码输出的是 undefined。为什么是 undefined,而不是报错?代码不是从上往下执行吗?这个就需要了解代码的预处理机制,其实上面的代码就等同于:
var i; console.log('global begin: ' + i); i = 1;
这种现象叫做变量提升。包括函数也是一样,但函数提升是直接创建的,所以要区分变量和函数提升。
foo(); // 报错,仅变量提升,值为 undefined bar(); // 不报错,已经创建函数 var foo = function () { }; function bar() { }
还有补充一点就是先变量提升再函数提升,示例:
1 function foo() { 2 3 } 4 var foo; 5 console.log(typeof foo); // "function"
第 12 行输出的也是 1,这是作用域相关的知识点。
最麻烦的是 foo 函数,它是一个递归函数,里面到底怎么工作的,看下面的执行上下文栈
|
当函数递归到 foo(4) 的时候,满足条件 i == 4 结束递归,退出了函数 foo(4),函数退出了就会从栈中移除,所以就变成了
|
foo(4) 退出后会执行 foo(3) 后面剩余的代码,此时的 i 当然等于 3。举个例子:
1 test(1); 2 function test(i) { 3 test2(i + 1); 4 console.log('test end: ' + i); // test2 退出后就执行我了 5 } 6 7 function test2(i) { 8 return 9 }
以此类推,foo(3)剩余代码执行完后就退出函数,然后再执行 foo(2) 剩余的代码。。
|
到最后就保留了 window 上下文,所以一共有5个上下文产生。
所以最后输出是:
undefined
1
2
3
3
2
1
1
总结
这主要是考查你对Javascript中执行上下文栈的理解。其实这就是个嵌套调用函数,如果要把它拆解成普通的嵌套就十分繁琐,比如下面的,结果一样,估计这样会好理解些。
1 console.log('global begin: ' + i); 2 var i = 1; 3 foo(1); 4 function foo(i) { 5 console.log('foo() begin: '+ i); 6 foo2(i+1); 7 console.log('foo() end: '+ i); 8 } 9 function foo2(i) { 10 console.log('foo() begin: '+ i); 11 foo3(i+1) 12 console.log('foo() end: '+ i); 13 } 14 function foo3(i) { 15 console.log('foo() begin: '+ i); 16 foo4(i+1); 17 console.log('foo() end: '+ i); 18 } 19 function foo4(i) { 20 return; 21 } 22 console.log('global end: ' + i);