一道经典的阿里面试题

130 阅读2分钟

有一道非常经典的阿里面试题,原题如下:

        var x = 1;
        function func(x, y = function () { x = 2 }) {
            var x = 3;
            y();
            console.log(x);
        }
        func(5)
        console.log(x);

执行完输出的结果为 3,1 。使我迷惑了好长一段事件,在网上也查看过多篇文章,感觉描述的都不够通透。下面就从浏览器的运行机制来一步一步的刨析这道题。

全局上下文中的 var x = 1; 以及 console.log(x); 存粹是为了其迷惑作用,这题不再做解释。

大家应该都知道,函数在执行的过程中会产生自己私有的上下文,用来保护自己的私有变量和外界的互不干扰。而在这道题中涉及到一个问题,即形参有一个为 x 的,函数中体又通过 var 来声明了一个 x 。那么我们最后打印的应该是哪个 x 呢?

这里面又涉及到一个比较复杂的机制。

  1. 函数中有 ES6 中的形参赋值默认值(无论是否生效)
  2. 函数体中出现基于 var/let/const 声明变量的操作

只要同时满足这两个条件,函数在执行的过程中就会产生两个上下文。函数产生的私有上下文(执行完形参赋值即结束) & 把函数体看成代码块产生的块级上下文(其上级上下文即为函数的私有上下文)。有兴趣的读者可以通过 debugger 在控制台来观察这两个上下文。

很明显,这道题中的函数同时满足的以上两个条件,函数在执行之初,产生了自己的私有上下文。在形参赋值完,也就是 x = 5; y = function () {x = 2}; 之后,又将函数体看成代码块,产生了一个新的块级上下文。这个块级上下文的作用域即为函数的私有上下文。忽略变量提升等阶段,最后块级上下文中会存在一个私有的变量 x ,值为 3 。y 虽然是在块级上下文中执行的,但是其是在函数的私有上下文中创建的,它的作用域也为函数的私有上下文。y 执行, 由于 y 中没有自己私有的变量 x ,需按作用域链到其上级上下文查找。x = 2; 时间就是将函数私有上下文中的 x 的值改为了 2 。接着在块级上下文中输出 x 。块级上下文有自己的私有变量 x ,值为 3 。所以输出的结果也为 3 。

这些步骤大家也可通过 debugger ,在控制台直接观察到。

希望本篇文章对大家有所帮助。