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 ...
随机推荐
- [Node.js] Node.js中的流
原文地址:http://www.moye.me/2015/03/29/streaming_in_node/ 什么是流? 说到流,就涉及到一个*nix的概念:管道——在*nix中,流在Shell中被实现 ...
- 机器学习&数据挖掘笔记_14(GMM-HMM语音识别简单理解)
为了对GMM-HMM在语音识别上的应用有个宏观认识,花了些时间读了下HTK(用htk完成简单的孤立词识别)的部分源码,对该算法总算有了点大概认识,达到了预期我想要的.不得不说,网络上关于语音识别的通俗 ...
- Hadoop阅读笔记(一)——强大的MapReduce
前言:来园子已经有8个月了,当初入园凭着满腔热血和一脑门子冲动,给自己起了个响亮的旗号“大数据 小世界”,顿时有了种世界都是我的,世界都在我手中的赶脚.可是......时光飞逝,岁月如梭~~~随手一翻 ...
- tiltShift.js - CSS3 滤镜实现移轴镜头效果
tiltShift.js 是一款很棒的 jQuery 插件,使用 CSS3 图片滤镜来实现照片的移轴镜头效果.使用非常简单,使用 data 属性配置参数.温馨提示:为保证最佳的效果,请在 IE10+. ...
- Head插件——学习Elasticsearch的锋刃利器!
在学习Elasticsearch的过程中,必不可少需要通过一些工具查看es的运行状态以及数据.如果都是通过rest请求,未免太过麻烦,而且也不够人性化. 此时,head可以完美的帮助你快速学习和使用e ...
- js基础篇——localStorage使用要点
localStorage主要用来替代cookie,解决cookie(可参考cookie使用要点)读写困难.容量有限的问题.localStorage有以下几个特点 1.localStorage是一个普通 ...
- Visual Studio开发Cordova应用示例
作者:Grey 原文地址:http://www.cnblogs.com/greyzeng/p/5455728.html 本文的GIF动画均使用ScreenToGif进行录制. Cordova是什么? ...
- canvas导出图片方法总结
html代码 <canvas id="canvas" width="100" height="100" ></canvas ...
- 简单的描述Java中的构造函数,及访问修饰符
作为一个Java 初学者,对Java的理解可能有些片面,甚至有些错误的理解,对于观看此处的您,希望您选择性观看!!! 访问修饰符: 1.常用访问修饰符: public 共有的 private 私有的 ...
- 【转载】Memcached在.Net中的基本操作
一.Memcached ClientLib For .Net 首先,不得不说,许多语言都实现了连接Memcached的客户端,其中以Perl.PHP为主. 仅仅memcached网站上列出的语言就有: ...