JS奇怪的函数作用域

310 阅读3分钟

这是我参与8月更文挑战的第4天,活动详情查看: 8月更文挑战

前言

最近看到同事分享的一个关于作用域的题目,看着挺简单的样子。然后就是hmmmm...我没答对,所以有了这篇文章。

题目大概长这样(我稍微改了下):

console.log(c in window, window.c, typeof c);  
console.log(b in window, window.b, typeof b)
{
    console.log(c in window, window.c, typeof c);  
    function c(){};
    console.log(c in window, window.c, typeof c);  
}

function b() {}
console.log(b in window, typeof b) 

我第一感觉答案应该是:

false undefined "undefined"
true f b(){} "function"

true f c(){} "function"
true f c(){} "function"

true "function"

然后在浏览器跑了下,懵了:

image.png

我基本上完美的避开了所有的正确答案。

分析

in操作符

这里自己当时傻了,c in window中的c是个变量名,应该要找到它真正对应的值去判断才行。

image.png

函数名称保存着函数的引用地址,所以用in操作符会返回false。

但是为啥第一个c in window返回true了,还没想通。按道理函数声明提升不会提升到这一块,也做了下边的测试:

console.log(c in window, window.c, typeof c);  
c();
{
    function c(){}; 
}

结果如下:

image.png

c函数未定义,符合预期。看表现此时的c应该是undefined

image.png

果然c是undefined,但是我们知道,既然函数申明并没有提升到这块作用域,访问c浏览器应该会报错,但为啥是undefined呢??

我们知道ES5中,函数只能申明在顶层作用域和函数作用域中,不能在块级作用域中申明,但是ES6标准中,允许在块级作用域声明函数。在阮大的ECMAScript6入门中提到:针对块级作用域中申明函数的处理,浏览器是不遵守块级作用域的规定,它有自己的方式:

  • 允许在块级作用域内申明函数
  • 函数声明类似于var,会提升到全局作用域或函数作用域的头部
  • 整个函数也会提升到所在块级作用域的头部

所以,上边的c函数就类似于:

var c;
console.log(c in window, window.c, typeof c);  //true undefined "undefined"
{
    function c(){};
}

函数声明提升

我们知道函数的声明是可以提升到当前函数的作用域的最前边。

所以b函数可以被window访问到,typeof b为‘function’无异议。好了重点来了,代码块中的c函数怎么解释呢??这里的window.c的表现好像跟标准说的不太一样啊~

{
    console.log(c in window, window.c, typeof c);  // false undefined "function"
    function c(){};
    console.log(c in window, window.c, typeof c); // false f c(){} "function"
}

在第二次输出的时候看到window.c已经是挂在全局的函数了,那是不是可以块级作用域外边访问呢??

image.png

现在块级作用域外也可以访问c函数了,所以为什么呢??暂时我也不知道(存疑)

严格模式

上边代码如果在严格模式下会怎么样呢??

'use strict'
console.log(c in window, window.c, typeof c);  
{
    console.log(c in window, window.c, typeof c);  
    function c(){
        console.log(111)
    };
    console.log(c in window, window.c, typeof c);
}
c();

执行结果:

image.png

可以看到,严格模式下,函数声明只在块级作用域内部生效。

image.png

node环境

console.log(c in global, typeof c); 
console.log(b in global)  //false
{
    console.log(c in global, typeof c);  
    function c(){
        console.log('1111')
    };
    console.log(c in global, typeof c);  
}
c();
function b() {}
console.log(b in global)  

运行结果:

image.png

符合预期结果,看一下严格模式:

image.png

image.png

可以看出严格模式下的node是完全遵从ES6标准的,申明在块级作用域的函数只能在块级作用域内访问。