ES6
let,const及var
let
- let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
{
let a =10;
var b= 8;
}
console.log(a);//ReferenceError: a is not defined(let声明的变量只在当前代码块内有效)
console.log(b);//1
- 不存在变量提升
对比:
var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined
// var 的情况
console.log(a); // 输出undefined
var a = 2;
// let 的情况
console.log(b); // 报错ReferenceError
let b = 2;
- 不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
let a = 1;
let a = 2
//SyntaxError: Identifier 'a' has already been declared
- 暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
const
- const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
说明:
const一旦初始化赋值,就不能改变
- const一旦声明变量,就必须立即初始化,不能留到以后赋值。对于const来说,只声明不赋值,就会报错
const a;// SyntaxError: Missing initializer in const declaration
说明:const一旦声明,必须赋值
- const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
if (true) {
console.log(b); // ReferenceError
const b = 5;
}
- const的作用域与let命令相同:只在声明所在的块级作用域内有效。
if (true) {
const b= 2;
}
console.log(b);//报错
var:变量提升(无论声明在何处,都会被提至其所在作用于的顶部)
let:无变量提升(未到let声明时,是无法访问该变量的)
const:无变量提升,声明一个基本类型的时候为常量,不可修改;声明对象可以修改
变量提升与暂时性死区
暂时性死区:
- ES6规定,如果区块中存在let和const命令,这个区块对let和const命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。这在语法上,称为“暂时性死区”(Temporal Tead Zone,简称TDZ)。
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
变量提升
- 如果我们直接输出一个没有定义的变量,会报错not defined
- 如果我们在定义变量的语句之前输出该变量,会输出undefine
- 这种情况就叫做变量提升,在定义语句上面调用变量的话,会假设在调用语句上面加了一句定义而未赋值的语句
变量的结构赋值
箭头函数及其 this 问题
Symbol 概念及其作用
现在我们知道了什么是基本类型,终于准备好如何定义什么是 symbols 了。symbols 是一种无法被重建的基本类型。这时 symbols 有点类似与对象创建的实例互相不相等的情况,但同时 symbols 又是一种无法被改变的基本类型数据。这里有一个例子:
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
阻止对象属性名冲突
symbols 可能对对象的私有属性没有直接好处,但是它有另外一个用途,它在不知道对象原有属性名的情况下,扩展对象属性很有用。
考虑一下当两个不同的库要读取对象的一些原始属性时,或许它们都想要类似的标识符。如果只是简单的使用字符串 id 作为 key,这将会有很大的风险,因为它们的 key 完全有可能相同。
function lib1tag(obj) {
obj.id = 42;
}
function lib2tag(obj) {
obj.id = 369;
}
Set 和 Map 数据结构
1. Set
1.1 基本用法
Set是ES6提供的新的数据结构。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set数据结构和Symbol不一样,Set需要使用构造函数来生成数据结构实例。
const s = new Set();
上面代码通过Set构造函数生成了一个Set数据实例。
const s = new Set();
[1, 2, 3, 4, 5, 6, 5, 4].forEach(v => s.add(v));
console.log(s);
// Set { 1, 2, 3, 4, 5, 6 }
上面代码中通过add方法向Set结构中加入成员,可以看出Set结构中不会添加重复的成员。
上面代码中通过add方法向Set结构中加入成员,可以看出Set结构中不会添加重复的成员。
【Set允许初始化时传入一个数组作为参数(或者具有 iterable 接口的其他数据结构)】,则上面的例子可以改写为下面这个样子。
const s = new Set([1, 2, 3, 4, 5, 6, 5, 4]);
console.log(s);
//Set { 1, 2, 3, 4, 5, 6 }
上面代码应用数组做为参数,将数组数据结构转换成了Set数据结构,并去除了重复数据。
结合扩展运算符,我们便又拥有了一种数组数据去重的方式
const s = new Set([1, 2, 3, 4, 5, 6, 5, 4]);
console.log([...s]);
//[ 1, 2, 3, 4, 5, 6 ]
上面代码中将原数组中的重复值很轻松的就去掉了。
【注意】在向Set中添加元素的时候,不会发生类型的转换!也就是说字符串‘5’和数值5是两个数据,不会被去重。而且Set数据结构的内部是使用的严格相等运算符(===),但是在Set数据结构中NaN是等于NaN的。
1.2 Set实例的属性和方法
1.2.1 Set实例属性
Set实例有以下两种属性
- Set.prototype.constructor:构造函数,默认就是Set函数。
- Set.prototype.size:返回Set实例的成员总数。
1.2.2 Set实例的方法
Set实例方法有【操作方法】和【遍历方法】两大类。
1.2.2.1 操作方法
-
add(value):添加某个值,返回Set结构本身
-
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
-
has(value):返回一个布尔值,表示该值是否为Set的成员。
-
clear():清除所有成员,没有返回值。
遍历方法
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
2.Map
Map数据结构是ES5键值对对象的加强,ES5的键值对对象只能使用字符串用作键名称,Map的键名范围不限于字符串,Map结构提供了“值-值”的对应,是一种完善的Hash结构的实现。
const m = new Map();
const key = { name: "Blue" };
m.set(key, 'Crazy');
m.get(key); //Crazy
m.has(key); //true
m.delete(key); //true
m.has(key); //false
上面代码中用了Map数据结构的Set方法,将对象key作为了键,然后又使用get方法读取这个键,has方法判断该结构中是否有该值,然后用delete删除了这个键。
【Map也可以接受一个数组作为参数】该数组的成员是一个个表示键值对的数组。
const m = new Map([
["Name", "Blue"],
["Age", 23],
["Gender", "Man"]
]);
console.log(m);
// Map { 'Name' => 'Blue', 'Age' => 23, 'Gender' => 'Man' }
上面代码在构造函数中传入一个二维数组作为参数,生成了一个拥有Name、Age、Gender三个键的Map数据结构。
Map数据结构接受数组作为参数实质是执行了下面的操作
const Arr = [
["Name", "Blue"],
["Age", 23],
["Gender", "Man"]
];
const m = new Map();
m.forEach(([key, value]) => m.set(key, value));
上面代码应用数组的解构赋值,调用本来的set方法为Map数据结构添加值。
【如果对一个键多次赋值,后面的值将覆盖前面的值】
const m = new Map();
m.set('Name', 'Blue');
m.set('Name', 'Crazy');
console.log(m);
Map { 'Name' => 'Crazy' }
【如果读取一个未知的键,返回undefined】
new Map().get('Name'); //undefined
【注意】只有对同一个对象的引用,Map 结构才将其视为同一个键。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
上面代码中的set和get方法,表面上是一个键,但实际上这是两个键,这两个数组虽然数组成员一样,但是两个数组在内存中的地址是不一样的。
那么同样值得两个实例,在Map结构中也被视为两个键。
const m = new Map();
const arr1 = ['a'];
const arr2 = ['a'];
m.set(arr1, 'Blue')
.set(arr2, 'Crazy');
console.log(m);
Map { [ 'a' ] => 'Blue', [ 'a' ] => 'Crazy' }