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 相关文章推荐
6款经典实用的jQuery小插件及源码(对话框/提示工具等等)
Feb 04 Javascript
解析Javascript中难以理解的11个问题
Dec 09 Javascript
jQuery中:only-child选择器用法实例
Jan 03 Javascript
js实现对table动态添加、删除和更新的方法
Feb 10 Javascript
JavaScript中的slice()方法使用详解
Jun 06 Javascript
js正则表达式中exec用法实例
Jul 23 Javascript
Jquery代码实现图片轮播效果(一)
Aug 12 Javascript
引用jquery框架后出错的解决方法
Aug 09 Javascript
浅谈Vue的基本应用
Dec 27 Javascript
Node.JS中事件轮询(Event Loop)的解析
Feb 25 Javascript
JavaScript实现微信号随机切换代码
Mar 09 Javascript
vue前端框架—Mint UI详解(更适用于移动端)
Apr 30 Javascript
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
使用sockets:从新闻组中获取文章(一)
2006/10/09 PHP
几道坑人的PHP面试题 试试看看你会不会也中招
2014/08/19 PHP
PHP读取汉字的点阵数据
2015/06/22 PHP
yii2.0使用Plupload实现带缩放功能的多图上传
2015/12/22 PHP
Laravel5.7 Eloquent ORM快速入门详解
2019/04/12 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
基于jquery的一个OutlookBar类,动态创建导航条
2010/11/19 Javascript
jquery中的 $(&quot;#jb51&quot;)与document.getElementById(&quot;jb51&quot;) 的区别
2011/07/26 Javascript
一张表格告诉你windows.onload()与$(document).ready()的区别
2014/05/16 Javascript
JavaScript中的原型prototype完全解析
2016/05/10 Javascript
bootstrap模态框跳转到当前模板页面 框消失了而背景存在问题的解决方法
2020/11/30 Javascript
javascript实现下雨效果
2017/03/27 Javascript
微信小程序实现倒计时60s获取验证码
2020/04/17 Javascript
Angular实现的简单定时器功能示例
2017/12/28 Javascript
微信、QQ、微博、Safari中使用js唤起App
2018/01/24 Javascript
vue多页面开发和打包正确处理方法
2018/04/20 Javascript
微信小程序实现可长按移动控件
2020/11/01 Javascript
python Opencv将图片转为字符画
2021/02/19 Python
简单了解python代码优化小技巧
2019/07/08 Python
详解Python 中sys.stdin.readline()的用法
2019/09/12 Python
tensorflow自定义激活函数实例
2020/02/04 Python
Tensorflow使用Anaconda、pycharm安装记录
2020/07/29 Python
python opencv角点检测连线功能的实现代码
2020/11/24 Python
草莓网化妆品加拿大网站:Strawberrynet Canada
2016/09/20 全球购物
size?瑞典:英国伦敦的球鞋精品店
2018/03/01 全球购物
德国户外商店:eXXpozed
2020/07/25 全球购物
法学专业自我鉴定
2014/02/05 职场文书
会计电算化毕业生自荐信
2014/03/03 职场文书
市级文明单位申报材料
2014/05/07 职场文书
人力资源求职信
2014/05/25 职场文书
授权委托书(公民个人适用)
2014/09/19 职场文书
企业与个人合作经营协议书
2014/11/01 职场文书
2014流动人口计划生育工作总结
2014/12/20 职场文书
上课迟到检讨书范文
2015/05/06 职场文书
个人更名证明
2015/06/23 职场文书
解析MySQL binlog
2021/06/11 MySQL