JavaScript中的各种变量提升(Hoisting)
首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确。因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升(Hoisting)。
JS 存在变量提升( Hoisting ),这个的设计其实是低劣的,它允许变量 不声明就可以访问 , 或声明在后使用在前 。新手对于此则很迷惑,甚至许多使用JS多年老手也比较迷惑。但在 ES6 加入 let/const 后,变量Hoisting 就不存在了。
一、 变量未声明,直接使用
function test() {
alert(notDefined);
}
test(); // ?
报错是自然的

二. 变量声明在末尾
function test() {
alert(declaredButNotAssigned); // undefined
var declaredButNotAssigned;
}
test();
输出 undefined, 结果比上例好一些,没有报错,代码可以运行,但变量值可能不是程序员所期望的。
三、 变量声明在末尾,同时给变量赋值
function test() {
alert(declaredAndAssigned); // undefined
var declaredAndAssigned = 1;
}
test();
结果和 二 相同, 很明显, 并不会因为赋值了 就输出 1。
二、三 都发生了变量提升( Hoisting ),简单定义
变量提升(Hoisting): 在指定作用域里,从代码顺序上看是变量先使用后声明,但运行时变量的 可访问性 提升到当前作用域的顶部,其值为 undefined ,没有 可用性。
这里强调 代码顺序 和 运行顺序 ,是因为多数时候我们写的代码都是顺序执行的,即 代码顺序 和 运行顺序 是一致的。比如有过 C语言 经验的程序员
#include <stdio.h>
int main() {
int x = 1;
printf("%d, ", x); // 1
}
两句代码,先声明整数型 x, 再输出。代码顺序和运行顺序是一致的,即正常运行。
如果顺序反过来
#include <stdio.h>
int main() {
printf("%d, ", x); // error
int x = 1;
}
此时,编译都不能通过了。但JS里可以如此,见二、三。
因此,有类 C语言 经验的程序员,都很清楚变量需要 先声明后使用 ,不然会报错。而到了JS里,有 变量提升 现象,可以 先使用后声明 ,C 的经验用到 JS 里迷惑便出现了。
四、 函数表达式也存在变量提升
function test() {
alert(func); // undefined
var func = function() {};
}
test();
但如果想使用这个 func,则无可能
function test() {
alert(func); // undefined
func(); // 报异常
var func = function() {};
}
test();
结果func 是 undefined,调用 func 则会报异常。 在上面的定义中提到了 可访问性和 可用性 对应如下语句。
可访问性:alert(func),输出 undefined,可以运行,可以访问 func。
可用性: func(), 报异常,不能正常调用 func,表示无可用性。
二、三、四 都是使用 var 声明的变量,JS 里函数声明也会存在提升(Hoisting),只是这个 “变量” 比较特殊,它是一个 function 类型(可以作为函数、方法或构造器)。它的名字(标识符)也会提升到当前作用域的顶部。
五、函数声明的名也会提升到当前作用域顶部
function test() {
alert(f1); // function
f1(); // "called"
function f1() {
alert('called');
}
}
test();
我们看到,声明 f1 在代码最末,f1 使用在前,alert(f1) 和 f1() 都正常执行,表示 可访问性 和 可用性 都有了。
前面说了,变量提升(Hoisting)没什么用,属于语言的低劣设计,好的习惯还是 “先声明后使用”。但奇怪的是这个特性会出现在各大公司面试题里,大家讨论起来津津有味。
题1:
// 写出以下代码的运行结果
var a = 1;
function fn() {
if (!a) {
var a = 2;
}
alert(a); // ?
}
fn();
题2:
// 写出以下代码的运行结果
var a = 1;
function fn() {
a = 2;
return;
function a() {}
}
fn();
alert(a); // ?
好在这一切随着 ES6 的 let/const 到来结束了,ES里除全局变量外,其它都推荐使用 let/const,如果把var替换成let,变量提升(Hoisting)就不复存在了。
function test() {
alert(declaredButNotAssigned1); // 报异常
alert(declaredButNotAssigned2); // 报异常
alert(func); // 报异常
let declaredButNotAssigned1;
let declaredButNotAssigned2 = true;
let func = function() {};
}
test();
这强制程序员养成好的习惯,变量需要“先声明再使用”,否则报错。
以下摘自MDN的关于let不在发生变量提升的描述
In ECMAScript 6, let does not hoist the variable to the top of the block. If you reference a variable in a block before the let declaration for that variable is encountered, this results in a ReferenceError , because the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
用 let 声明变量后,typeof 也不再安全
if (condition) {
alert(typeof num); // Error!
let num = 100;
}
以前可以用 typeof == 'undefined',来判断是否引入了某lib,比如jQuery
// 判断jQuery是否引入了
if (typeof $ !== 'undefined') {
// do something
}...
jQuery没有引入,$ 没有声明,这句也不会报错而影响到下面的代码执行,但如果是let声明的就会报错了。
JavaScript中的各种变量提升(Hoisting)的更多相关文章
- JS中作用域和变量提升(hoisting)的深入理解
作用域(Scoping) javascript作用域之所以迷惑,是因为它程序语法本身长的像C家族的语言.我对作用域的理解是只会对某个范围产生作用,而不会对外产生影响的封闭空间.在这样的一些空间里,外部 ...
- JavaScript中函数的变量提升问题
函数的大体分三种,一种是函数的声明,一种是函数表达式(又称为函数的字面量) 1.函数的声明 => function myFn(){}; 2.函数的表达式 => var myFn = fun ...
- JavaScript中变量提升------Hoisting
原谅链接:http://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html 因为这个问题很是经典,而且容易出错,所以在介绍一次.哈哈.莫怪 ...
- js中的变量提升(hoisting)
来看如下代码: function HelloJS(){ var array = [1,2,3,4,5]; for(var i in array){ } alert(i); } HelloJS(); a ...
- Javascript中函数及变量定义的提升
<html> <head> <title>函数提升</title> <script language="javascript" ...
- JavaScript系列文章:变量提升和函数提升
第一篇文章中提到了变量的提升,所以今天就来介绍一下变量提升和函数提升.这个知识点可谓是老生常谈了,不过其中有些细节方面博主很想借此机会,好好总结一下. 今天主要介绍以下几点: 1. 变量提升 2. 函 ...
- JavaScript解析机制之变量提升
1.什么是预解析? 在当前作用域下,JS 运行之前,会把带有 var 和 function 关键字的事先声明,并在内存中安排好.(这个过程也可以理解为变量提升)然后再从上到下执行 JS 语句(预解析只 ...
- Javascript中的循环变量声明,到底应该放在哪儿?
相信很多Javascript开发者都在声明循环变量时犹豫过var i到底应该放在哪里:放在不同的位置会对程序的运行产生怎样的影响?哪一种方式符合Javascript的语言规范?哪一种方式和ecma标准 ...
- js函数、变量提升(hoisting)
其实我只是想复习下变量提升的,然后看到了函数提升,然后再看到了函数声明.函数表达式. 有必要怀着敬仰之心提及园子里的TOM大叔的解密命名函数表达式,不愧是大叔,好好地脑补了下基础知识. 在ECMASc ...
随机推荐
- Spring MVC 学习总结(四)——视图与综合示例
一.表单标签库 1.1.简介 从Spring2.0起就提供了一组全面的自动数据绑定标签来处理表单元素.生成的标签兼容HTML 4.01与XHTML 1.0.表单标签库中包含了可以用在JSP页面中渲染H ...
- HTML5移动Web开发(二)——配置移动开发环境以及简单示例
一.准备 1.配置本地网络服务.对于Windows.Mac和Linux,最容易的方法是使用免费的XAMPP软件:http://www.apachefriends.org/en/index.html X ...
- 为网格布局图片打造的超炫 CSS 加载动画
今天,我想与大家分享一些专门为网格布局的图像制作的很酷的 CSS 加载动画效果.您可以把这些效果用在你的作品集,博客或任何你想要的网页中.设置很简单.我们使用了下面这些工具库来实现这个效果: Norm ...
- mybatis错误之配置文件属性配置问题
在mybatis的配置文件SqlMapConfig.xml中,可以在开始的地方先加载一个properties节点,用来定义属性变量. <!-- 加载属性文件 --> <propert ...
- Apache Kylin 部署之不完全指南
1. 引言 Apache Kylin(麒麟)是由eBay开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据.底层存储用的是HBase,数据输入与cu ...
- JS魔法堂:再识IE的内存泄露
一.前言 IE6~8除了不遵守W3C标准和各种诡异外,我想最让人诟病的应该是内存泄露的问题了.这阵子趁项目技术调研的机会好好的再认识一回,以下内容若有纰漏请大家指正,谢谢! 目录一大坨! 二.内存泄漏 ...
- 在DevExpress中使用WizardControl控件构建多步向导界面
利用好的界面控件,往往能做成比较界面体验效果.在一些界面操作里面,我们可能把它拆分为几部进行处理,这个时候引入WizardControl向导控件应该是比较不错的选择了.多步的处理方式,可以让用户避免一 ...
- DevExpress的XtraReport和微软RDLC报表的使用和对比
我们开发程序的时候,经常会碰到一些报表,如果是Winform的报表,一般可以采用DevExpress控件组的XtraReport,或者微软的RDLC报表,当然还有一些其他的,在此不再赘述.由于本人在W ...
- 只用CSS实现容器内图片上下左右居中
一直以来,大家都知道,DIV容器内设置 text-align:center 即可让图片居中,但是DIV内默认的图片是上对齐,不会上下居中,如果想要实现这样的效果,JS判断是比较麻烦的,因为DIV容器内 ...
- UML - 类图
在UML类图中,有以下几种常见关系: 泛化 - Generalization "is-a",继承关系,表示一般与特殊的关系: 子类(接口)特化父类(接口)的所有特征和行为,是对象之 ...