已在全局作用域提升的JS函数为何会出现在块级作用域中?
块内函数声明的作用域行为解释
复现代码
console.log(x); // logs undefined { let a; function x() { let p; var q; } } x();
行为原因说明
这一现象本质是ES6规范对非严格模式下块内函数声明的兼容设计,浏览器为了兼容ES5时期的旧代码做了特殊处理,规则如下:
- 全局作用域预解析阶段,会先为块内声明的函数
x创建一个全局绑定,初始值为undefined,这就是你在块外提前打印x得到undefined的原因。 - 进入块级作用域后,块内会单独提升函数声明
x,此时块级作用域内的x已经是完整的函数对象,但这个值还没有同步到全局的x绑定上,所以此时在块外调用x()会抛出TypeError: x is not a function,因为全局的x此时还是undefined。 - 当代码执行到块内
function x() {}这一行的位置时,浏览器会把块内x的当前值同步给全局的x绑定,所以你进入函数内部断点时,window.x已经可以打印出完整的函数定义,块外也可以正常调用x()。
不同浏览器差异说明
Safari和Chrome/FF表现不同,是因为不同浏览器对ES6之前的旧兼容逻辑实现有差异。如果切换到严格模式,所有浏览器的表现会完全统一:
'use strict'; console.log(x); // 直接抛出 ReferenceError: x is not defined { let a; function x() {} } x(); // 同样抛出 ReferenceError: x is not defined
严格模式下块内的函数声明作用域仅限制在块内,不会泄露到全局,也不会提前在全局创建undefined的绑定,完全符合块级作用域的规则。
最佳实践
不要在块内写函数声明,如果确实需要块级的函数,优先使用const x = function() {}或者箭头函数的写法,行为完全可控,不会出现兼容问题。
内容的提问来源于stack exchange,提问作者Samarth Saxena
相关产品推荐
相关产品推荐

