JavaScript学习系列5 ---ES6中的var, let 和const
我们都知道JavaScript中的var,在本系列的
JavaScript学习系列2一JavaScript中的变量作用域 中,我们详细阐述了var声明的变量的作用域
文章中提到,JavaScript中没有块级作用域(由{}限定的作用域), 也就是说JS中变量的作用域只有两种,一种是全局作用域,另一种就是函数作用域
但是,其实在ES6中,新增了let命令,改变了这种状况,它为JavaScript增加了块级作用域. ES6中还有一个const命令。 我们现在来看看它们
一个简单的例子
{
let a = 1;
var b = 1;
} a // ReferenceError: a is not defined
b // 1
上面这个例子中,在代码块中,用let声明了一个变量a, 用var声明了一个变量b. 然后我们尝试在代码块之外去访问,发现可以访问到b,但是却没法访问到用let声明的变量a.
上面这个例子很好的说明,ES6中的let设立了一个JavaScript中的块级作用域,上面的let声明的变量a,只在{}里面的块级作用域中有效,出了这个{}块级作用域,它就不存在了,所以才会报错 ReferenceError
在JavaScript中,有一个地方非常适合使用let,哪里呢. 就是for循环中的计数器 (记住这一点 JavaScript中for循环的计算器,特别适合使用使用let声明), 我们来看一个例子
for(let i = 0; i < 100; i++)
{
//....
}
因为for循环中的计数器只需要在for循环中使用,所以它的作用域就是for循环这一个语句块,所以特别适合使用let来声明。 除了作用域不同之外,var和let还有哪些区别呢,我们来看看
1. 不存在var 那样的 声明提升 (hoisting)
在JavaScript学习系列2一JavaScript中的变量作用域中,我们提到了JavaScript中用var来声明变量时,存在的声明提升(hoisting), 如下
alert(name); // 输出 undefined
var name = "Luke"; //实际执行过程 (变量提升) var name;
alert(name);
name = "Luke";
那么ES6中的块级作用域声明let是否也存在变量提升呢, 答案是否定的,我们来看看
let const 不存在变量提升
// var 声明的变量 console.log(test); // 输出 undefined
var test = 2 // let 声明的变量 console.log(test) // 报错 ReferenceError
let test = 2;
我们可以看到,用let来声明变量时,没有发生变量提升. 当执行console.log(test) 时,在它后面的声明语句 let test = 2; 是用let来声明的,不会发生声明提升。所以,这个时候test还是没有声明的变量,不存在,所以报错ReferenceError
let, const中的暂时性死区
什么是暂时性死区呢,我们还是先通过一个例子来直观的理解
var test = 'a'; if(true)
{
test = 'b'; //报错 ReferenceError
let test; }
是不是觉得很难理解,感觉不应该报错啊,因为虽然在if语句块里面, 用let对test的声明是在赋值 test = 'b'之后,但是在if语句块外面,我们用var对test进行了声明,它应该也是个全局变量。好像这样理解是对的哈。但事实呢...
ES6中有规定, 如果在语句块 (区块) 中存在let 和 const命令, 这个语句块(区块) 对用let或者const声明的变量, 就形成了一个封闭作用域. 如果你在声明之前就开始使用这个变量,那么从第一次使用它开始,一直到它的声明出现的地方,这个范围我们称之为 "暂时性死区" (temporal dead zone 简称 TDZ) . 在这个 暂时性死区 TDZ 中,使用这些变量 (因为是在声明之前使用),就一定会报错。
上面的代码中,if语句块内部就是一个暂时性死区. 在这个暂时性死区中,用let声明的变量test就会绑定 (binding) 在这个区域,不再受到外部影响. 也就是说如果你在 暂时性死区 外面也声明了一个同名的全局变量,也完全对暂时性死区 里面的变量没有任何影响。
所以,在if语句块这个暂时性死区中,你在let声明test之前,就对test赋值,会报错,因为这个时候test压根就还不存在 我们再看一个例子
if(true)
{ // TDZ 开始
test = 'a';
console.log("The result is " + test); let test; //TDZ结束
console.log(test); // 输出 undefined test = 1;
console.log(test); //输出 1
}
上面这个例子,对 暂时性死区 (TDZ) 的区分写的很清楚,在if语句块中,从test变量第一次出现开始,一直到test的声明出现,这整个范围都是 TDZ 暂时性死区
出现了let之后,以前我们知道的一个常识 “JavaScript中的typeof运算符是百分之百安全的” 就不再是正确的了. 我们来看一个let出现之前(ES6之前)的一个例子
typeof a // 输出 "undefined"
在上面的代码中,因为a没有声明,所以默认就是一个全局变量,而类型未知,所以输出它的类型就是 undefined
出现了let之后,我们再来看一个例子
typeof x; // 报错 ReferenceError
let x;
按照let的暂时性死区 TDZ的说法,在let x之前一直到这一行,都是暂时性死区. 在这个 暂时性死区 TDZ 中,使用这些变量 (因为是在声明之前使用),就一定会报错。所以这个时候使用 typeof x 就一定会报错 ReferenceError, 因为它这个时候压根就还不存在
针对let的暂时性死区TDZ, 我们再来看一个有意思的例子
// 不报错
var a = a; //报错 ReferenceError: a is not defined
let a = a;
上面,在用let声明变量时,就已经开始使用变量,把a赋值给a,也就是说声明和使用是几乎同步进行的。这种情况,报错 ReferenceError:a is not defined . 这说明,在let形式的暂时性死区TDZ中,只要你是在变量还没有声明完成之前使用,就会报错
所以,上面这行语句 let a = a, 在let a 对变量a进行声明语句还没有执行完成之前,就去获取a的值,就报错了。报的错误就是 a 这个时候还没有定义,所以不能使用 (a is not defined).
let,const不允许在同一个作用域内重复声明一个变量
我们来看一个例子,
上面的例子,我用var重复声明了变量a, 完全没有任何问题,成功获取到了最后一次声明时赋的值1. 我们把同样的操作放在let上,来看看会出现什么情况
从上面例子中可以看到,不能使用let重复声明同一个变量, 会报错. 我们再看看
好了,再看一个例子
由上面的两个例子可以看出,用var和let同时声明一个相同的变量,同样报错,因为同样也是,重复声明一个变量.
因为不能重复声明一个变量,所以看看下面的例子:
可以看到, 出错了. 因为函数myFunc的传入参数名为arg,它的作用域是整个函数内部. 而在函数内部又用let声明了同名的变量arg.所以就报错.
这个例子中,可以看到完全没有任何问题。因为let的作用域,函数myFunc的传入参数arg,它的作用域是整个函数内部。而我们在函数内部的{}块声明的变量let arg, 作用域只在这个{}内部,所以没有问题
我们看看下面这个例子就知道了:
也就是说 使用let来定义变量时,内层作用域可以定义和外层作用域同名的变量。但是在同一个作用域内,不能定义同名的变量
{{{ let test = ‘This is test’; {let test = 'This is test'} }}};
上面这个例子,是完全没有问题的
看完了let, 我们再来看看const
和在C#中一样,const用来声明一个常量, 既然是常量,那么它一旦声明,常量的值就不能改变. 我们来看一个例子
上面,我们声明了一个常量a=123; 声明后,它的值是不可以改变的。 上面的代码试图修改它的值为355,所以报错
用const声明的变量不能改变值,也就是说, const声明的变量一旦声明,就必须立即初始化,不能说留到以后去赋值.
const test;
Uncaught SyntaxError: Missing initializer in const declaration
上面的例子中可以看到,声明一个常量 const test 但是没有给它赋初始值. 结果就是报错 Missing initializer in const declaration
上面在讲let时,我们说到, let只在其声明的块级作用域中有效, const也是一样,只在它声明的块级作用域中有效果,只要出了这个块级作用域,它就无效了
if(true)
{
const PI = 3.1415;
} PI //报错 Uncaught ReferenceError: PI is not defined => Const定义的常量PI 只存在于if条件语句块中
和 let 声明的变量一样, const声明的变量同样不存在变量提升,也存在暂时性死区TDZ. 只可以先完成声明工作(const是声明和初始化工作),后面才可以开始使用
if(true)
{
console.log(a); // 报错 ReferenceError => 此时a还没有声明没有初始化,直接使用时报错 , 这里到下面的声明初始化为止,也是常量a的暂时性死区TDZ
const a = 1;
}
和let一样,const也不允许在同一个作用域内重复声明一个变量, 我们来看几个例子
我们看到,上面三个例子全部都是错误的,都是在同一作用域内,重复声明了一个变量a
顶层对象的属性
JavaScript中的顶层对象, 在浏览器环境中指的是window对象。 在Node中指的是global对象. 在原来的ES5中,顶层对象的属性与全局变量是等价的...这句话怎么理解呢,我们来看一个例子就理解了
window.a = 1;
a // a此时为1 a = 2;
window.a //此时 window.a 为2
从上面的代码中可以看到, 顶层对象的属性赋值 和 全局变量的赋值,是同一件事. 很多人都认为,顶层对象的属性与全局变量挂钩,是JavaScript语言最大的设计败笔之一.
但是,在ES6中,let命令,const命令声明的全局变量, 都不再属于顶层对象的属性. 如下面的例子
var a = 0;
window.a // 此时,window.a 也是0 let b = 1;
window.b // 此时,window.b是undefined
上面的例子就可以看出, 使用var声明并且初始化了全局变量a, 它同时也是顶层对象window的属性。
继而,我们又用let声明并初始化了全局变量let, 但它就不是顶层对象window的属性,所以此时window对象并没有b属性,所以返回undefined
JavaScript学习系列5 ---ES6中的var, let 和const的更多相关文章
- JavaScript学习系列6 -- JavaScript中的垃圾回收(内存释放)
程序开发中,涉及到的内存生命周期基本是一样的,分为以下三步 1. 分配需要的内存 2. 使用分配到的内存 3. 释放其内存 ----什么时候释放内存,以及需要释放哪些变量的内存, 就是垃圾回收机 ...
- JavaScript学习系列2一JavaScript中的变量作用域
在写这篇文章之前,再次提醒一下 JavaScript 是大小写敏感的语言 // 'test', 'Test', 'TeSt' , 'TEST' 是4个不同的变量名 JavaScript中的变量,最重要 ...
- ES6中的var let const应如何选择
javascript世界里面的每个人都在说有关ECMAScript 6 (ES6,也称作ES 2015)的话题,对象的巨大变化 ( 类 , super() , 等), 函数 (默认参数等), 以及模块 ...
- Nodejs与ES6系列4:ES6中的类
ES6中的类 4.1.class基本语法 在之前的javascript语法中是不存在class这样的概念,如果要通过构造函数生成一个新对象代码 function Shape(width,height) ...
- JavaScript学习系列之内存模型篇
一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 如果真的想学好一门语言,那么一定要了解它内存模型,本篇文章就带你走进JavaScript的内存模型,由于本人才疏学浅,若有什么表述有误的地方, ...
- [ES6系列-03]ES6中关于参数相关特性详解(参数默认值与参数解构赋值与剩余参数)
[原创] 码路工人 大家好,这里是码路工人有力量,我是码路工人,你们是力量. 今天总结一下 ES6 中跟参数相关的内容. 欢迎补充斧正.留言交流. 让我们互相学习一起进步. 1. ES6 参数默认值( ...
- ES6中不得不说的关键字const
上一节讲了let关键字,它是用来声明一个变量,只在块级作用域起作用.这一节我们来学习ES6新增的另一个关键字const. const 的作用 const是constant(常量)的缩写,const和 ...
- ES6中声明变量 let和const特点
在ES6中我们有两种定义变量的方式:let const let特点: 1.let定义时不会进行变量声明提升 2.变量不允许被重复定义 3.变量不可以被删除 4.在for循环当中用let定义i 循 ...
- JavaScript学习系列4 ----- JavaScript中的扩展运算符 三个点(...)
在JavaScript中, ES6开始有rest参数 和 三个点扩展运算符 (spread运算符) 我们来看看他们各自的用处 1. rest参数 rest参数的形式为 ...变量名 ...
随机推荐
- 20145229吴姗珊 《Java程序设计》第7周学习总结
20145229吴姗珊 <Java程序设计>第7周学习总结 教材学习内容总结 第13章时间与日期 即使标注为GMT(格林威治时间),实际上谈到的的是UTC(Unix时间)时间. 秒的单位定 ...
- 20145229吴姗珊《Java程序设计》第二周学习总结
教材学习内容总结 一.类型.变量与运算符 1.类型 整数:可细分为short整数.int整数和long整数.不同长度的整数可储存的整数范围也不同. 字节:byte类型顾名思义.长度就是一字节,需要逐字 ...
- poj 2251 Dungeon Master-搜索进阶-暑假集训
普及一下知识 s.empty() 如果栈为空返回true,否则返回falses.size() 返回栈中元素的个数s.pop() 删除栈顶元素但不返回其值s.top() 返回栈顶的元素,但不删除该元素s ...
- 算法(Algorithms)第4版 练习 2.3.17
关键代码: public static void sort(Comparable[] a) { StdRandom.shuffle(a);//eliminate dependence on input ...
- ubuntu下搭建Scrapy框架简单办法
1. 先执行以下命令 sudo apt-get install python-lxml sudo apt-get install libxslt1-dev sudo apt-get install p ...
- 大话设计模式--装饰者模式 Decorator -- C++实现实例
1.装饰者模式 Decorator 动态地给一个对象添加一个额外的职责, 就添加功能来说, 装饰模式比生成子类更为灵活. 每个装饰对象的实现和如何使用这个对象分离, 每个装饰对象只关心自己的功能,不 ...
- Xcode 中代码提示不显示
解决办法: Xcode->Window->Organizer->Projects选中你的项目,点击如下图Derived Data右侧的Delete按钮 DerivedData从字面上 ...
- Angular Js 控制器
在Angularjs中用ng-controller指令定义了应用程序中的控制器:例如: <div ng-app="myApp" ng-controller="myC ...
- 英语发音规则---ai字母组合发音
英语发音规则---ai字母组合发音 一.总结 一句话总结:字母组合ai在音词中一般发字母a的音/eɪ/,通常出现在闭音节中.这里要注意的是单词中air字母组合与ai字母组合发音的区别,air发/eə/ ...
- php 实现分享到QQ空间 新浪微博
//分享到新浪微博 $('#blog').click(function(){ window.sharetitle = '<%$info.title%>';//标题 window.share ...