JavaScript中标识符提升问题


Posted in Javascript onJune 11, 2015

JS 存在变量提升,这个的设计其实是低劣的,或者是语言实现时的一个副作用。它允许变量不声明就可以访问,或声明在后使用在前。新手对于此则很迷惑,甚至许多使用JS多年老手也比较迷惑。但在 ES6 加入 let/const 后,变量Hoisting 就不存在了。

一、 变量未声明,直接使用

function test() {
  alert(notDefined);
}
test(); // ?

报错是自然的

JavaScript中标识符提升问题

二. 变量声明在末尾

function test() {
  alert(declaredButNotAssigned); // undefined
  var declaredButNotAssigned;
}
test();

输出 undefined, 结果比上例有所改善,没有报错,代码可以运行,但变量值可能不是程序员所期望的。

三、 变量声明在末尾,同时给变量赋值

function test() {
  alert(declaredAndAssigned); // undefined
  var declaredAndAssigned = 1;
}
test();

结果和 二 相同, 很明显,并不会因为赋值了就输出 1。

二、三 都发生了变量提升(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 里函数声明也会存在提升,只是这个 “变量” 比较特殊,它是一个 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 后变量提升就不复存在了。

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 相关文章推荐
Jquery+JSon 无刷新分页实现代码
Apr 01 Javascript
js function使用心得
May 10 Javascript
JQuery 学习技巧总结
May 21 Javascript
新浪微博字数统计 textarea字数统计实现代码
Aug 28 Javascript
jQuery动态添加删除select项(实现代码)
Sep 03 Javascript
jQuery基于扩展实现的倒计时效果
May 14 Javascript
JavaScript遍历Json串浏览器输出的结果不统一问题
Nov 03 Javascript
基于jquery二维码生成插件qrcode
Jan 07 Javascript
bootstrap下拉菜单使用方法解析
Jan 13 Javascript
基于layui数据表格以及传数据的方式
Aug 19 Javascript
微信小程序如何使用globalData的方法
Jun 06 Javascript
Vue用mixin合并重复代码的实现
Nov 27 Vue.js
JQuery删除DOM节点的方法
Jun 11 #Javascript
JQuery插入DOM节点的方法
Jun 11 #Javascript
JQuery创建DOM节点的方法
Jun 11 #Javascript
JQuery查找DOM节点的方法
Jun 11 #Javascript
JQuery鼠标移到小图显示大图效果的方法
Jun 10 #Javascript
JQuery实现超链接鼠标提示效果的方法
Jun 10 #Javascript
jquery序列化方法实例分析
Jun 10 #Javascript
You might like
php生成随机密码的几种方法
2011/01/17 PHP
PHP中__get()和__set()的用法实例详解
2013/06/04 PHP
解析在PHP中使用mysqli扩展库对mysql的操作
2013/07/03 PHP
php中用date函数获取当前时间有误的解决办法
2013/08/02 PHP
phpQuery占用内存过多的处理方法
2013/11/13 PHP
PHP中类与对象功能、用法实例解读
2020/03/27 PHP
jquery URL参数判断,确定菜单样式
2010/05/31 Javascript
js中将具有数字属性名的对象转换为数组
2011/03/06 Javascript
Javascript表格翻页效果实现思路及代码
2013/08/23 Javascript
JavaScript利用正则表达式去除日期中的-
2014/06/09 Javascript
jQuery 限制输入字符串长度
2016/06/20 Javascript
Javascript实现汉字和拼音互转的终极方案
2016/10/19 Javascript
jQuery内存泄露解决办法
2016/12/13 Javascript
ionic实现下拉刷新载入数据功能
2017/05/11 Javascript
vue 项目打包通过命令修改 vue-router 模式 修改 API 接口前缀
2018/06/13 Javascript
uni app仿微信顶部导航条功能
2019/09/17 Javascript
vue 动态添加的路由页面刷新时失效的原因及解决方案
2021/02/26 Vue.js
[54:24]Optic vs TNC 2018国际邀请赛小组赛BO2 第二场
2018/08/18 DOTA
[01:25:38]DOTA2-DPC中国联赛 正赛 VG vs LBZS BO3 第一场 1月19日
2021/03/11 DOTA
Python八大常见排序算法定义、实现及时间消耗效率分析
2018/04/27 Python
python实现WebSocket服务端过程解析
2019/10/18 Python
python 使用shutil复制图片的例子
2019/12/13 Python
详解python程序中的多任务
2020/09/16 Python
CSS3之transition实现下划线的示例代码
2018/05/30 HTML / CSS
HTML5 的新的表单元素(datalist/keygen/output)使用介绍
2013/07/19 HTML / CSS
配置H5的滚动条样式的示例代码
2018/03/09 HTML / CSS
全球酒店比价网:HotelsCombined
2017/06/20 全球购物
ALDO加拿大官网:加拿大女鞋品牌
2018/12/22 全球购物
aden + anais英国官网:美国婴儿贴身用品品牌
2019/09/08 全球购物
LINUX下线程,GDI类的解释
2016/12/14 面试题
文员个人求职自荐信
2013/09/21 职场文书
交警正风肃纪剖析材料
2014/10/29 职场文书
自主招生推荐信怎么写
2015/03/26 职场文书
2015年环卫处个人工作总结
2015/07/27 职场文书
2016年艾滋病宣传活动总结
2016/04/01 职场文书
二维码条形码生成的JavaScript脚本库
2022/07/07 Javascript