[ES6]Day01—ES6简介、Let、Const关键字、块级作用域
ECMAScript 6 入门 (Day01)
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6
月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
1.1 ECMAScript 和 JavaScript 的关系
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有
JScript 和 ActionScript)。日常场合,这两个词是可以互换的。
1.2 ES6 与 ECMAScript 2015 的关系
ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。
-----------------------------------------------------------分隔线-------------------------------------------------
1.3 let 命令
ES6 新增了let命令,用来声明变量。 其用法类似于var,但是 使用let关键字声明的变量,只在所在的代码块内有效;使用let关键字声明的变量才具有块级作用域 。
1)使用let关键字声明的变量,只在所在的代码块内有效。
if(true){
let a = 10;
console.log(a) //10
}
console.log(a) // a is not defined
2)使用let关键字声明的变量才具有块级作用域
if(true){
let a = 10;
var b = 200;
}
console.log(a) // a is not defined
console.log(b) // 200
3)不允许重复声明同一个变量
let 不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
因此,不能在函数内部重新声明参数。
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
4)防止循环变量 变成 全局变量
for(let i = 0;i < 2; i++){
}
console.log(i) //i is notdefined
for(var i = 0;i < 2; i++){
}
console.log(i) //2
5)不存在变量提升,需要先定义再使用
变量提升 即 变量可以在声明之前使用,值为undefined
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
6)使用let关键字声明的变量具有暂时性死区
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。 凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// 暂时性死区开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // 直到定义,暂时性死区才结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
拓展:面试题
原题:循环用var 关键字
var arr=[];
for(var i=0;i<2;i++){
arr[i]=function(){
console.log(i)
}
}
arr[0]();//2
arr[1]();//2
题目分析:
定义一个数组,循环体是给数组内的元素赋值,这里给每个元素赋的值是方法,方法中打印i,当i=2时跳出循环,arr0; 表示取出数组下标为0的元素,这个元素为一个方法,直接arr[0]()调用方法
题目详解:
console.log(i)
根据作用域链查找原则,要向上一级作用域查找,在这里上一级作用域是循环创建的全局作用域下的全局变量 i
,当arr[i]();
函数执行时,此时循环已经停止,i=2
,由于数组中的元素函数输出的都是全局变量i
,所以结果都是 2
变题:循环用let关键字
var arr=[];
for(let i=0;i< 2;i++){
arr[i]=function(){
console.log(i)
}
}
arr[0]();//0
arr[1]();//1
题目详解:
由于使用的是let
关键字,具有块级作用域,在循环结束后,生成了两个块级作用域,产生的两个块级作用域都有各自的变量i
,两个变量互不影响,console.log(i)
根据作用域链查找原则,要向上一级作用域查找,在这里上一级作用域是循环创建的两个块级作用域都有各自的变量i
,当arr[i]();
函数执行时,由于数组中的元素函数输出的都是块级作用域 变量i
,所以结果分别是0 ,1
1.4 const 命令
const声明一个只读的常量。一旦声明,常量的值就不能改变。
1)声明常量时必须赋初始值
const PI //Missing initializer in const declaration 丢失初始值
2)使用const关键字声明的变量才具有块级作用域
if(true){
const a = 10;
if(true){
const a = 20;
console.log(a) //20
}
console.log(a) //10
}
console.log(a) // a is not defined
3)用于声明常量,常量的值(内存地址)就不能改变
ECMAScript中有5中简单数据类型(也称为基本数据类型): Undefined、Null、Boolean、Number和String,复杂的数据类型:Object
对于基本类型,值不可更改,例如:String 、Number、 Boolean类型等
const PI = 3.1415;
PI = 3; // TypeError: Assignment to constant variable.
上面代码表明改变常量的值会报错。
const ary=[100,200];
ary[0]='a'; // 为 ary 修改元素,可以成功
ary[1]='b';
console.log(ary);//['a','b']
// 将 ary 直接赋值,就会报错
ary=['a','b'] ;// TypeError: Assignment to constant variable.
const foo = {};
foo.prop = 123; // 为 foo 添加一个属性,可以成功
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
上面代码表明改变常量值对应的内存地址会报错。 常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。
1.const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值
2.const的作用域与let命令相同:只在声明所在的块级作用域内有效。
3.const声明的常量,也与let一样不可重复声明
4.const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
1.5 块级作用域
1)ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
块级作用域,其实就相当于一对大括号内的区域,比如
if(){ }
、function(){ }
,for(){ }
等等
- 第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';// 内层的tmp变量覆盖了外层的tmp变量
}
}
f(); // undefined
上面代码的原意是,if 代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。
- 第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
2)块级作用域与函数声明
ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。
- ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。
- ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
- 允许在块级作用域内声明函数。
- 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
- 同时,函数声明还会提升到所在的块级作用域的头部
ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
// 第一种写法,报错
if (true) let x = 1;
// 第二种写法,不报错
if (true) {
let x = 1;
}
上面代码中,第一种写法没有大括号,所以不存在块级作用域,而let只能出现在当前作用域的顶层,所以报错。第二种写法有大括号,所以块级作用域成立。
函数声明也是如此,严格模式下,函数只能声明在当前作用域的顶层。
// 不报错
'use strict';
if (true) {
function f() {}
}
// 报错
'use strict';
if (true)
function f() {}
ES6 声明变量的六种方法
var , function , let , const , import , class
ES5 只有两种声明变量的方法:var命令 和 function命令。
ES6 除了添加 let 和 const 命令,后面章节还会提到,另外两种声明变量的方法:
import命令和class命令。
所以,ES6 一共有 6 种声明变量的方法。
1.6 顶层对象的属性
顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
- 在ES6 中 var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性;
- 另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。
- 也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
var a = 1;
window.a // 1
let b = 1;
window.b // undefined
上面代码中,全局变量 a 由 var 命令声明,所以它是顶层对象的属性;
全局变量 b 由 let 命令声明,所以它不是顶层对象的属性,返回undefined。
下一篇 : [ES6]Day02—变量的解构赋值、箭头函数
参考链接: