<!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 defined3: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) // false4.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'); // true4.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 lines4.4.3 标签模板
var a = 5; var b = 10; tag`Hello ${ a + b } world ${ a * b }`; tag,它是一个函数。整个表达式的返回值,就是tag函数处理模板字符串后的返回值 tag函数所有参数的实际值如下。 第一个参数:['Hello ', ' world '] 第二个参数: 15 第三个参数:50tag函数实际上以下面的形式调用: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) // false8:Math 新方法
Math.trunc():去除一个数的小数部分,返回整数部分。对于空值和无法截取整数的值,返回NaN Math.trunc(4.1) // 4 Math.trunc(-4.1) // -4 Math.trunc('ddd') // NaNMath.sign():判断一个数到底是正数、负数、还是零。返回五种值:如下
Math.sign(-5) // -1 Math.sign(5) // +1 Math.sign(0) // +0 Math.sign(-0) // -0 Math.sign('hubwiz'); // NaNMath.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); // 3Math.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 // 19.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); // 109.4 Array.findIndex() : 用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
let index = [1, 5, 10, 15].findIndex(function(value, index, arr) { return value > 9; }) console.log(index); // 29.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 // 213.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) // false13.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); // false14.2 WeakSet没有size属性,没有办法遍历它的成员。
ws.size // undefined ws.forEach // undefined ws.forEach( function(item){ console.log('WeakSet has ' + item)} )//undefined is not a function15: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) // true15.4 size属性和clear方法
let map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2 map.clear() map.size // 015.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 // world17.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); // true21.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, too21.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>