购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.5 ECMAScript 6新特新
——let、const关键字

本节介绍关于ECMAScript 6语法的新特性,主要就是let和const关键字的使用方法,还包括var关键字与let和const关键字的区别。同时,在介绍ECMAScript 6语法的新特性之前,还会介绍一些关于ECMAScript语法的特殊知识,作为介绍ECMAScript 6语法新特性的铺垫。

2.5.1 变量作用域

前文中,我们介绍不通过“var”关键字来声明变量的方法。其实,使用或不使用“var”关键字还有一个很常见的问题,那就是变量的作用域。

下面来看一个关于ECMAScript变量作用域的代码示例(详见源代码ch02目录中ch02-js-variable-scope.html文件)。

【代码2-36】

   01  <script type="text/javascript">
   02         var a = 1;
   03         console.log("a = " + a);
   04         function func_a() {
   05             a = 2;
   06         }
   07         func_a();
   08         console.log("a = " + a);
   09         var b = 1;
   10         console.log("b = " + b);
   11         function func_b() {
   12             var b = 2;
   13         }
   14         func_b();
   15         console.log("b = " + b);
   16  </script>

关于【代码2-36】的分析如下:

第04~06行代码定义一个函数方法(func_a())。其中,第05行代码重新为变量(a)进行赋值操作(a=2);

第07行代码直接调用第04~06行代码定义的函数方法(func_a());

第09~15行代码与第02~08行代码类似,定义变量(b),几乎再次复制第02~08行代码的内容。唯一不同的地方就是第12行代码,细心的读者会发现其与第05行代码的不同,使用“var”关键字重新定义了变量(b)。

页面效果如图2.36所示。

图2.36 ECMAScript变量作用域

从浏览器控制台中输出的内容来看,【代码2-36】中第05行代码没有使用“var”和第12行代码使用“var”关键字定义变量还是有区别的:变量(a)的内容在经过第05行代码的操作后,第08行代码输出重新赋值后的数值;而变量(b)的内容在经过第12行代码的操作后,第15行代码输出的数值没有发生任何改变。这是为什么呢?

因为在第12行代码中,我们使用“var”关键字定义了变量(b),此时的变量(b)仅仅是局部变量(只在函数方法(func_a())中有效)。换句话说,第12行代码定义的变量(b)与第09行代码定义的变量(b)是完全不相关的两个变量,因此第12行代码中对变量(b)的赋值操作(b=2)是根本不会影响到第15行代码的输出结果。

以上就是对变量作用域的简单介绍,接下来介绍ECMAScript语法规范中关于变量提升的知识。

2.5.2 变量提升

在JavaScript(ECMAScript)语法中,变量的提升是一种很常见的现象。那么具体什么是JavaScript(ECMAScript)变量的提升呢?

下面来看一个关于JavaScript(ECMAScript)变量提升的代码示例(详见源代码ch02目录中ch02-js-variable-enhance.html文件)。

【代码2-37】

   01  <script type="text/javascript">
   02         console.log("a = " + a);
   03         var a = 1;
   04         console.log("a = " + a);
   05         function func_b() {
   06             console.log("b = " + b);
   07             var b = 1;
   08             console.log("b = " + b);
   09         }
   10         func_b();
   11  </script>

关于【代码2-37】的分析如下:

第02行和第04行代码分别在定义变量(a)之前和之后,尝试在浏览器控制台窗口中输出变量(a)的内容;

第05~09行代码定义一个函数方法(func_b()),其中第06行和第08行代码分别在定义变量(b)之前和之后,尝试在浏览器控制台窗口中输出变量(b)的内容;

第10行代码调用第05~09行代码定义的函数方法(func_b())。

页面效果如图2.37所示。从浏览器控制台中输出的内容来看,虽然【代码2-37】中定义的第02行代码和第06行代码看似会报错,但实际却输出变量未定义(undefined)的内容。这是为什么呢?

图2.37 ECMAScript变量提升

这是因为JavaScript(ECMAScript)变量提升的特性而产生的结果,在JavaScript(ECMAScript)脚本代码编译过程中,会将全部变量提升到该变量作用域的最顶部,返回到【代码2-37】中,根据变量提升的特点,在执行第02行代码和第06行代码时,变量(a和b)已经存在(只不过未初始化),因此会有输出值(undefined)。

2.5.3 块级作用域

在JavaScript(ECMAScript)语法中,是没有“块级作用域”这个概念的。因此,JavaScript(ECMAScript)全局变量的有效作用域就是整个页面,而局部变量的有效作用域就是其所定义位置的函数内。那么该如何理解呢?

下面来看一个关于JavaScript(ECMAScript)变量“块级作用域”的代码示例(详见源代码ch02目录中ch02-js-variable-block.html文件)。

【代码2-38】

   01  <script type="text/javascript">
   02         function func_block() {
   03             var i;
   04             for (i = 0; i < 3; i++) {
   05                 console.log("i = " + i);
   06                 var j = i;
   07             }
   08             console.log("j = " + j);
   09             if (i == 3) {
   10                 var k = i;
   11             }
   12             console.log("k = i = " + k);
   13         }
   14         func_block();
   15  </script>

关于【代码2-38】的分析如下:

第02~13行代码定义一个函数方法(func_block());

第04~07行代码定义一个for循环语句,其自变量就是变量(i)。比较特殊的是第06行代码,通过“var”关键字定义变量(j),并赋值为变量(i)的值;

第09~11行代码通过if条件选择语句判断变量(i)是否等于数值3;

第10行代码通过“var”关键字定义第三个变量(k),并赋值为变量(i)的值;

第14行代码调用了第02~13行代码定义的函数方法(func_block())。

页面效果如图2.38所示。从浏览器控制台中输出的内容来看,虽然【代码2-38】中第06行代码通过“var”关键字定义的第二个变量(j)是在for循环语句内,但第08行代码仍然成功地获取并输出了变量(j)的值。这是为什么呢?

图2.38 ECMAScript块级作用域

这就是因为JavaScript(ECMAScript)语法规范中没有定义“块级作用域”而产生的结果,变量(j)虽然是定义在for循环语句内的,但其有效作用域都是在整个函数方法(func_block())内的。

同样的,第12行代码能够成功获取并输出第10行代码定义的变量(k)的值,也就不难理解了。

2.5.4 通过let关键字实现块级作用域

为了解决前文中介绍的JavaScript(ECMAScript)语法中没有“块级作用域”这个问题,ECMAScript 6语法规范中增加了一个“let”关键字来实现“块级作用域”的功能。

下面来看一个关于let关键字的代码示例(详见源代码ch02目录中ch02-js-es6-let.html文件),该代码是在【代码2-38】的基础上修改而完成的。

【代码2-39】

   01  <script type="text/javascript">
   02         function func_let() {
   03             var i;
   04             for (i = 0; i < 3; i++) {
   05                 console.log("i = " + i);
   06                 let j = i;
   07                 console.log("j = " + j);
   08             }
   09             console.log("j = " + j);
   10         }
   11         func_let();
   12  </script>

关于【代码2-39】的分析如下:

第04~08行代码定义一个for循环语句,其自变量就是变量(i)。比较特殊的是第06行代码,通过“let”关键字定义了第二个变量(j),并赋值为变量(i)的值;

第09行代码在for循环语句之后,再次在浏览器控制台窗口中输出变量(j)的值。

页面效果如图2.39所示。从浏览器控制台中输出的内容来看,【代码2-39】中第09行代码的变量(j)是未定义的,这就与【代码2-38】中第08行代码执行结果完全不同了。而从第07行代码输出的内容来看,在for循环语句内的变量(j)均获取了具体的,这就充分地说明第06行代码中,通过“let”关键字定义的变量(j)的有效作用域仅存在于第04~08行代码定义的“块级作用域(for循环语句)”内。

图2.39 通过let运算符实现块级作用域

2.5.5 let关键字使用规则

既然“let”关键字是ECMAScript 6语法规范中新增的了一个特性,那么其在使用规则上自然会与“var”关键字有所区别,在使用“let”关键字时要避免出现以下两种错误情形:

下面,先看一个关于let关键字使用规则的代码示例(详见源代码ch02目录中ch02-js-es6-let-rules-a.html文件)。

【代码2-40】

   01  <script type="text/javascript">
   02         function func_let_rules() {
   03             console.log("a = " + a);
   04             let a = 1;
   05             console.log("a = " + a);
   06         }
   07         func_let_rules();
   08  </script>

关于【代码2-40】的分析如下:

第04行代码通过“let”关键字定义第一个变量(a),并进行初始化赋值(a=1);

第03行和第05行代码分别尝试直接在浏览器控制台窗口中输出变量(a)的值。其中,第03行代码是在变量(a)声明定义之前,第05行代码是在变量(a)声明定义之后。

页面效果如图2.40所示。从浏览器控制台中输出的内容来看,【代码2-40】中第03行代码直接报错,在使用“let”关键字声明变量初始化之前是无法调用该变量的。

图2.40 let关键字使用规则(1)

下面,接着再看一个关于let关键字使用规则的代码示例(详见源代码ch02目录中ch02-js-es6-let-rules-b.html文件)。

【代码2-41】

   01  <script type="text/javascript">
   02         function func_let_rules() {
   03             let a = 1;
   04             console.log("a = " + a);
   05             let a = 2;
   06             console.log("a = " + a);
   07         }
   08         func_let_rules();
   09  </script>

页面效果如图2.41所示。从浏览器控制台中输出的内容来看,【代码2-41】中第03行和第05行代码直接报错,使用“let”关键字是无法重新声明变量的。

图2.41 let关键字使用规则(2)

2.5.6 let关键字应用

前面铺垫这么多内容,接下来该是重点要介绍的内容。读者可能会有疑问,既然“let”关键字的功能也可以通过“var”关键字来实现,那么ECMAScript 6语法规范中新增“let”关键字的作用是什么呢?文字阐述往往没有实际代码表达得透彻,我们还是先看一个具体的代码示例。

下面是一个为了更好地介绍let关键字应用所进行铺垫的代码示例(详见源代码ch02目录中ch02-js-es6-let-usage-a.html文件)。

【代码2-42】

   01  <script type="text/javascript">
   02         var arrJS = ["JavaScript", "EcmaScript", "jQuery"];
   03         for (var i = 0; i < 3; i++) {
   04             console.log("arrJS[" + i + "] = " + arrJS[i]);
   05         }
   06  </script>

关于【代码2-42】的分析如下:

这段代码很简单,先定义一个字符串数组,然后通过for循环语句依次在浏览器控制台中输出每个数组项的内容。

页面效果如图2.42所示。浏览器控制台中依次输出了每个数组项的内容。【代码2-42】很简单,我们先铺垫该代码的目的是为了介绍下面的代码示例。

图2.42 let关键字应用(1)

下面接着看一个在【代码2-42】的基础上稍作改动的代码示例(详见源代码ch02目录中ch02-js-es6-let-usage-b.html文件)。

【代码2-43】

   01  <script type="text/javascript">
   02         var arrJS = ["JavaScript", "EcmaScript", "jQuery"];
   03         for (var i = 0; i < 3; i++) {
   04             setTimeout(function () {
   05                 console.log("arrJS[" + i + "] = " + arrJS[i]);
   06             }, 500);
   07         }
   08  </script>

页面效果如图2.43所示。从浏览器控制台中输出的内容来看,第05行代码连续输出重复三次的数组项内容(undefined)。这个结果与图2.42的内容完全不同,这是什么原因呢?

图2.43 let关键字应用(2)

其实,主要原因还是变量的作用域造成的。第03行代码定义的for循环语句的自变量(i)是通过“var”关键字声明的,因此该自变量(i)的作用域是整个脚本代码空间。由于setTimeout()方法会设定延时,因此在for循环语句执行完毕后,第05行代码定义的在浏览器控制台中输出内容仍未执行,而此时自变量(i)的值已经变为3了。所以,最后等到第05行代码执行时获取已经是数组项arrJS[3](未定义,undefined)的内容。

【代码2-43】的问题主要就是JavaScript(ECMAScript)语法规范中没有“块级作用域”的概念造成的。那么如何解决呢?这时就该是前文中介绍的“let”关键字发挥作用的时刻了。

在【代码2-43】的基础上稍作改动,代码示例如下(详见源代码ch02目录中ch02-js-es6-let-usage-c.html文件)。

【代码2-44】

   01  <script type="text/javascript">
   02         var arrJS = ["JavaScript", "EcmaScript", "jQuery"];
   03         for (let i = 0; i < 3; i++) {
   04             setTimeout(function () {
   05                 console.log("arrJS[" + i + "] = " + arrJS[i]);
   06             }, 500);
   07         }
   08  </script>

页面效果如图2.44所示。从浏览器控制台中输出的内容来看,其与图2.42中的内容完全相同,说明“let”关键字成功将自变量(i)的作用域限定在每一次for循环语句块内,因此也就能获取正常的数组项的内容。

图2.44 let关键字应用(3)

2.5.7 通过const关键字定义常量

在类似C和Java的这类高级语言中常量很常用,ECMAScript 6语法规范中也增加一个“const”关键字来实现常量定义的功能。

ECMAScript 6语法规范中的常量也适用于“块级作用域”,有点像使用“let”关键字定义的变量。与其他高级语言类似,ECMAScript的常量值同样不能通过重新赋值来改变,并且也不能重新进行声明。

常量声明的同时就要进行初始化,而且创建的值仅是一个只读引用,因此无法进行更改。但也有例外,如果创建的常量是一个引用的对象,就可以改变对象的内容(如对象的参数值)。

下面,先看一个关于const关键字的代码示例(详见源代码ch02目录中ch02-js-es6-const.html文件)。

【代码2-45】

   01  <script type="text/javascript">
   02         /**
   03          * const声明时必须初始化
   04          */
   05         const myPI;
   06  </script>

关于【代码2-45】的分析如下:

第05行代码尝试通过“const”关键字声明一个常量(myPI),但并没有进行初始化操作。

页面效果如图2.45所示。从浏览器控制台中输出的内容来看,JS调试器直接提示需要进行常量的初始化操作。

图2.45 const关键字(1)

下面,继续看一个关于const关键字的代码示例(详见源代码ch02目录中ch02-js-es6-const.html文件)。

【代码2-46】

   01  <script type="text/javascript">
   02         /**
   03          * const常量不能再进行赋值操作
   04          * @type {number}
   05          */
   06         const myPI = 3.1415926;
   07         myPI = 3.14;
   08  </script>

页面效果如图2.46所示。从浏览器控制台中输出的内容来看,JS调试器直接对常量再次赋值的初始化操作进行了报错。

图2.46 const关键字(2)

在【代码2-46】的基础上进行修改,完成一个关于const关键字的代码示例(详见源代码ch02目录中ch02-js-es6-const.html文件)。

【代码2-47】

   01  <script type="text/javascript">
   02         /**
   03          * const常量不能重复声明
   04          * @type {number}
   05          */
   06         const myPI = 3.1415926;
   07         const myPI = 3.1415926;
   08  </script>

页面效果如图2.47所示。从浏览器控制台中输出的内容来看,JS调试器直接对常量重复声明的操作进行了报错。

图2.47 const关键字(3)

另外,对于已经通过“const”关键字声明的常量,再次使用“var”或“let”关键字声明也是不被允许的。

前面的几个代码示例说明常量已经声明定义就无法再更改了,这点是毋庸置疑的。不过也有一种例外,那就是对象常量的参数值是可以更改的。不过需要注意,对象常量本身是不可更改的,仅仅是常量参数可以更改。

下面来看一个关于const关键字定义对象常量的代码示例(详见源代码ch02目录中ch02-js-es6-const.html文件)。

【代码2-48】

   01  <script type="text/javascript">
   02         /**
   03          * const声明对象常量
   04          * @type {{key: string}}
   05          */
   06         const myObj = {"key": "JavaScript"};
   07         console.log("key : " + myObj.key);
   08         myObj.key = "EcmaScript";
   09         console.log("key : " + myObj.key);
   10  </script>

页面效果如图2.48所示。从浏览器控制台中输出的内容来看,第08行代码成功修改了常量对象(myObj)中“key”的参数值。

图2.48 const关键字(4) p+RchxLQITm13YeHp+1hN9YdQ0YXJAWna++TH1fa76oGjbCYWcgiCbOJ0S9+4QVU

点击中间区域
呼出菜单
上一章
目录
下一章
×