关于es6块级作用域内函数声明的问题

foo(1)    //报错
{
    foo(2)    //2
    function foo(v) {
        console.log(v)
    }
    foo(3)    //3
}
foo(4)      // 4

//代码运行环境node9.0

函数声明foo正常情况下在块级作用域内,被提升到声明所在到块级作用域的最上方,所以foo(1)报错,foo(2),foo(3)成功执行,但为什么foo(4)能够执行.
虽然了解es规范为了兼容代码,允许js引擎按照自己的方式执行代码,但还是无法理解foo(4)为什么能被执行,求指点。

阅读 4.1k
4 个回答

在早期(es6之前),Block-level 的函数是一个语法错误,但是被浏览器兼容且能够执行。所以你的代码运行环境在 es5, 就会出现这种 foo(4) 的值为 4 的怪异现象。假若我们让代码执行在 'use strict' 环境。

'use strict'
console.log(typeof foo)  // 'undefined'
{
    foo(2)    //2
    function foo(v) {
        console.log(v)
    }
    foo(3)    //3
}
console.log(typeof foo) // 'undefined'

也就是说,执行完块级作用域后,函数 foo() 就被销毁了。所以,如果要使用 Block-level 函数时,更好的替代是函数表达式。

foo(1)    //报错
{
    foo(2)    // 报错
    let foo = function (v) {
        console.log(v)
    }
    foo(3)    //3
}
foo(4) // 报错

至于为什么在非 strict mode,foo(4) 不报错,这应该就涉及到浏览器实现问题,对于我们理解这个问题并不重要。

参考Block-Level Functions

foo函数虽然是在块作用域内生明的,但是他的上下文仍然指向window,因为他是使用 function xx(){} 这种形式生明的,而如果改成 let foo = function(){}这种生明方式,那么foo(4)就会报错了

多说一句,函数内生明和块级作用域生明同理,只不过更严格,因为 var foo=functolion(){},也能把函数的使用范围限制住

foo声明的地方并不能理解为块级作用域,理解js的{}需要看{}的内容

if(condition){
    var a = 1 // a在外部仍然是可以使用的,也存在作用域提升的问题,并不能当然块级作用域考虑
}
// 如果是使用ES6的声明
if(condition){
    let a = 1 // 此时的{}才会形成块级作用域
}

我最近看书也发现了这个问题。按书上说的(根据es6规范),你这段代码的foo(4)也会报错,按照正常的理解也应该会报错才对。我在chrome浏览器试了一下结果却跟你的一样,得use strict才会出现理想结果(即foo(4)报错)。这应该算是function语法的一个“bug”吧。
我是这么理解的:

{}里面用function定义的“函数名”会被提升到它的父级function的作用域(一开始是undefined)
接着在块级作用域{}里面、最上方进行赋值,之后才能调用

上面代码等效于:

let foo = undefined;    //foo被提升到父级function作用域(这里是全局)
foo(1)    //报错
{
    foo = function (v) {    //
        console.log(v)
    }
    foo(2)    //2
    foo(3)    //3
}
foo(4)      // 4

所以foo(1)报错,foo(4)就能执行。

下面是我的更复杂的例子,希望帮助你理解:
(按照正常理解应该是输出outside,书上也说是“输出outside”,但实际上我测试时却报错了。得use strict才会输出outside)
测试的时候记得每次刷新浏览器

function f(){
    console.log('I\'am outside!');
}
(function(){
    //'use strict'
    if(false){    //这里为true时,输出两个inside。
                //需要再use strict,才会输出理想结果:inside和outside
        //重复声明该函数
        function f(){
            console.log('I\'am inside!');
        }
        f();
    }
    f(); //这里会报错,而不是outside
        //因为相当于在闭包function最上方被let f = undefined;形成“暂时性死区”了。
        //而下面的if(false)却使f无法赋值成为一个函数。所以报错f is not a function
        //use strict时,才会输出理想结果:outside
})();

//改变if(false/true)、'use strict'会得到4种不同结果
//false:报错
//true:输出两个inside
//false + 'use strict':输出一个outside
//true + 'use strict':输出inside和outside

上面代码等效于:

function f() {
    console.log('I\'am outside!');
}
(function(){
    let f = undefined;
    if(false){
        f = function() {
            console.log('I\'am inside!');
        }
        f();
    }
    f();     //if(false),所以无法赋值,执行后报错
})();

综上,要想使function在块级作用域{}中达到理想标准的效果,还得'use strict'

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏