我们都知道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的更多相关文章

  1. JavaScript学习系列6 -- JavaScript中的垃圾回收(内存释放)

    程序开发中,涉及到的内存生命周期基本是一样的,分为以下三步 1. 分配需要的内存 2. 使用分配到的内存 3. 释放其内存    ----什么时候释放内存,以及需要释放哪些变量的内存, 就是垃圾回收机 ...

  2. JavaScript学习系列2一JavaScript中的变量作用域

    在写这篇文章之前,再次提醒一下 JavaScript 是大小写敏感的语言 // 'test', 'Test', 'TeSt' , 'TEST' 是4个不同的变量名 JavaScript中的变量,最重要 ...

  3. ES6中的var let const应如何选择

    javascript世界里面的每个人都在说有关ECMAScript 6 (ES6,也称作ES 2015)的话题,对象的巨大变化 ( 类 , super() , 等), 函数 (默认参数等), 以及模块 ...

  4. Nodejs与ES6系列4:ES6中的类

    ES6中的类 4.1.class基本语法 在之前的javascript语法中是不存在class这样的概念,如果要通过构造函数生成一个新对象代码 function Shape(width,height) ...

  5. JavaScript学习系列之内存模型篇

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 如果真的想学好一门语言,那么一定要了解它内存模型,本篇文章就带你走进JavaScript的内存模型,由于本人才疏学浅,若有什么表述有误的地方, ...

  6. [ES6系列-03]ES6中关于参数相关特性详解(参数默认值与参数解构赋值与剩余参数)

    [原创] 码路工人 大家好,这里是码路工人有力量,我是码路工人,你们是力量. 今天总结一下 ES6 中跟参数相关的内容. 欢迎补充斧正.留言交流. 让我们互相学习一起进步. 1. ES6 参数默认值( ...

  7. ES6中不得不说的关键字const

    上一节讲了let关键字,它是用来声明一个变量,只在块级作用域起作用.这一节我们来学习ES6新增的另一个关键字const. const 的作用 const是constant(常量)的缩写,const和 ...

  8. ES6中声明变量 let和const特点

    在ES6中我们有两种定义变量的方式:let    const let特点: 1.let定义时不会进行变量声明提升 2.变量不允许被重复定义 3.变量不可以被删除 4.在for循环当中用let定义i 循 ...

  9. JavaScript学习系列4 ----- JavaScript中的扩展运算符 三个点(...)

    在JavaScript中, ES6开始有rest参数 和 三个点扩展运算符 (spread运算符) 我们来看看他们各自的用处 1. rest参数 rest参数的形式为 ...变量名          ...

随机推荐

  1. 20145229吴姗珊 《Java程序设计》第7周学习总结

    20145229吴姗珊 <Java程序设计>第7周学习总结 教材学习内容总结 第13章时间与日期 即使标注为GMT(格林威治时间),实际上谈到的的是UTC(Unix时间)时间. 秒的单位定 ...

  2. 20145229吴姗珊《Java程序设计》第二周学习总结

    教材学习内容总结 一.类型.变量与运算符 1.类型 整数:可细分为short整数.int整数和long整数.不同长度的整数可储存的整数范围也不同. 字节:byte类型顾名思义.长度就是一字节,需要逐字 ...

  3. poj 2251 Dungeon Master-搜索进阶-暑假集训

    普及一下知识 s.empty() 如果栈为空返回true,否则返回falses.size() 返回栈中元素的个数s.pop() 删除栈顶元素但不返回其值s.top() 返回栈顶的元素,但不删除该元素s ...

  4. 算法(Algorithms)第4版 练习 2.3.17

    关键代码: public static void sort(Comparable[] a) { StdRandom.shuffle(a);//eliminate dependence on input ...

  5. ubuntu下搭建Scrapy框架简单办法

    1. 先执行以下命令 sudo apt-get install python-lxml sudo apt-get install libxslt1-dev sudo apt-get install p ...

  6. 大话设计模式--装饰者模式 Decorator -- C++实现实例

    1.装饰者模式 Decorator 动态地给一个对象添加一个额外的职责, 就添加功能来说, 装饰模式比生成子类更为灵活. 每个装饰对象的实现和如何使用这个对象分离,  每个装饰对象只关心自己的功能,不 ...

  7. Xcode 中代码提示不显示

    解决办法: Xcode->Window->Organizer->Projects选中你的项目,点击如下图Derived Data右侧的Delete按钮 DerivedData从字面上 ...

  8. Angular Js 控制器

    在Angularjs中用ng-controller指令定义了应用程序中的控制器:例如: <div ng-app="myApp" ng-controller="myC ...

  9. 英语发音规则---ai字母组合发音

    英语发音规则---ai字母组合发音 一.总结 一句话总结:字母组合ai在音词中一般发字母a的音/eɪ/,通常出现在闭音节中.这里要注意的是单词中air字母组合与ai字母组合发音的区别,air发/eə/ ...

  10. php 实现分享到QQ空间 新浪微博

    //分享到新浪微博 $('#blog').click(function(){ window.sharetitle = '<%$info.title%>';//标题 window.share ...