您当前的位置:首页 > 微信小程序开发笔记 >

JavaScript 变量声明与赋值

来源:JavaScript权威指南第7版0

在JavaScript中使用变量或常量前,必须先声明它。在ES6及之后的版本中,这是通过 let 和 const 关键字来完成的,接下来我们会介绍。在ES6之前,变量是通过 var 声明的,这个关键字更特殊一些,将在后面介绍。

使用let和const声明

在现代JavaScript(ES6及之后)中,变量是通过 let 关键字声明的,声明变量的同时(如果可能)也为其赋予一个初始值是个好的编程习惯:

let message="hello";
let i=0,j=0,k=0; //也可以使用一条 let 语句声明多个变量
let x=2,y=x*x;   //初始化语句可以使用前面声明的变量
let n;
console.log(n);

如果在 let 语句中不为变量指定初始值,变量也会被声明,但在被赋值之前它的值是 undefined。

要声明常量而非变量,则要使用const而非let。const与let类似,区别在于const必须在声明时初始化常量:

const H0=74;        //哈勃常数(km/s/Mpc)
const C=299792.458; //真空中的光速(km/s)
const AU=1.496E8;   //天文单位:地球与太阳间的平均距离(km)

顾名思义,常量的值是不能改变的,尝试给常量重新赋值会抛出TypeError。声明常量的一个常见(但并非普遍性)的约定是全部字母大写,如H0或HTTP_NOT_FOUND,以区别于变量。

何时使用const

关于使用const关键字有两种论调。一种论调是只在值基本不会改变的情况下使用const,比如物理常数、程序版本号,或用于标识文件类型的字节序。另一种论调认为程序中很多所谓的变量实际上在程序运行时并不会改变。为此,应该全部使用const声明,然后如果发现确实需要允许值改变,再改成let。这样有助于避免因为意外修改变量而导致出现bug。在第一种情况下,我们只对那些必须不变的值使用const。另一种情况下,对任何不会变化的值使用const。我本人在写代码时倾向前一种思路。

在第5章中,我们会学习JavaScript中的for、for/in和for/of循环语句。其中每种循环都包含一个循环变量,在循环的每次迭代中都会取得一个新值。JavaScript允许在循环语法中声明这个循环变量,这也是let另一个常见的使用场景:

for(let i=0, len=data.length; i < len; i++) console.log(data[i]);
for(let datum of data) console.log(datum);
for(let property in object) console.log(property);

虽然看起来有点怪,但也可以使用const声明for/in和for/of中的这些循环“变量”,只要保证在循环体内不给它重新赋值即可。此时,const声明的只是一次循环迭代期间的常量值:

for(const datum of data) console.log(datum);
for(const property in object) console.log(property);

变量与常量作用域

变量的作用域(scope)是程序源代码中一个区域,在这个区域内变量有定义。通过let和const声明的变量和常量具有块作用域。这意味着它们只在let和const语句所在的代码块中有定义。JavaScript类和函数的函数体是代码块,if/else语句的语句体、while和for循环的循环体都是代码块。粗略地讲,如果变量或常量声明在一对花括号中,那这对花括号就限定了该变量或常量有定义的代码区域(当然,在声明变量或常量的let或const语句之前的代码行中引用这些变量或常量也是不合法的)。作为for、for/in或for/of循环的一部分声明的变量和常量,以循环体作为它们的作用域,即使它们实际上位于花括号外部。

如果声明位于顶级,在任何代码块外部,则称其为全局变量或常量,具有全局作用域。在Node和客户端JavaScript模块中,全局变量的作用域是定义它们的文件。但在传统客户端JavaScript中,全局变量的作用域是定义它们的HTML文档。换句话说,如果有<script>标签声明了一个全局变量或常量,则该变量或常量在同一个文档的任何<script>元素中(或者至少在let和const语句执行之后执行的所有脚本中)都有定义。

重复声明

在同一个作用域中使用多个let或const声明同一个名字是语法错误。在嵌套作用域中声明同名变量是合法的(尽管实践中最好不要这么做):

const x=1;     //声明x为全局常量
if(x===1){
  let x=2;     //在同一个代码块中
  console.log(x); //打印2
}
console.log(x);  //打印1,现在又回到了全局作用域
let x=3;      //错误!重新声明x会导致语法错误

声明与类型

如果你使用过静态类型语言(如C或Java),可能认为变量声明的主要目的是为变量指定可以赋予它的值的类型。但我们也看到了,JavaScript的变量声明与值的类型无关。JavaScript变量可以保存任何类型的值。例如,在JavaScript中,给一个变量赋一个数值,然后再给它赋一个字符串是合法的。

使用var的变量声明

在ES6之前的JavaScript中,声明变量的唯一方式是使用var关键字,无法声明常量。var的语法与let的语法相同:

var x;
var data=[], count=data.length;
for(var i=0; 1 < count; i++) console.log(data[i]);

虽然var和let有相同的语法,但它们也有重要的区别。

  • 使用var声明的变量不具有块作用域。这种变量的作用域仅限于包含函数的函数体,无论它们在函数中嵌套的层次有多深。
  • 如果在函数体外部使用var,则会声明一个全局变量。但通过var声明的全局变量与通过let声明的全局变量有一个重要区别。通过var声明的全局变量被实现为全局对象的属性。全局对象可以通过 globalThis 引用。因此,如果你在函数外部写了 var x=2;,就相当于写了 globalThis.x=2;。不过要注意,这么类比并不完全恰当。因为通过全局var创建的这个属性不能使用delete操作 符删除。通过let和const声明的全局变量和常量不是全局对象的属性。
  • 与通过let声明的变量不同,使用var多次声明同名变量是合法的。而且由于var变量具有函数作用域而不是块作用域,这种重新声明实际上是很常见的。变量i经常用于保存整数值,特别是经常用作for循环的索引变量。在有多个for循环的函数中,每个循环通常都以 for(var i=0; ...开头。因为var并不会把这些变量的作用域限定在循环体内,每次循环都会(无害地)重新声明和重新初始化同一个变量。
  • var声明的一个最不同寻常的特性是作用域提升(hoisting)。在使用var声明变量时,该声明会被提高(或提升)到包含函数(指包含内部函数的函数)的顶部。但变量的初始化仍然在代码所在位置完成,只有变量的定义转移到了函数顶部。因此对使用var声明的变量,可以在包含函数内部的任何地方使用而不会报错。如果初始化代码尚未运行,则变量的值可能是undefined,但在初始化之前是可以使用变量而不报错的(这会成为一个bug来源,也是let要纠正的一个最重要的错误特性。如果使用let声明了一个变量,但试图在let语句运行前使用该变量则会导致错误,而不是得到undefined值)。

使用未声明的变量

在严格模式下,如果试图使用未声明的变量,那代码运行时会触发引用错误。但在严格模式外部,如果将一个值赋给尚未使用let、const或var声明的名字,则会创建一个新全局变量。而且,无论这个赋值语句在函数或代码块中被嵌套了多少次,都会创建一个全局变量。这肯定不是我们想要的,非常容易招致缺陷,也是推荐使用严格模式一个最好的理由。

以这种意外方式创建的全局变量类似使用var声明的全局变量,都定义全局对象的属性。但与通过恰当的var声明定义的属性不同,这些属性可以通过delete操作删除。

建站咨询

在线咨询真诚为您提供专业解答服务

咨询热线

137 1731 25507×24小时服务热线

微信交流

二维码终于等到你,还好我没放弃
返回顶部