es6

时间:2023-03-09 15:41:30
es6

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>lcr</title>

1:引入的改变:
script标签的type属性的值是module(或者traceur),而不是text/javascript
<script type="module"> < /script>

2:let 块级变量
if(true){var a = 1; let b = 2; }
console.log(a);// ok
console.log(b);// 报错:ReferenceError: b is not defined

3:const 命令
const 声明的是常量,一旦声明,值将是不可变的。
const PI = 3.1415;
//PI = 3; 报错:TypeError: Assignment to constant variable.
//const PI = 3.1; 报错:const 不可重复声明

const 不能变量提升(必须先声明后使用)
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}

const 指向变量所在的地址,对变量进行属性设置是可行的(未改变变量地址),如果想完全不可变化(包括属性),那么可以使用冻结。
const C1 = {};
C1.a = 1;
console.log(C1.a); // 1
C1 = {}; // 报错 重新赋值,地址改变

//冻结对象,此时前面用不用const都是一个效果
const C2 = Object.freeze({});
C2.a = 1; //Error,对象不可扩展
console.log(C2.a);

4:String 新方法
4.1
includes(): 返回布尔值,表示是否找到了参数字符串。
startsWith(): 返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith(): 返回布尔值,表示参数字符串是否在源字符串的尾部。
var str = "Hello world!";
str.startsWith("Hello") // true
str.endsWith("!") // true
str.includes("o") // true

这三个方法都支持第二个参数,表示开始搜索的位置。
var str = "Hello world!";
str.startsWith("world", 6) // true
str.endsWith("Hello", 5) // true
str.includes("Hello", 6) // false

4.2 repeat()原字符串重复
var str1 = "hello";
str1.repeat(2) // "hellohello"

4.3 String.raw() 原生的String对象:
let raw = String.raw`Not a newline: \n`;
console.log(raw === 'Not a newline: \\n'); // true

4.4 模板字符串,要用``符号包括;模板字符串提供了3个有意思的特性。
4.4.1 模板字符中,支持字符串插值:
let first = 'hubwiz';
let last = '汇智网';
alert(`Hello ${first} ${last}!`);//Hello hubwiz 汇智网!

4.4.2 模板字符串可以包含多行:
let multiLine = `
This is
a string
with multiple
lines`;
console.log(multiLine); //This is a string with multiple lines

4.4.3 标签模板
var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b }`;

tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值

tag函数所有参数的实际值如下。
第一个参数:['Hello ', ' world ']
第二个参数: 15
第三个参数:50

tag函数实际上以下面的形式调用:tag(['Hello ', ' world '], 15, 50)

7:Number 新方法:
Number.isFinite()用来检查一个数值是否非无穷(infinity)。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite("foo"); // false
Number.isFinite("15"); // false
Number.isFinite(true); // false

Number.isNaN()用来检查一个值是否为NaN。
Number.isNaN(NaN); // true
Number.isNaN(15); // false
Number.isNaN("15"); // false
Number.isNaN(true); // false

Number.isInteger()用来判断一个值是否为整数。3和3.0被视为同一个值。
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false

8:Math 新方法
Math.trunc():去除一个数的小数部分,返回整数部分。对于空值和无法截取整数的值,返回NaN
Math.trunc(4.1) // 4
Math.trunc(-4.1) // -4
Math.trunc('ddd') // NaN

Math.sign():判断一个数到底是正数、负数、还是零。返回五种值:如下
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign('hubwiz'); // NaN

Math.hypot():返回所有参数的平方和的平方根。
Math.hypot(3, 4); // 5
Math.hypot(3, 4, 5); // 7.0710678118654755
Math.hypot(); // 0
Math.hypot(NaN); // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5'); // 7.0710678118654755
Math.hypot(-3); // 3

Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)

9:Array 数组
9.1 Array.from() : 方法用于将两类对象转为真正的数组:
//返回类数组,将这个对象转为真正的数组,才能使用forEach方法
let list = document.querySelectorAll('ul.fancy li');
Array.from(list).forEach(function (li) {
............
});
//任何有length属性的对象,都可以通过Array.from方法转为数组
let array = Array.from({ 0: "a", 1: "b", 2: "c", length: 3 });
console.log(array); // [ "a", "b" , "c" ]

//Array.from()的一个应用是,将字符串转为数组,然后返回字符串的长度。这样可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。
function countSymbols(string) {
return Array.from(string).length;
}

9.2 Array.of() : 方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

9.3 Array.find(function(){}) :找出第一个符合条件的数组成员。回调函数找出第一个返回值为true的成员,然后返回该成员。否则返回undefined。

let array = [1, 4, -5, 10].find((n) => n < 0);
console.log("array:", array);//array:-5

//三个参数,依次为当前的值、当前的位置和原数组。
let array = [1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
})
console.log(array); // 10

9.4 Array.findIndex() : 用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
let index = [1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})
console.log(index); // 2

9.5 Array.fill() : 填充数组
let arr = ['a', 'b', 'c'].fill(7)
console.log(arr); // [7, 7, 7]

let newArr = new Array(3).fill(7)
console.log(newArr); // [7, 7, 7]

//fill()还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
let newArr = ['a', 'b', 'c'].fill(7, 1, 2)
console.log(newArr); // ['a', 7, 'c']

9.6 遍历数组:entries(),keys(),values()
// 可以用for...of循环进行遍历
// keys()是对键名的遍历、
// values()是对键值的遍历,
// entries()是对键值对的遍历。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"

11:Object.assign()
//将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。只要有一个参数不是对象,就会抛出TypeError错误。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
//如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

12: function
12.1 默认参数
//传统的指定默认参数的方式
function sayHello(name){
var name = name||'hubwiz';
console.log('Hello '+name);
}
sayHello(); //输出:Hello hubwiz
sayHello('汇智网'); //输出:Hello 汇智网
//运用ES6的默认参数
function sayHello2(name='hubwiz'){
console.log(`Hello ${name}`);
}
sayHello2(); //输出:Hello hubwiz
sayHello2('汇智网'); //输出:Hello 汇智网

12.2 rest参数 ...变量名
//rest参数(形式为“...变量名”)可以称为不定参数,用于获取函数的多余参数。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(1, 2, 3) // 6
//不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如以上示例中,...values 代表了所有传入add函数的参数。

12.3 扩展运算符 ...
//该运算符主要用于函数调用。它允许传递数组或者类数组直接做为函数的参数而不用通过apply。

var people=['张三','李四','王五'];

//sayHello函数本来接收三个单独的参数people1,people2和people3
function sayHello(people1,people2,people3){
console.log(`Hello ${people1},${people2},${people3}`);
}

//但是我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数
sayHello(...people); //输出:Hello 张三,李四,王五

//而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法
sayHello.apply(null,people); //输出:Hello 张三,李四,王五

12.4 箭头函数:=>
var array = [1, 2, 3];
//传统写法
array.forEach(function(v, i, a) {console.log(v); });
//ES6
array.forEach(v => console.log(v));

//支持表达式体和语句体。
// 箭头函数有几个使用注意点。
// 函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。
// 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
// 不可以使用arguments对象,该对象在函数体内不存在。
// 上面三点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。
var evens = [1,2,3,4,5];
var fives = [];
// 表达式体
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
// 语句体
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
console.log(fives);
// 例子中 'this._friends.forEach' 的this指向是bob对象,而不是_friends数组
var bob = {
_name: "Bob",
_friends: ["Amy", "Bob", "Cinne", "Dylan", "Ellen"],
printFriends() {
this._friends.forEach(f =>
//this._name == "Bob"
//Bob knows Amy
//Bob knows Bob
//Bob knows Cinne
//Bob knows Dylan
//Bob knows Ellen
console.log(this._name + " knows " + f)
);
}
}
bob.printFriends();

12.5 函数绑定
//函数绑定运算符是并排的两个双引号(::),双引号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

foo::bar;
//等同于
bar.call(foo);

foo::bar(...arguments);
//等同于
bar.apply(foo, arguments);

13: Set 数据结构
13.1
//数据结构Set类似于数组,但是成员的值都是唯一的,没有重复的值
var s = new Set();
[2,3,5,4,5,2,2].map(x => s.add(x))
for (i of s) {console.log(i)}// 2 3 5 4
//Set函数可以接受一个数组作为参数,用来初始化。
var items = new Set([1,2,3,4,5,5,5,5]);
console.log(items.size); // 5
//Set加入值的时候,不会发生类型转换,所以5和“5”是两个不同的值
let set = new Set();
set.add({})
set.size // 1
set.add({})
set.size // 2 两个空对象不是精确相等,所以它们被视为两个值。
//Set.prototype.size:返回Set实例的成员总数。
//Set.prototype.constructor:构造函数,默认就是Set函数。
let s = new Set();
s.add(1).add(2).add(2);// 注意2被加入了两次
s.size // 2

13.2 set的方法
1、add(value) :添加某个值,返回Set结构本身。
2、delete(value) :删除某个值,返回一个布尔值,表示删除是否成功。
3、has(value) :返回一个布尔值,表示该值是否为Set的成员。
4、clear() :清除所有成员,没有返回值。
let s = new Set();
s.add(1).add(2).add(2);
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false

13.3 Array.from方法可以将Set结构转为数组:
var items = new Set([1, 2, 3, 4, 5]);
var array = Array.from(items);

13.4 四个遍历方法
keys() :返回一个键名的遍历器
values() :返回一个键值的遍历器
entries() :返回一个键值对的遍历器
forEach() :使用回调函数遍历每个成员

//Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以key方法和value方法的行为完全一致。
let set = new Set(['red', 'green', 'blue']);
//keys()
for ( let item of set.keys() ){
// red green blue
console.log(item);
}
//values()
for ( let item of set.values() ){
// red green blue
console.log(item);
}

//entries()
for ( let item of set.entries() ){
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
console.log(item);
}
//forEach()
set.forEach(function(item){
// red green blue
console.log(item);
})

14: WeakSet 数据结构
//WeakSet和Set一样都不存储重复的元素,但有一些不同点,WeakSet的成员只能是对象,而不能是其他类型的值。
14.1 原型方法
WeakSet.prototype.add(value): 向WeakSet实例添加一个新成员。
WeakSet.prototype.delete(value):清除WeakSet实例的指定成员。
WeakSet.prototype.has(value): 返回一个布尔值,表示某个值是否在

var ws = new WeakSet();
var obj = {};
var foo = {};
ws.add(window);
ws.add(obj);
ws.has(window); // true
ws.has(foo); // false
ws.delete(window);
ws.has(window); // false

14.2 WeakSet没有size属性,没有办法遍历它的成员。
ws.size // undefined
ws.forEach // undefined
ws.forEach(
function(item){ console.log('WeakSet has ' + item)}
)//undefined is not a function

15:Map 数据结构
15.1 说明
Map 是一个“超对象”,其 key 除了可以是 String 类型之外,还可以为其他类型(如:对象)
var m = new Map();
o = {p: "Hello World"};
m.set(o, "content")
console.log(m.get(o))// "content"

15.2 set()方法
//set()方法返回的是Map本身,因此可以采用链式写法
let map = new Map().set(1, 'a').set(2, 'b').set(3, 'c');
console.log(map);

15.3 has()和delete()
var m = new Map();
m.set("edition", 6) // 键是字符串
m.set(262, "standard") // 键是数值
m.set(undefined, "nah") // 键是undefined
var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") // 键是函数

m.has("edition") // true
m.has("years") // false
m.has(262) // true
m.has(undefined) // true
m.has(hello) // true

15.4 size属性和clear方法
let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

15.5 历遍方法
let map = new Map([
['F', 'no'],
['T', 'yes'],
]);

15.5.1 keys():返回键名的遍历器。
for (let key of map.keys()) {
// "F", "T"
console.log(key);
}

15.5.2 values():返回键值的遍历器。
for (let value of map.values()) {
// "no", "yes"
console.log(value);
}

15.5.3 entries():返回所有成员的遍历器。
for (let item of map.entries()) {
// "F" "no"
// "T" "yes"
console.log(item[0], item[1]);
}
//entries():返回所有成员的遍历器。
for (let [key, value] of map.entries()) {
// "F" "no"
// "T" "yes"
console.log(key, value);
}
//等同于entries()
for (let [key, value] of map) {
// "F" "no"
// "T" "yes"
console.log(key, value);
}

15.5.4 forEach方法,与数组的forEach方法类似。
map.forEach(function(value, key, map)) {
console.log("Key: %s, Value: %s", key, value);
};

15.6 结合使用扩展运算符(...)
//将二维数据做参数,生成键值对
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three']
]);
console.log([...map.keys()]); // [1, 2, 3]
console.log([...map.values()]); // ['one', 'two', 'three']
console.log([...map.entries()]); // [[1,'one'], [2, 'two'], [3, 'three']]
console.log([...map]); // [[1,'one'], [2, 'two'], [3, 'three']]

16:WeakMap 数据结构
WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受原始类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。set()和get()分别用来添加数据和获取数据:

var map = new WeakMap(),
element = document.querySelector(".element");
map.set(element, "Original");

// 下面就可以使用了
var value = map.get(element);
console.log(value); // "Original"

WeakMap与Map在API上的区别主要是两个:
1:是没有遍历操作(即没有key()、values()和entries()方法),也没有size属性;
2:是无法清空,即不支持clear方法。
3:WeakMap只有四个方法可用:get()、set()、has()、delete()。

17:Iterator(遍历器)
17.1 说明
Iterator的作用有三个:
1:是为各种数据结构,提供一个统一的、简便的访问接口;
2:是使得数据结构的成员能够按某种次序排列;
3:是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator的遍历过程是这样的。
创建一个指针,指向当前数据结构的起始位置。也就是说,遍历器的返回值是一个指针对象。
第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回当前成员的信息,具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

17.2 原生具备Iterator接口的数据结构
//在ES6中,可迭代数据结构(比如数组)都必须实现一个名为Symbol.iterator的方法,该方法返回一个该结构元素的迭代器。注意,Symbol.iterator是一个Symbol,Symbol是ES6新加入的原始值类型。
//下面代码中,变量arr是一个数组,原生就具有遍历器接口,部署在arr的Symbol.iterator属性上面。所以,调用这个属性,就得到遍历器。
Array 数组
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
var a = iter.next();
console.log(a.value +" "+ a.done);// { value: 'a', done: false }
var a = iter.next();
console.log(a.value +" "+ a.done);// { value: 'b', done: false }
var a = iter.next();
console.log(a.value +" "+ a.done);// { value: 'c', done: false }
var a = iter.next();
console.log(a.value +" "+ a.done);// { value: undefined, done: true }

String 字符串
var someString = "hi";
typeof someString[Symbol.iterator];// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
//上面代码中,调用Symbol.iterator方法返回一个遍历器,在这个遍历器上可以调用next方法,实现对于字符串的遍历。可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的

17.3 调用默认Iterator接口的场合
17.3.1 对数组和Set结构进行解构赋值时,会默认调用iterator接口。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;// x='a'; y='b'
let [first, ...rest] = set;// first='a'; rest=['b','c'];

17.3.2 扩展运算符(...)也会调用默认的iterator接口
var str = 'hello';
[...str] // ['h','e','l','l','o']

let arr = ['b', 'c'];
['a', ...arr, 'd']// ['a', 'b', 'c', 'd']

17.3.3 其他场合
yield*
Array.from()
Map(), Set(), WeakMap(), WeakSet()
Promise.all(), Promise.race()

17.4 Symbol.iterator方法的最简单实现
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]

// 或者采用下面的简洁写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
// hello
// world

17.5 return方法
遍历器返回的指针对象除了具有next方法,还可以具有return方法和throw方法。其中,next方法是必须部署的,return方法和throw方法是否部署是可选的。

return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。

18:Generator
18.1 说明
Generator函数是一个函数的内部状态的遍历器(也就是说,Generator函数是一个状态机)。
Generator函数是一个普通函数,但是有两个特征。
1:function命令与函数名之间有一个星号;
2:函数体内部使用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 }

18.2 throw()方法
//Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw('出错了');// 出错了
// 上面代码的最后一行,Generator 函数体外,使用指针对象的 throw 方法抛出的错误,可以被函数体内的 try ... catch 代码块捕获。这意味着,出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的。

18.3 for...of循环
//for...of循环可以自动遍历Generator函数,且此时不再需要调用next方法。
function *foo() {
yield 1; yield 2; yield 3; yield 4; yield 5;
return 6;
}
for (let v of foo()) {
// 1 2 3 4 5
console.log(v);
}

18.4 yield*语句
如果yield命令后面跟的是一个遍历器,需要在yield命令后面加上星号,表明它返回的是一个遍历器。这被称为yield*语句。其实yield关键字就是以一种更直观、便捷的方式让我们创建用于遍历有限序列集合的迭代器,而yield则用于将生成器函数的代码切片作为有限序列集合的元素(元素的类型为指令+数据,而不仅仅是数据而已)。下面我们一起看看yield关键字是怎样对代码切片的吧!

// 定义生成器函数
function *enumerable(msg){
console.log(msg)
var msg1 = yield msg + ' after '
console.log(msg1)
var msg2 = yield msg1 + ' after'
console.log(msg2 + ' over')
}
//上述代码最终会被解析为下面的代码:
var enumerable = function(msg){
var state = -1
return {
next: function(val){
switch(++state){
case 0:
console.log(msg + ' after')
break
case 1:
var msg1 = val
console.log(msg1 + ' after')
break
case 2:
var msg2 = val
console.log(msg2 + ' over')
break
}
}
}
}

18.5 作为对象属性的Generator函数
//如果一个对象的属性是Generator函数,可以简写成下面的形式。
let obj = {
* myGeneratorMethod() {
//···
}
};
//上面代码中,myGeneratorMethod属性前面有一个星号,表示这个属性是一个Generator函数。
//它的完整形式如下,与上面的写法是等价的。
let obj = {
myGeneratorMethod: function* () {
// ···
}
};

19: Promise
19.1 说明
所谓Promise,就是一个对象,用来传递异步操作的消息。
Promise对象有以下两个特点:

对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:
Pending(进行中)、
Resolved(已完成,又称Fulfilled)
Rejected(已失败)。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点:
首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
第三,当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

19.1 基本用法:
Promise对象是一个构造函数,用来生成Promise实例

//创建promise
var promise = new Promise(function(resolve, reject) {
// 进行一些异步或耗时操作
if ( /*如果成功 */ ) {
resolve("Stuff worked!");
} else {
reject(Error("It broke"));
}
});
//绑定处理程序
promise.then(function(result) {
//promise成功的话会执行这里
console.log(result); // "Stuff worked!"
}, function(err) {
//promise失败会执行这里
console.log(err); // Error: "It broke"
});

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

19.2 then方法
Promise实例具有then方法,也就是说,then方法是定义在原型对象,作用是为Promise实例添加状态改变时的回调函数。

then方法两个参数:
Resolved状态的回调函数;
Rejected状态的回调函数(可选)。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

19.3 指定发生错误时的回调函数
Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

getJSON("/posts.json").then(function(posts) {
// ...
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
getJSON方法返回一个Promise对象,如果该对象状态变为Resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为Rejected,就会调用catch方法指定的回调函数,处理这个错误。

var promise = new Promise(function(resolve, reject) {
throw new Error('test')
});
promise.catch(function(error) { console.log(error) });
// Error: test
上面代码中,Promise抛出一个错误,就被catch方法指定的回调函数捕获。

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function(comments) {
// some code
}).catch(function(error) {
// 处理前面三个Promise产生的错误
});
上面代码中,一共有三个Promise对象:一个由getJSON产生,两个由then产生。它们之中任何一个抛出的错误,都会被最后一个catch捕获。

19.4 Promise.all()方法
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例。(Promise.all方法的参数不一定是数组,但是必须具有iterator接口,且返回的每个成员都是Promise实例。)

p的状态由p1、p2、p3决定,分成两种情况。
1:只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
2:只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
return getJSON("/post/" + id + ".json");
});

Promise.all(promises).then(function(posts) {
// ...
}).catch(function(reason){
// ...
});

19.5 Promise.race()方法
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.race([p1,p2,p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。

如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

19.6 Promise.resolve()方法
有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
如果Promise.resolve方法的参数,不是具有then方法的对象(又称thenable对象),则返回一个新的Promise对象,且它的状态为Resolved。
var p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
由于字符串Hello不属于异步操作(判断方法是它不是具有then方法的对象),返回Promise实例的状态从一生成就是Resolved,所以回调函数会立即执行。

20:async函数
async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的一种方法。
async函数,就是下面这样。
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
async函数对Generator函数的改进,体现在以下三点。
内置执行器。Generator函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
var result = asyncReadFile();
更好的语义。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
更广的适用性。co函数库约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以跟Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

21:class
21.1 描述
ES6引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
//定义类
class Point {
//构造方法
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
//this关键字则代表实例对象
return '('+this.x+', '+this.y+')';
}
}

//实例对象
var point = new Point(2, 3);

constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
class表达式,与函数一样,Class也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};

21.2 继承
//Class之间可以通过extends关键字,实现继承。子类会继承父类的属性和方法。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}

class ColorPoint extends Point {
constructor(x, y, color) {
//this.color = color;
//子类的constructor方法没有调用super之前,就使用this关键字会报错,而放在super方法之后就是正确的。
super(x, y);
this.color = color; // 正确
}
}
//ColorPoint继承了父类Point,但是它的构造函数必须调用super方法。
let cp = new ColorPoint(25, 8, 'green');
console.log(cp instanceof ColorPoint); // true
console.log(cp instanceof Point); // true

21.3 (get)(set ) 函数
//在Class内部可以使用get和set关键字,对某个属性设置存值函数和取值函数。
class MyClass {
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 123; // 调用了set prop(123) 方法;显示:setter: 123
inst.prop // 调用了“get prop()” 方法 ;返回'getter'

21.4 Class的Generator方法
//如果某个方法之前加上星号(*),就表示该方法是一个Generator函数。
class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
//helloworld
console.log(x);
}
//上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。

21.5 Class的静态方法 static关键字
// 1:直接通过类来调用,这就称为“静态方法”。
// 2:该方法不会被实例继承
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod() // 报错:该方法不会被实例继承
//父类的静态方法,可以被子类继承。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod(); // 'hello'
//父类的静态方法,可以被子类覆盖
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
console.log(Bar.classMethod());// hello, too

21.6 new.target属性
//new是从构造函数生成实例的命令。ES6为new命令引入了一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}

// 另一种写法
function Person(name) {
if (new.target === Person) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}
var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三'); // 报错
// 上面代码确保构造函数只能通过new命令调用。
// Class内部调用new.target,返回当前Class。
// 子类继承父类时,new.target会返回子类。

22: export,import 命令
22.1 export 命令
//export命令用于用户自定义模块,规定对外接口;
//profile.js文件,保存了用户信息,用export命令对外部输出了三个变量。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};

22.2 export default 命令
// export-default.js
export default function () {
console.log('foo');
}
//export-default.js,它的默认输出是一个函数。加载该模块时,import命令可以为该匿名函数指定任意名字。
// import-default.js import命令后面,不使用大括号
import customName from './export-default';
customName(); // 'foo'

22.3 import 命令
//import命令就用于加载profile.js文件,并从中输入变量。import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。
import {firstName, lastName, year} from './profile';
function sfirsetHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
//重新取一个名字,import语句中要使用as关键字, ES6支持多重加载,即所加载的模块中又加载其他模块。
import { lastName as surname } from './profile';

22.4 export 输出方法或类
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}

// main.js
//写法一
import { area, circumference } from 'circle';
console.log("圆面积:" + area(4));
console.log("圆周长:" + circumference(14));
//另一种写法是整体输入。
import * as circle from 'circle';
console.log("圆面积:" + circle.area(4));
console.log("圆周长:" + circle.circumference(14));

22.5 module 命令
//module可以取代import语句,达到整体输入模块的作用。
// main.js
module circle from 'circle';
console.log("圆面积:" + circle.area(4));
console.log("圆周长:" + circle.circumference(14));

22.1 模块的继承

//假设有一个circleplus模块,继承了circle模块。

// circleplus.js
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
//这时,也可以将circle的属性或方法,改名后再输出。
export { area as circleArea } from 'circle';

//加载上面模块的写法如下。代码中的"import exp"表示,将circleplus模块的默认方法加载为exp方法。
// main.js
module math from "circleplus";
import exp from "circleplus";
console.log(exp(math.pi));

23: Symbol 类型
//ES6引入了一种新的原始数据类型Symbol,表示独一无二的ID。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
let s = Symbol();
typeof s;// "symbol"

24:内置代理Proxy 内置的一个代理工具,使用他可以在对象处理上加一层屏障:
//S6原生提供Proxy构造函数,用来生成Proxy实例。
var plain = {
name : "hubwiz"
};
var proxy = new Proxy(plain, {
get: function(target, property) {
return property in target ? target[property] : "汇智网";
}
});
proxy.name // "hubwiz"
proxy.title // "汇智网"

</script></head> <body> </body> </html>