七、解构
7.1 解构的实用
在 ECMAScript 5 或更早的版本中,从对象或数组中获取特定的数据并赋值给本地变量需要书写很多并且相似的代码。例如:
let options = {
repeat: true,
save: false
};
// 从对象中提取数据
let repeat = options.repeat,
save = options.save;
这段代码反复地提取在 options 上存储地属性值并将它们传递给同名的本地变量。虽然这些看起来不是那么复杂,不过想象一下如果你的一大批变量有着相同的需求,你就只能一个一个地赋值。而且,如果你需要从对象内部嵌套的结构来查找想要的数据,你极有可能为了一小块数据而访问了整个数据结构。
这也是 ECMAScript 6 给对象和数组添加解构的原因。当你想要把数据结构分解为更小的部分时,从这些部分中提取数据会更容易些。很多语言都能使用精简的语法来实现解构操作。ECMAScript 6 解构的实际语法或许你已经非常熟悉:对象和数组字面量。
7.2 对象解构
7.2.1 对象解构的基本形式
对象结构的语法就是在赋值语句的左侧使用类似对象字面量的结构。
let node = {
type: "Identifier",
name: "foo"
};
//这里就相当于声明了两个变量: type = node.type; name:node.name
let { type, name } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
在上面的结构中必须要初始化。否则会出现语法错误。
// 语法错误!
var { type, name };
// 语法错误!
let { type, name };
// 语法错误!
const { type, name };
7.2.2 解构赋值表达式
如果声明的变量想改变他们的值,也可以使用解构表达式。
7.2.3 对象解构时的默认值
如果赋值号右边的对象中没有与左边变量同名的属性,则左边的变量会是 undefined
let node = {
type: "Identifier",
name: "foo"
};
//因为node中没有叫value的属性,所以valued的值将会是undefined
let { type, name, value } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined
不过我们也可以手动指定他的默认值。(这个和函数的参数默认值很像)
7.2.4 赋值给不同的变量名
在前面的操作中,都是把对象的属性值,赋值给同名变量。
其实也可以赋值给不同名的变量。
注意:冒号后面才是要定义的新的变量,这个和我们的对象字面量不太一样!
这个地方也可以使用默认值。
let node = {
type: "Identifier"
};
let { type: localType, name: localName = "bar" } = node;
console.log(localType); // "Identifier"
console.log(localName); // "bar"
7.3 数组解构
7.3.1 数组解构基本语法
数据解构的语法和对象解构看起来类似,只是将对象字面量替换成了数组字面量,而且解构操作的是数组内部的位置(索引)而不是对象中的命名属性,例如:
let colors = [ "red", "green", "blue" ];
let [ firstColor, secondColor ] = colors;
console.log(firstColor); // "red"
console.log(secondColor); // "green"
如果只想取数组中的某一项,则可以不用命名。
let colors = [ "red", "green", "blue" ];
//只取数组中的第三项。
let [ , , thirdColor ] = colors;
console.log(thirdColor); // "blue"
7.3.2 解构表达式
你可以想要赋值的情况下使用数组的解构赋值表达式,但是和对象解构不同,没必要将它们包含在圆括号中,例如:
let colors = [ "red", "green", "blue" ],
firstColor = "black",
secondColor = "purple";
[ firstColor, secondColor ] = colors; //可以不用加括号。当然添加也不犯法
console.log(firstColor); // "red"
console.log(secondColor); // "green"
数组解构表达式有一个很常用的地方,就是交换两个变量的值。在以前一般定义一个第三方变量进行交换,例如下面的代码:
那么在ES6中完全可以抛弃第三方变量这种方式,使用我们的数组解构表达式
八、新的基本类型:Symbol
以前我们有5种基本数据类型:Number、String、Boolean、Null、Undefined
ES6新增了一种新的数据类型:Symbol
在ES5之前我们都没办法创建私有变量,只能想办法去封装。symbol 来创建私有成员,这也是 JavaScript 开发者长久以来期待的一项特性。
8.1 创建Symbol
Symbol在基本数据类型中是比较特别的。我们以前的都可以用字面量去创建基本数据类型的数据,但是Symbol却不可以使用字面量的是形式去创建。
我们可以使用symbol全局函数来创建Symbol。
说明:上面的代码中,firstName 作为 symbol 类型被创建并赋值给 person 对象以作其属性。每次访问这个属性时必须使用该 symbol 。
在创建Symbol的时候,也可以传入字符串,这个字符串也仅仅是在调试输出的时候方便,实际没有啥用处。
注意:任意两个Symbol都不会相等,即使创建他们的时候使用了相同的参数。
8.2 识别Symbol
既然 symbol 是基本类型,你可以使用 typeof 操作符来判断变量是否为 symbol 。ECMAScript 6 拓展了 typeof 使其操作 symbol 时返回 "symbol"。例如:
let symbol = Symbol();
console.log(typeof symbol); // "symbol"
8.3 Symbol作为属性名
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
var mySymbol = Symbol();
// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
var a = {
[mySymbol]: 'Hello!'
}
以上两种写法都是相同的结果
注意:
symbol作为对象的属性的时候,只能使用 [ ] 去访问,不能使用点去访问。
symbol作为对象的属性名使用的时候,该属性还是公开属性,不是私有属性。但是这个时候使用for... in和for...of时无法遍历到这个symbol属性的。
8.4 Symbol属性名的遍历
Symbol 作为属性名,该属性不会出现在for...in循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
看下面的代码
看下面的代码
var obj = {};
var foo = Symbol("foo");
obj[foo] = "lisi";
for (var i in obj) {
console.log(i); // 无输出 。 因为遍历不到Symbol型的属性
}
Object.getOwnPropertyNames(obj);// [] 只能拿到非Symbol类型的属性
Object.getOwnPropertySymbols(obj) //[Symbol(foo)]
还有一个新API可以拿到所有类型的属性,包括常规和Symbol型的。
Reflect.ownKeys
let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj);// ["enum", "nonEnum", Symbol(my_key)]
说明:
- 由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
8.5 Symbol.for(字符串)和Symbol.keyFor(symbol类型的值)
一、Symbol.for(字符串参数):在全局环境中搜索 以该字符串作为参数的Symbol值,如果搜到则返回这个sybol,如果搜不到则创建一个Symbol,并把它注册在全局环境中。
Symbol.for()和Symbol()都可以创建Symbol类型的数据。
二者区别:
- Symbol.for()对同样的字符串,每次得到结果肯定是一样的。因为都是从全局环境中搜索。
- Symbol()则不会有搜索的过程,每次都是一个全新的不同的symbol,而且也不会向全局环境中注册。
看下面的代码
二、Symbol.keyFor(symbol):返回一个已经全局注册的symbol的"key"。
九、Set数据结构
JavaScript 在绝大部分历史时期内只有一种集合类型,那就是数组。数组在 JavaScript 中的使用方式和其它语言很相似,但是其它集合类型的缺乏导致数组也经常被当作队列(queues)和栈(stacks)来使用。
因为数组的索引只能是数字类型,当开发者觉得非数字类型的索引是必要的时候会使用非数组对象。这项用法促进了以非类数组对象为基础的 set 和 map 集合类型的实现。
Set是类似数组的一种结构,可以存储数据,与数组的区别主要是 Set中的元素不能重复,而数组中的元素可以重复。
一句话总结:Set类型是一个包含无重复元素的有序列表
9.1 创建Set和并添加元素
Set本身是一个构造函数。
9.2 Set中不能添加重复元素
看下面的代码:
在上面的代码中,数字5和字符串5都会添加成功。为什么呢?
Set是使用什么机制来判断两个元素是否相等的呢?
是通过我们前面说过的 Object.is(a, b) 来判断两个元素是否相等。
9.3 使用数组初始化Set
9.4 判断一个值是否在Set中
使用Set的 has() 方法可以判断一个值是否在这个set中。
9.5 移除Set中的元素
delete(要删除的值) :删除单个值
clear():清空所有的值
9.6 遍历Set
数组有个方法forEach可以遍历数组。
- Set也有forEach可以遍历Set。
使用Set的forEach遍历时的回调函数有三个参数:
function (value, key, ownerSet){
}
参数1:遍历到的元素的值
参数2:对set集合来说,参数2的值和参数1的值是完全一样的。
参数3:这个 ==set== 自己
- for…of也可以遍历set。
for(var v of set){
console.log(v)
}
9.7 将Set转换为数组
将数组转换为Set相当容易,你只需要在创建Set集合时把数组作为参数传递进去即可。
把Set转换为数组使用前面讲到的扩展运算符也很容易
这种情况在需要去数组中重复元素的时候非常好用。
Set提供了处理一系列值的方式,不过如果想给这些值添加一些附加数据则显得力不从心,所以又提供了一种新的数据结构:Map