一道有点东西的 JS 变量提升题

简介: 前端西瓜哥

大家好,我是前端西瓜哥。今天在一个前端交流群里,一个群友发了这么一道题。

function fn(a) {
  console.log(a);
  var a = 2;
  function a() { }
  console.log(a);
}
fn(1);

一看就知道是变量提升题。然后我试着猜了下,果不其然做错了。var 和它的变量提升果然让人迷惑。

我们先学习一些底层的一些知识。

var

var 是 ES6 之前用来声明变量的关键字。var 这人看起来挺机灵,但是干起活来却非常离谱。

块作用域中用 var 写的变量声明,会往外跑,跑到最近的函数作用域或全局作用域中

当然也有一些比较特殊的情况,比如 withtry...catch,它们可以形成特殊的块作用域,让变量不往外跑。

一道非常经典的循环使用定时器的题目就是考察了这个特性:

for (var i = 0; i < 5; i++) {
  setTimeout(function(){
    console.log(i);
  }, i * 1000);
}

这题我不讲,因为太经典了,没做过的朋友可以试着做一下。

解决方案是用 IIFE(立即执行函数表达式)来形成一个 var 不会外溢的函数作用域,当然实际开发中更常见的做法是抛弃 var,使用支持块作用域的 let/const。

如果一个变量名没有被声明过,且没有使用 var 就赋予了初始值,它就会成为全局作用域下的变量

function setA() {
  a = 1;
}
setA();
console.log(a); // 1

如果多次声明同一个变量, 之后的 var 声明会被忽略掉

var a = 1;
var a = 2;
var a = 3;

等价于

var a = 1;
a = 2;
a = 3;

函数声明你也可以认为是一个 var 声明,千万不要以为后面的 var 声明能够覆盖掉同名的函数声明。

声明的提升

变量声明和函数声明都是会发生 提升 (hoist)的。

怎么理解,就是在执行代码前,会先扫描一下代码,将其中类似 var a = 1 的语句中声明的部分,也就是 var a 先找出来,先执行。

这是编译器的行为,所以在表现上,就是声明被放到了代码的开头,成为 提升

除了 var / let / const 声明的变量会提升,函数声明也会提升,且函数声明会优先于变量提升,即放到变量声明的上方

b();
var a = 1;
function b() { console.log(a) };

等价于

function b() { console.log(a) };
var a;
b(); // 输出 undefined
a = 1;

有一个易错点,就是把函数表达式当成了函数声明。函数表达式是不会发生变量提升的,切记

回到本题

function fn(a) {
  console.log(a);
  var a = 2;
  function a() { }
  console.log(a);
}
fn(1);

首先是函数的参数 a,它等价于一个外部的一个变量,上面代码等价于:

var a = 1;
(function() {
  function a() { }
  console.log(a);
  a = 2;
  console.log(a);
})();

这里函数声明做了提升,跑到最顶部。接下来考虑给 var a = 2 做提升。但我们的函数 a 的声明已经提升了,所以这里的 var 算是重复声明了,直接去掉 var,最终得到上述代码。

我们很容易看出答案是:

ƒ a() { }
2

本题除了考察比较常规的声明提升,还考察了一个比较特别的情况,就是 函数的参数声明应该放在哪里

答案是放到一个额外的包裹着函数体的中间层中,而不是直接放到函数体的顶部。

说真的,谁要是在工作中这样写代码,我可能要好好问候一下他。

最后

正经前端开发在实际工作中都是使用 let / var 的,然后交给编译器转换为兼容 ES5 的使用 var 的代码,再发布到生产环境。

var 是个坏文明,总是在秀它那没有下限的骚操作,尤其是在嬉皮笑脸的面试官的操纵下。

相关文章
|
8天前
|
JavaScript 前端开发 开发者
【Web 前端】什么是JS变量提升?
【5月更文挑战第1天】【Web 前端】什么是JS变量提升?
【Web 前端】什么是JS变量提升?
|
8天前
|
JavaScript 前端开发 开发者
js开发:请解释变量提升(hoisting)是什么,以及它是如何工作的。
JavaScript中的变量提升(Hoisting)在编译阶段将`var`声明的变量和函数声明提升到各自作用域顶部。变量默认值为`undefined`,函数声明可先调用后定义。但赋值、`let`和`const`以及函数表达式不被提升。现代实践建议避免依赖此特性,以增加代码可读性。
|
8天前
|
前端开发 JavaScript Java
除了变量提升,JavaScript还有哪些特性?
【2月更文挑战第20天】【2月更文挑战第61篇】除了变量提升,JavaScript还有哪些特性?
|
8天前
|
JavaScript 前端开发
解释 JavaScript 中的变量提升现象。
解释 JavaScript 中的变量提升现象。
17 4
|
8天前
|
JavaScript 前端开发
JavaScript开发基础问题:解释一下变量提升。
JavaScript开发基础问题:解释一下变量提升。
43 5
|
8天前
|
JavaScript 前端开发
变量提升:JavaScript代码是按顺序执行的吗?
变量提升:JavaScript代码是按顺序执行的吗?
29 0
|
9月前
|
JavaScript 前端开发
JavaScript 为什么要进行变量提升,它导致了什么问题?
JavaScript 为什么要进行变量提升,它导致了什么问题?
|
9月前
|
JavaScript
理解js的变量提升
理解js的变量提升
36 0
|
JavaScript 前端开发
深入理解javascript变量提升机制
深入理解javascript变量提升机制
58 0
|
JavaScript 前端开发 程序员
浏览器原理 06 # 变量提升:JavaScript代码是按顺序执行的吗?
浏览器原理 06 # 变量提升:JavaScript代码是按顺序执行的吗?
86 0
浏览器原理 06 # 变量提升:JavaScript代码是按顺序执行的吗?