(目测是受 ES6 块作用域 TDZ
的影响在 编译阶段 抛出的问题)
{
var foo = 1; // 该句报错,“foo 重复声明”
function foo(){};
console.log(typeof(foo));
}
以下理解是否正确?(希望各位 dalao 不吝赐教
个人理解的变量提升(只考虑 var 的情况)步骤为:
(自学 js 中)最近在看《 you-dont-know-js 》—— js 的变量提升部分(一版,中文)
在作用域-函数优先部分有如下代码:
foo(); // "b"
var a = true;
if (a) {
function foo() { console.log( "a" ); }
}
else {
function foo() { console.log( "b" ); }
}
实际运行时发现 foo(); 一行报 TypeError: foo is not a function 错误。
思考之后觉得应该是 es6 块作用域的问题,导致 foo 的变量提升不如预期。遂更换 node 版本至 4.9 果然成功输出了 "b"
我的理解如下:
var foo;
var a;
foo(); // 此次相当于 foo 已声明,但未定义,暂为 undefined 。故报错
a = true;
if(a){
foo = function(){console.log("a")};
}else{
foo = function(){console.log("b")}
}
在理解上面这个出错问题的时候发现在块作用域下 var foo 变量声明和 foo 函数声明放在一起会报 重复声明 错。如下:
{
var foo = 1; // 该句报错,“foo 重复声明”
function foo(){};
console.log(typeof(foo));
}
已知 var 可重复声明,该情况(指同标识符的变量声明与函数声明)在全局、函数作用域下无 重复声明 问题。
(后测试同样的代码 node4.9 版本无此问题,个人考虑定位至块作用域特性相关问题,调试发现在还没执行下去的时候就已报错了,应该是 编译阶段 就抛出的问题。
个人基础较差,再往下就没啥头绪了,搜索"块作用域 var 函数声明 重复声明"相关字符也没找到结果。
希望各位 dalao 不吝赐教,指点以下该怎么分析这个问题(特性? 实际代码运行的时候是怎么样的情况?
1
dartabe 2020-08-10 02:55:48 +08:00
好像就是重复申明了变量啊 改了一下就对了
{ var foo = 1; // 该句报错,“foo 重复声明” foo = function (){}; console.log(typeof(foo)); } |
2
ZacharyM OP @dartabe emm 问题不是怎么改,主要是想知道 js 具体是怎么处理和判断这个错误的。
另外有趣的是在 Edge(非 chrome 内核版本,Microsoft Edge 44.19041.1.0)、ios(13.5.1)上的 safari 以及 nodejs6.0.0 以下版本执行不报错,直接输出"number"。 Firefox,safari,chrome,nodejs6.0.0 及以上的才会报重复声明的错 |
3
dartabe 2020-08-10 03:32:20 +08:00 1
@ZacharyM
The function declaration in the block uses ES6 declaration semantics (like let or const), which does not allow redeclarations. 帮你在 stackOverFlow 上查了 还是多 google 好...... |
4
ZacharyM OP @dartabe 这里用的是 var,不算 ES6 declaration semantics 吧。
|
5
dartabe 2020-08-10 04:16:47 +08:00
@ZacharyM
好像是 function declaration 的行为和 let const 一样 改为 let 有同样问题 { var foo = 1; // 该句报错,“foo 重复声明” let foo = function(){}; console.log(typeof(foo)); } |
6
ianva 2020-08-10 05:45:08 +08:00
这个要看 ecma262 的规范,才比较好理解,推荐篇两篇文章,看懂执行模型就理解了,第一篇是 08 年的,不过讲的最清楚,不了解规范会有些复杂
https://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html https://juejin.im/post/6844903704466833421 |
7
ochatokori 2020-08-10 08:06:50 +08:00 via Android 3
你可以理解成 function xx 声明都会被改写成
var xx // 被提前到代码最开头 //执行你声明 function 前的代码 xx=function(){} 所以你写的代码执行顺序就是 var foo // function 声明的效果 var foo = 1; // 该句报错,“foo 重复声明” foo=function(){}; console.log(typeof(foo)); 其实没必要纠结这些,你也知道不同执行环境有不同执行结果,那就要避免写出这种代码,谁写生产环境写出这种代码那他会被捶死 |
8
Doracis 2020-08-10 09:27:52 +08:00
@ochatokori 大佬正解,最后一句高亮,一般这样的代码是真的不会放生产的,不符合代码规范不说,一旦出现 bug 会被 leader 锤死,老老实实搬砖不香吗
|
9
palmers 2020-08-10 09:37:09 +08:00
```js
{ var foo = 1; // 该句报错,“foo 重复声明” function foo(){}; console.log(typeof(foo)); } ``` 最后的代码 在 ES5 中应该是会报错的吧? 我想是因为函数声明提升和变量声明提升 但是函数声明提升优先于变量声明提升, 所以变成这样了: ```js { var foo = function() {} var foo = 1; console.log(typeof(foo)); } ``` 所以在做提升的时候 也就是编译阶段就已经知道重复声明变量了 会提示 Identifier 'foo' has already been declared 我理解是这样 |
10
yaphets666 2020-08-10 09:46:06 +08:00
@ochatokori 好像是这样的
var foo // function 声明的效果 var foo // 该句报错,“foo 重复声明” foo = 1; foo=function(){}; console.log(typeof(foo)); |
11
krapnik 2020-08-10 10:26:16 +08:00
1.ES6 规定,块级作用域之中,函数声明语句的行为类似于 let ;
2.函数声明还会提升到所在的块级作用域的头部。 https://es6.ruanyifeng.com/#docs/let#块级作用域 |
13
Arrowing 2020-08-10 11:14:38 +08:00
本来呢,var 确实是可以重复声明。
但是你用块级作用域({}) + 函数声明语句( function a(){})之后,就不可以重复声明了。 ES6 规定,块级作用域之中,函数声明语句的行为类似于 let 。 只要使以上 2 个条件任意一个失效即可。 1 、块级作用域干掉,改为用 function 包裹的方式; 2 、用函数表达式声明函数,即 var foo = function(){} 。 |
14
frankkai 2020-08-10 11:18:02 +08:00
工作几年会发现 这是个无聊的问题。。
|
15
yzqtdu 2020-08-10 13:33:08 +08:00
这个问题跟 ECMAScript 语言定义有关,ES5 之前,块内的函数声明是未定义行为,具体表现跟旧浏览器的实现有关,总体来说,这种方式是不推荐的。ES6 之后引入了块级作用域,对块内函数声明的语义也进行了定义,可以理解为 let 或 const,因此会报错。https://www.ecma-international.org/ecma-262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
为了历史兼容,其实块内函数声明会带来一些“bug”,具体见 https://www.zhihu.com/question/404772996 |
16
ChanKc 2020-08-19 12:41:57 +08:00 via Android
无聊的问题
养成习惯,手动提升,万事大吉 |
17
u823tg 2020-08-28 00:13:03 +08:00
这种问题没必要研究, 这是 js 缺陷问题。 除非考 js 各种奇淫巧计
|