Set和Map数据结构
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);}
// 2 3 5 4
// 去除数组的重复成员
[...new Set(array)]
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
//注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
Map 结构的实例有以下属性和操作方法。
//size属性返回 Map 结构的成员总数。
const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
//set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
m.set(262, 'standard') // 键是数值
//get方法读取key对应的键值,如果找不到key,返回undefined。
m.get(hello) // Hello ES6!
//has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
m.has(262) // true
//delete方法删除某个键,返回true。如果删除失败,返回false。
m.delete(undefined)
//clear方法清除所有成员,没有返回值。
map.clear()
Proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
var proxy = new Proxy({}, {
get: function(target, propKey) {
console.log(target,propKey) //{} "time"
return 35;
}});
console.log(proxy.time) // 35
set()方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求。
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
}};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错
Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。一旦状态改变,就不会再变,任何时候都可以得到这个结果。
const promise = new Promise(function(resolve, reject) {
// ... some code
var b = false
if (b){
resolve('調用成功');
console.log(2); //还是会继续执行的,不受resolve的影响
} else {
reject('調用失敗');
}});
//then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
promise.then(function(value) {
// success
console.log('成功',value)
}, function(error) {
// failure
console.log('失敗',error)
});
promise.catch((error)=>{
console.log('出问题了')
})
//finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise.finally(()=>{
console.log('无论如何都会执行')
})
Promise.all()的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.all([p1, p2, p3]).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
Promise.any()只要有一个成功,就返回fulfilled
Promise.race()只要有一个返回,有以第一个返回的状态为准
for … of
ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for…of循环,作为遍历所有数据结构的统一的方法。a
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
JavaScript 原有的for…in循环,只能获得对象的键名,不能直接获取键值。ES6 提供for…of循环,允许遍历获得键值。
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}
Set 结构遍历时,返回的是一个值,而 Map 结构遍历时,返回的是一个数组,该数组的两个成员分别为当前 Map 成员的键名和键值。
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
for (let [key, value] of map) {
console.log(key + ' : ' + value);
}
// a : 1
// b : 2
并不是所有类似数组的对象都具有 Iterator 接口,一个简便的解决方法,就是使用Array.from方法将其转为数组。
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
// 报错
for (let x of arrayLike) {
console.log(x);
}
// 正确
for (let x of Array.from(arrayLike)) {
console.log(x); // a b
}
对于普通的对象,for…of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,for…in循环依然可以用来遍历键名。
let es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"};
for (let e in es6) {
console.log(e);}
// edition
// committee
// standard
//不能用of
for (let e of es6) {
console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function
//of要替换成下面的操作
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
for (var key of Object.keys(arrayLike)) {
console.log(key + ':' + arrayLike[key]);
//0:a 1:b length:2
}
//或者使用 Generator 函数将对象重新包装一下
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}}
for (let [key, value] of entries(obj)) {
console.log(key, '->', value);}
// a -> 1
// b -> 2
// c -> 3
Generator 函数的语法
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
//第四次调用,此时 Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
asyn 函数
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。它就是 Generator 函数的语法糖。
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const gen = function* () {
const f1 = yield readFile('/etc/fstab');
const f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//上面代码的函数gen可以写成async函数,就是下面这样。
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
在asyn里面的await使函数可以顺序的执行完成
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function testAsyn() {
console.log(1)
await timeout(1000).then(()=>{
console.log('1000的then')
});
console.log(2)
await timeout(3000).then(()=>{
console.log('3000的then')
});
console.log(3)
return '按顺序1234执行完成!';
}
testAsyn().then(function (result) {
console.log(4)
console.log(result);
});
上面函数有await打印的顺序
1
1000的then
2
3000的then
3
4
按顺序1234执行完成!
上面函数没有await打印的顺序
1
2
3
4
按顺序1234执行完成!
1000的then
3000的then