你知道下面的JavaScript代码执行时会输出什么吗?

var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
console.log(foo);
}
bar();

答案是“10”,吃惊吗?那么下面的可能会真的让你大吃一惊:

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

答案是 “1”。为什么会这样呢,这就涉及到 JS 里面的作用域、作用域链和提升的相关知识了。

首先要明白 作用域和作用域链的内容(点击阅读),现在主要介绍提升。

在 JavaScript 中,当遇到 var a = 1; 这个语句时,我们可能会这么认为,“为一个变量分配内存,给这个变量命名为 a,再把 1 保存进去”。然而这个过程并不完全正确。当编译器遇到 var a = 1 时,会做以下两个步骤:

1. 遇到 var a ,首先会先看看当前的作用域中是否已经有 a,若有,就不再声明 a,若没有,就会在当前的作用域,创建一个 a

2. 然后处理 a = 1 。先查看当前作用域,是否有 a 。如果有,就把 1 赋值给 a 。如果没有,就向上一个作用域中寻找 a,直到寻找到全局作用域。全局作用域中如果还没有,就会抛出异常。

所以,当我们看到 var a = 1; 会认为这是一个声明,实际上这是两个声明, var a 和 a = 1 。第一个声明会在编译阶段进行,第二个声明会在原地等待执行。函数声明和变量声明总会被 JavaScript 的编译器提升到他们所在作用域的顶部。有了这些知识就可以解释上面的代码了

第一段代码实际上如下图:在遇到并执行 bar()函数时,执行到 if 语句里面的 foo,此时会先查询 bar()函数的作用域内有没有声明 foo:

若没有声明,就像上一个作用域中寻找 foo 有没有被声明。

若声明了,就直接将声明提升到 bar 作用域里的顶部(foo = 10 留在原地按顺序等待执行),这样,foo 就是 undefined,!foo 就是 true,可以进入 if 循环。

第二段代码实际上如下图:执行 b()时,首先就是 a = 10;这条语句,执行这条语句之前,先看 a 在 b 中是否被声明,若没有被声明就向上一个作用域中寻找 a ,现在的情况是,在 b 函数内部,a 以函数声明的方式,被声明了,那么就要把这个 a 函数声明提升到 a 所在的作用域的顶部,再按顺序执行后面的代码。

对于提升总结的知识点:

1. 函数声明和变量声明都会提升,但在同一个作用域相同名字的函数声明优先于变量声明。

2. 变量声明会提升,但变量赋值的过程不会提升,会在原地等待被执行。

3. 提升后,赋值语句会向前覆盖提升的内容。例子 2 中,b 函数的内部,最开始 a 是函数提升,a 的本质是函数,但是函数提升后,执行了一个赋值语句:a = 10;此时,a 就是一个 number 类型的变量了。

对于这种情况,我们在写 JavasScript 代码的时候该怎么做呢?

使用单 var 模式:避免变量提升所带来的问题

/*jslint onevar: true [...] */
function foo(a, b, c) {
var x = 1,
bar,
baz = "something";
}

本文参考以下资料:

颜海镜的博客:JavaScript作用域链和提升机制

你不知道的JavaScript

JavaScript高阶程序设计

JS 作用域与变量提升---JS 学习笔记(三)的更多相关文章

  1. JS作用域、变量提升和闭包

    作用域 作用域可以理解为JS引擎执行代码的时候,查找变量的规则. 从确定变量访问范围的阶段的角度,可以分为2类,词法作用域和动态作用域.js是词法作用域. 从变量查找的范围的角度,可以分为3类,全局作 ...

  2. js 作用域,变量提升

    先看下面一段代码: 代码执行的结果是: 1st alert : a = 0 2nd alert : a = undefined 5th alert : a = 0 3rd alert : a = 3 ...

  3. JS作用域和变量提升看这一篇就够了

    作用域是JS中一个很基础但是很重要的概念,面试中也经常出现,本文会详细深入的讲解这个概念及其他相关的概念,包括声明提升,块级作用域,作用域链及作用域链延长等问题. 什么是作用域 第一个问题就是我们要弄 ...

  4. js作用域和变量提升

    Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of ...

  5. JS 函数作用域及变量提升那些事!

    虽然看了多次js函数作用域及变量提升的理论知识,但小编也是一知半解~ 这几天做了几道js小题,对这部分进行了从新的理解,还是有所收获的~ 主要参考书籍: <你不知道的JavaScript(上卷) ...

  6. JS _函数作用域及变量提升

    虽然看了多次js函数作用域及变量提升的理论知识,但也是一知半解~ 这几天做了几道js小题,对这部分进行了从新的理解,还是有所收获的~ 主要参考书籍: <你不知道的JavaScript(上卷)&g ...

  7. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 ——持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  8. javascript中的变量作用域以及变量提升

    在javascript中, 理解变量的作用域以及变量提升是非常有必要的.这个看起来是否很简单,但其实并不是你想的那样,还要一些重要的细节你需要理解. 变量作用域 “一个变量的作用域表示这个变量存在的上 ...

  9. javascript中的变量作用域以及变量提升详细介绍

    在javascript中, 理解变量的作用域以及变量提升是非常有必要的.这个看起来是否很简单,但其实并不是你想的那样,还要一些重要的细节你需要理解变量作用域 “一个变量的作用域表示这个变量存在的上下文 ...

随机推荐

  1. js中filter的用法

    filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素. 和map()类似,Array的filter()也接收一个函数.和map()不同的是,filter()把传入的函 ...

  2. 微软移除WIN10密码过期政策Microsoft Removes Password-Expiration Policy in Windows 10

    Microsoft this week announced a series of changes to the security baseline in Windows 10, including ...

  3. visual studio 中被遗忘的任务列表和书签

    任务列表(Task List)是VS中被人遗忘的一个功能,用到跳转到不同的代码段非常不便.以后就不用每次前进和后退导航了. 使用“任务列表” 跟踪使用 TODO 和 HACK或自定义令牌等令牌的代码注 ...

  4. nvidia-smi 实时查看

    需要用到 watch 命令: watch -n nvidia-smi

  5. WebApi接口访问异常问题。尝试创建“testController”类型的控制器时出错。请确保控制器具有无参数公共构造函数

    本来运行的好好的webAPI 接口突然报了个 :“尝试创建“testController”类型的控制器时出错.请确保控制器具有无参数公共构造函数” 错误.耗了半宿最终解决了, 原因: api控制器中引 ...

  6. Vue系列之 => 结合webpack使用vue-router

    安装 vue-router cnpm i vue-router -S index.html <!DOCTYPE html> <html lang="en"> ...

  7. mysql 5.7 配置文件说明

    1.配置文件样例 [client] #password= socket=/data/var/mysql/mysql.sock [mysqld_safe] pid-file=/data/var/mysq ...

  8. wm_concat函数的排序问题

    wm_concat在行转列的时候非常有用,但在行转列的过程中的排序问题常常难以控制. 可见下面例子: 准备测试表: drop table t; create table t (n number,m n ...

  9. cocos dos命令

  10. win10电脑只有自带浏览器能上网

    今天遇到了个奇葩问题,开机后登陆微信,怎么也登陆不上,用360检测,修复不了,找来大神一枚,打开我的Edge浏览器,各种网页浏览正常.......就告诉我就修好了.WTF!!! 现象:只有自带浏览器能 ...