JavaScript中标识符提升问题

JS 存在变量提升,这个的设计其实是低劣的,或者是语言实现时的一个副作用。它允许变量不声明就可以访问,或声明在后使用在前。新手对于此则很迷惑,甚至许多使用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),简单定义

变量提升: 在指定作用域里,从代码顺序上看是变量先使用后声明,但运行时变量的 “可访问性” 提升到当前作用域的顶部,其值为 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 声明的就会报错了。

以上所述就是本文的全部内容了,希望大家能够喜欢。

(0)

相关推荐

  • JavaScript实现生成GUID(全局统一标识符)

    GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可能的数字.GUID的唯一缺陷在于生成的结果串会比较大. GUID的格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 大家都知道GUID在前端开发中用处不大,但如果需要插入某个ID,并且这个ID与后台对应等其它需要GUID的操作时,为了方便,我们还是可以生成一个GUID

  • IE6/IE7中JavaScript json提示缺少标识符、字符串或数字问题处理

    昨天在使用Jquery Ui Datapicker做显示签到日历功能的时候,出现了Js问题,在IE8/IE9以及FF.Chrome下都显示正常, 但是在IE6/IE7以及IE8兼容视图下显示有问题,提示"页面上有错误",进一步查看显示错误信息"缺少标识符.字符串或数字", 一开始以为是Jquery 和Jquery Ui的版本不匹配导致的,后来尝试了其他支持的版本也是不行, 然后将自己的Js代码逐段调式,在花了两个多小时后终于以外的发现是由Json最后一个键值后加了逗

  • PHP生成不重复标识符的方法

    本文实例讲述了PHP生成不重复标识符的方法.分享给大家供大家参考.具体实现方法如下: 生成唯一不重复的标识我们主要是根据当前的一个时间time然后再转换在md5值,这样几乎是可以保证标签的唯一性,下面整理了一些关于PHP生成不重复标识符程序代码,感兴趣的朋友可以来看一下 PHP倒是自带了生成唯一id的函数:uniqid() ,它是基于当前时间微秒数的,用法如下: 复制代码 代码如下: echo uniqid(); //13位的字符串 echo uniqid("php_"); //当然你

  • JS中产生标识符方式的演变

    一.ES5时代 var function 我们知道 JS 不象其它语言 Java.Ruby等,它用来命名变量的只有关键字 var,不论何种类型数据都用 var 声明,当然弱类型并不代表该语言没有类型,它的类型在运行时(根据不同运算符)会隐式转换.而其它语言如Java,光声明数字的关键字就有 int. float.double.long. // JS var num1 = 10; // 整数 var num2 = 10.1; // 浮点数 var str = 'John'; // 字符串 var

  • C#中使用@声明变量示例(逐字标识符)

    在C#中,@符号不仅可以加在字符串常量之前,使字符串不作转义之用,还可以加在变量名之前,使变量名与关键字不冲突,这种用法称为"逐字标识符".请看下面的代码: class @class { public static void @static(bool @bool) { if (@bool) System.Console.WriteLine("true"); else System.Console.WriteLine("false"); } } 这

  • C#标识符的使用小结

    标识符(Identifier)是适用于变量.类.方法和其他各种用户定义对象的一般术语.  在编写代码时遵循命名规则,可以让程序更加易懂.易读:而且还能提供它的功能信息,如它是否是一个常量.包名或类等,这都有助于对程序的理解. 标识符是由一系列字符组成,其中包括大小写字母.数字.下划线(_)和@字符.标识符不能以数字开头,也不能包含空格.合法的标识符的例子如Welcome1, _value, m_inputField1和button7.7button这个名字不是一个合法的标识符,因为它以数字开头.

  • mysql把主键定义为自动增长标识符类型

    1.把主键定义为自动增长标识符类型 在mysql中,如果把表的主键设为auto_increment类型,数据库就会自动为主键赋值.例如: create table customers(id int auto_increment primary key notnull, name varchar(15)); insert into customers(name) values("name1"),("name2"); 一旦把id设为auto_increment类型,my

  • VBS中的标识符(Identifiers)

    VBS其实存在两种标识符: 普通标识符(NORMALIDENTIFIER) 中括号标识符(BRACKETIDENTIFIER) 但是我看过的VBS书籍上都只讲了普通标识符,没有讲中括号标识符. 普通标识符的规则我们已经很熟悉了: 第一个字符必须是字母. 后面的字符可以是字母.数字和下划线(_) 长度不能超过 255 个字符 不能是VBS保留的关键字 中括号标识符的规则更简单: 中括号括起来的任意Unicode字符(换行.回车和NULL除外) 长度不能超过 255 个字符(不包括中括号) 有了中括

  • JS在IE下缺少标识符的错误

    根据报错的行数查找源码,也看不出哪里有问题,看样子没缺少什么啊,代码如下: if (opts.display.class != '') { $(list).addClass(opts.display.css); } 此句其实是判断 opts 对象的 display 的 class 属性有没值,否则就做相应的处理,语法上来说是没错的,后来上网搜索一番,才发现原来 IE 对JS语法的要求比较苛刻,因为其中的 class 是 js 的关键字,如用此来作变量的话,IE就会报以上错误了,唉! 由于这是 j

  • 浅析JavaScript中的同名标识符优先级

    一,局部变量先使用后声明,不影响外部同名变量 复制代码 代码如下: var x = 1; // --> 外部变量x function fn(){     alert(x);  // --> undefined 局部变量x先使用     var x = 2; // 后声明且赋值 } fn(); alert(x); // --> 1<BR> 第一点,函数fn内第一句输出x,x是在第二句才定义的.这在JS中是允许的,这里的允许是指不会出现语法错误程序可以运行. 但在其它语言如C,J

随机推荐