我们都知道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. ThinkPHP的Auth类认证

    Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比RBAC更方便 .  RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了 ...

  2. Spring Cloud之服务治理(注册发现)

    服务治理SpringCloud Eureka 什么是服务治理 在传统rpc远程调用中,服务与服务依赖关系,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用.负载均衡.容 ...

  3. JavaWeb -- 会话, Cookie 和 Session

    1. 会话 •Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器.当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去.这样,web资源处理的就是 ...

  4. requests获取响应时间(elapsed)与超时(timeout)

    前言 requests发请求时,接口的响应时间,也是我们需要关注的一个点,如果响应时间太长,也是不合理的.如果服务端没及时响应,也不能一直等着,可以设置一个timeout超时的时间 关于request ...

  5. cookie对比localStorage哪个适合作为网站皮肤存储

    cookie对比localStorage哪个适合作为网站皮肤存储 cookie cookie : 一般由服务器生成,可设置失效时间.如果在浏览器生成,默认是关闭浏览器之后失效 存储大小:4k 每次都会 ...

  6. java客户端文件的上传和下载

    java客户端文件的上传和下载 //上传 public JTable upload(String id){ JTable table=new JTable(); System.out.println( ...

  7. 两个stack实现一个queue

    package com.hzins.suanfa; import java.util.Stack; /** * 两个stack实现一个queue * @author Administrator * * ...

  8. 【转】C++11 标准新特性:Defaulted 和 Deleted 函数

    原文链接http://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/ 本文将介绍 C++11 标准的两个新特性:defaul ...

  9. linux命令学习笔记(53):route命令

    Linux系统的route命令用于显示和操作IP路由表(show / manipulate the IP routing table).要实现两 个不同的子网之间的通信,需要一台连接两个网络的路由器, ...

  10. 1038 Recover the Smallest Number (30)(30 分)

    Given a collection of number segments, you are supposed to recover the smallest number from them. Fo ...