es6 学习笔记

1.变量声明const和let

let的作用域实在它所在的代码块,但不会被提升到当前函数的最顶部

const 声明的是常量,设置完成以后不能更改 如果const是对象,对象所包含的值是可以被修改的

2.变量的结构赋值

// 2.1 数组的解构赋值
// ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

// 以前 为变量赋值,只能直接指定值
let a = 1;
let b = 2;
let c = 3;
// 现在ES6允许写成下面这样
let [a,b,c] = [1,2,3];
// 上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

// 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []	

// 解构赋值允许指定默认值。
let [foo = true] = [];
foo // true

// 2.2 对象的解构赋值

let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

// 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined

// 也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

// 2.3 字符串的解构赋值

const [a,b,c,d,e] = "hello";
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

// 类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';
len // 5

// 2.4 变量的解构赋值的用途
// (1) 变换变量的值
let x = 1;
let y = 2;
[x,y] = [y,x]

// 上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰。

// (2) 从函数返回多个值

// 返回一个数组

// 函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取这些值就非常方便。

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象

function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

// (3) 函数参数的定义

// 解构赋值可以方便地将一组参数与变量名对应起来。

// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);

// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});

// (4) 提取 JSON 数据

// 解构赋值对提取 JSON 对象中的数据,尤其有用。

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

// (5)函数参数的默认值
// 指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这的语句。

// (6)遍历 Map 结构

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world

// (7)输入模块的指定方法
// 加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰

const { SourceMapConsumer, SourceNode } = require("source-map");

3.字符串的扩展

// 3.1 es6模板字符简直是开发者的福音啊,解决了ES5在字符串功能上的痛点。第一个用途,基本的符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定。

// es5
var name = 'lux';
console.log('hello' + name);
// es6
const name2 = 'lux';
console.log(`hello${name2}`);

// 3.2 多行字符串或者字符串一行行拼接,ES6反引号(``)直接搞定。

const template = `<div>hello world!</div><span>你好</span>`;

$("body").html(template);

// 3.3 ES6 字符串方法 

	// 1 includes; 判断是否包含然后直接返回布尔值
	const str = 'lokka'
	console.log(str.includes('k')); // true

	// 2 repeat 字符串重复n次
	const html = 'he'
	console.log(html.repeat(3))
	//如果你带入小数, Math.floor(num) 来处理
	// s.repeat(3.1) 或者 s.repeat(3.9) 都当做成 s.repeat(3) 来处理

	// 3 startsWith 和 endsWith 判断是否以 给定文本 开始或者结束

	const str =  'hello world!'
 	   console.log(str.startsWith('hello')) // true
 	   console.log(str.endsWith('!')) // true

 	// 4 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
 	let greeting = `\`Yo\` World!`;

 	// 5 模板字符串中嵌入变量,需要将变量名写在${}之中。

 	function authorize(user, action) {
 		if (!user.hasPrivilege(action)) {
 	 		throw new Error(
 	 		  // 传统写法为
 	 		  // 'User '
 	 		  // + user.name
 	 		  // + ' is not authorized to do '
 	 		  // + action
 	 		  // + '.'
 	 		  `User ${user.name} is not authorized to do ${action}.`);
 			}
	}	

	// 6 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
	let x = 1;
	let y = 2;

	`${x} + ${y} = ${x + y}`
	// "1 + 2 = 3"

	`${x} + ${y * 2} = ${x + y * 2}`
	// "1 + 4 = 5"

	let obj = {x: 1, y: 2};
	`${obj.x + obj.y}`
	"3"

	// 7 模板字符串之中还能调用函数。
	function fn() {
	  return "Hello World";
	}

	`foo ${fn()} bar`
	// foo Hello World bar

	// 8 如果模板字符串中的变量没有声明,将报错。
	// 变量place没有声明
	let msg = `Hello, ${place}`;
	// 报错

4.数值的扩展


// 4.1 二进制和八进制表示法 

0b111110111 === 503 // true
0o767 === 503 // true

// 4.1.1 如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。

Number('0b111')  // 7
Number('0o10')  // 8

// 4.2 Number.isFinite(), Number.isNaN() 

// Number.isFinite()用来检查一个数值是否为有限的(finite),即不是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.isNaN(9/NaN) // true
Number.isNaN('true' / 0) // true
Number.isNaN('true' / 'true') // true

5.函数的扩展

// 5.1 函数默认参数
function action(num = 200) {
	console.log(num)
}
action(0) // 0
action() //200
action(300) //300

// 5.2 箭头函数
// ES6很有意思的一部分就是函数的快捷写法。也就是箭头函数。

// 箭头函数最直观的三个特点。

// 不需要 function 关键字来创建函数
// 省略 return 关键字
// 继承当前上下文的 this 关键字

var getTime = x => x+1;

console.log(getTime(1));

// 等同于
function getTime(x){
	return x+1;
}
console.log(getTime(1));

// 如果箭头函数不需要参数或需要多个参数,就是用一个圆括号代表参数部分

var f = () => 5;

// 等同于
var f = function () {return 5};

console.log(f());

// 如果参数大于一个就需要用小括号括起来,参数之间用,隔开
// 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句回。

var sum = (num1, num2) => { let num = num1 + num2; return num; }

console.log(sum(1,2));

// 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,则报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

// 使用注意点
// 箭头函数有几个使用注意点。
// 
// (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
// 
// (2)不可以当作构造函数,也就是说,不可以使用new命令// ,否则会抛出一个错误。
// 
// (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 // rest	 参替。
// 
// (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
// 
// 上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

6. 数组的扩展

// 6.1. Array.from

// Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

// 下面是一个类似数组的对象,Array.from将它转为真正的数组。

let arrayLike = {
	'0': 'a',
	'1': 'b',
	'2': 'c',
	length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

// 6.2. Array.of()

// Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

// 6.3. 数组实例的 find() 和 findIndex() 

// 数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件成员,则返回undefined。

[1, 4, -5, 10].find((n) => n < 0)
// -5

// 上面代码找出数组中第一个小于 0 的成员。

[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

// 上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

// 数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

// 这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26

// 上面的代码中,find函数接收了第二个参数person对象,回调函数中的this对象指向person对象。

7. 对象的扩展

// 7.1 ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。例如:
    function people(name, age) {
    return {
        name: name,
        age: age
    };
}
// 7.2 键值对重名,ES6可以简写如下:
    function people(name, age) {
    return {
        name,
        age
    };
}
// 7.3 ES6 同样改进了为对象字面量方法赋值的语法。ES5为对象添加方法:
 const people = {
    name: 'lux',
    getName: function() {
        console.log(this.name)
    }
}
// 7.4 ES6通过省略冒号与 function 关键字,将这个语法变得更简洁
 const people = {
    name: 'lux',
    getName () {
        console.log(this.name)
    }
}
// ES6 对象提供了 Object.assign()这个方法来实现浅复制。Object.assign() 可以把任意多个对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。第一参数即为目标对象。在实际项目中,们为了不改变源对象。一般会把目标对象传为{}
const objA = { name: 'cc', age: 18 }
const objB = { address: 'beijing' }
const objC = {} // 这个为目标对象
const obj = Object.assign(objC, objA, objB)

// 我们将 objA objB objC obj 分别输出看看
console.log(objA)   // { name: 'cc', age: 18 }
console.log(objB) // { address: 'beijing' }
console.log(objC) // { name: 'cc', age: 18, address: 'beijing' }
console.log(obj) // { name: 'cc', age: 18, address: 'beijing' }

// 是的,目标对象ObjC的值被改变了。
// so,如果objC也是你的一个源对象的话。请在objC前面填在一个目标对象{}
Object.assign({}, objC, objA, objB)

8.Symbol

// 8.1 symbol是通过symbol函数生成的 凡是symbol类型都是独一无二的 不会与其他属性名冲突
let s = Symbol();
typeof s
// "symbol"

// Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的据类型。

// Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

// 上面代码中,s1和s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
// Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不等的。
// 8.2 作为属性名的 Symbol 不能用点运算符
let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

9.Spread Operator 展开运算符

// ES6中另外一个好玩的特性就是Spread Operator 也是三个点儿...接下来就展示一下它的用途组对象或者数组

//一、数组的扩展

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
// 9.1.该运算符主要用于函数调用function add(x, y) {
  return x + y;
}

const numbers = [4, 38];
add(...numbers) // 42

// 9.2.替代函数的 apply 方法

// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

// 9.3.扩展运算符的应用

// Ⅰ.合并数组

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

// 都是浅拷贝,如果修改了原数组的成员,会同步反映到新数组

// Ⅱ. 与解构赋值结合

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

// 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

// 二、对象的扩展

// 1. 解构赋值

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

// 2. 扩展运算符

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

// 对象的扩展运算符等同于使用Object.assign()方法。

let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

// 扩展运算符可以用于合并两个对象

let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);

10.import 和 export

	// import导入模块、export导出模块

	// 导入
	import people from './example'
	
	// 可以省略掉from直接导入
	import './example'
	
	// ES6模块
	import { stat, exists, readFile } from 'fs';


	//有一种特殊情况,即允许你将整个模块当作单一对象进行导入
	//该模块的所有导出都会作为对象的属性存在
	import * as example from "./example.js"
	console.log(example.name)
	console.log(example.age)
	console.log(example.getName())
	
	// 导入部分
	import {name, age} from './example'
	
	// 导出默认, 有且只有一个默认
	export default App
	
	// 部分导出
	export class App extend Component {};

// -----------------------------------------------------------  

// 模块的整体加载

// 除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加在这个对象上面。

// 下面是一个circle.js文件,它输出两个方法area和circumference。

// 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));	

// ----------------------------------------------------------- 		

// export default 命令

// 从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。

// 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模指定默认输出。		

// export-default.js
export default function () {
		console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

// 上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。

// export default命令用在非匿名函数前,也是可以的。

// export-default.js
export default function foo() {
  console.log('foo');
}

// 或者写成

function foo() {
  console.log('foo');
}

export default foo;

// -----------------------------------------------------------  

// 1.当用export default people导出时,就用 import people 导入(不带大括号)

// 2.一个文件里,有且只能有一个export default。但可以有多个export。

// 3.当用export name 时,就用import { name }导入(记得带上大括号)

// 4.当一个文件里,既有一个export default people, 又有多个export name 或者 // export age时,导入就用 import people, { name, age } 

// 5.当一个文件里出现n多个 export // 导出很多模块,导入时除了一个一个导入,也可以import * as example

11.set和map 数据结构

// ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

// Set 本身是一个构造函数,用来生成 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
// 上面代码通过add方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值
// set具体方法看阮一峰es6 http://peiyu.ynet.com/es6tutorial/#docs/set-map

// Map 
// ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比Object 更合适。

// set具体方法看阮一峰es6 http://peiyu.ynet.com/es6tutorial/#docs/set-map

12.Proxy

// Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(metaprogramming),即对编程语言进行编程。
var proxy = new Proxy({}, {
get: function(target, property) {
  	return 35;
  }
});

proxy.time // 35
proxy.name // 35
proxy.title // 35

13.Reflect

// Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目有这样几个

14.promise对象

// 在promise之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过Promise机制,平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,大的降低了代码耦合性而提高了程序的可扩展性。
// 说白了就是用同步的方式去写异步代码。
// Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作可以用同样的方法进行处理
// Promise对象有以下两个特点:

// (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示他手段无法改变。
// (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,果你错过了它,再去监听,是得不到结果的。
	
// 基本用法
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (异步操作成功){
    resolve(value);
  } else {
    reject(error);
  }
});
	
// resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失时调用,并将异步操作报出的错误,作为参数传递出去。

// Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});	

// 下面是一个Promise对象的简单例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}

timeout(100).then((value) => {
  console.log(value);
});	
let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved
// 上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
// 14.1 Promise.prototype.then()

// 它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
// then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链写法,即then方法后面再调用另一个then方法。
	getJSON("/posts.json").then(function(json) {
	  return json.post;
	}).then(function(post) {
	  // ...
	});
// 上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作参数,传入第二个回调函数。
// 采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生化,才会被调用。	
// 14.2 Promise.prototype.catch()
// Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回函数。

getJSON("/posts.json").then(function(posts) {
  // ...
}).catch(function(error) {
  // 处理 getJSON 和 前一个回调函数运行时发生的错误
  console.log('发生错误!', error);
});
// 上面代码中,getJSON方法返回一个Promise对象,如果该对象状态变为Resolved,则会调用then法指定的回调函数;如果异步操作抛出错误,状态就会变为Rejected,就会调用catch方法指定的回调数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
p.then((val) => console.log("fulfilled:", val))
		.catch((err) => console.log("rejected:", err));
// 等同于
p.then((val) => console.log("fulfilled:", val))
	.then(null, (err) => console.log("rejected:", err));
// .catch()的作用是捕获Promise的错误,与then()的rejected回调作用几乎一致。但是由Promise的抛错具有冒泡性质,能够不断传递,这样就能够在下一个catch()中统一处理这些错误。同catch()也能够捕获then()中抛出的错误,所以建议不要使用then()的rejected回调,而是统一使catch()来处理错误
promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
)
	
// 下面代码中,promise抛出一个错误,就被catch方法指定的回调函数捕获。下面这三种写法是等的
const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test
// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) { // 解决 拒绝
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});
// 比较上面两种写法,可以发现reject方法的作用,等同于抛出错误。
// 如果 Promise 状态已经变成resolved,再抛出错误是无效的。
// 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捕获。
// 一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使catch方法。
// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });
// 13.3 Promise.prototype.finally()
  Promise.prototype.finally()
  promise
  .then(result => {···})
  .catch(error => {···})
  .finally(() => {···});
// Promise.all() 
// Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
// Promise.race()
// Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
// Promise.resolve()
// 有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用
// const jsPromise = Promise.resolve($.ajax('/whatever.json'));
// 上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。
// Promise.resolve等价于下面的写法。
// romise.resolve('foo')
// 等价于
// new Promise(resolve => resolve('foo'))
// Promise.resolve方法的参数分成四种情况。
//(1)参数是一个 Promise 实例
/// 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改		  /、原封不动地返回这实例。

//(2)参数是一个thenable对象

// thenable对象指的是具有then方法的对象,比如下面这个对象。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
// Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行		  thenable对象then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
// 上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved		  ,从而立即行最后那个then方法指定的回调函数,输出 42。

//(3)参数不是具有then方法的对象,或根本就不是对象

// 如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.		  resolve方法返一个新的 Promise 对象,状态为resolved。

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello
// 上面代码生成一个新的 Promise 对象的实例p。由于字符串Hello不属于异步操作		  (判断方是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是		  resolved,所以调函数会立即执行。Promise.resolve方法的参数		  ,会同时传给回调函数。

//(4)不带有任何参数

// Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

// 所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法		 。

const p = Promise.resolve();

p.then(function () {
  // ...
});
//上面代码的变量p就是一个 Promise 对象。

// 需要注意的是,立即resolve的 Promise 对象,是在本轮“事件循环”(event loop		  )的结时,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three
// 上面代码中,setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()		在本“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。
// Promise.reject()
// Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

15. Iterator 和 for…of 循环

// JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

// 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有员)。

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

// Iterator 的遍历过程是这样的。

// (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是个指针对象。

// (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

// (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

// (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

// 15.1 for...of 循环 § ⇧
// ES6 借鉴 C++、Java、C# 和 Python 语言,引入了for...of循环,作为遍历所有数据结构的一的方法。

// 一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator法。

// for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

// 15.2 Set 和 Map 结构
// Set 和 Map 结构也原生具有 Iterator 接口,可以直接使用for...of循环。

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262
// 上面代码演示了如何遍历 Set 结构和 Map 结构。值得注意的地方有两个,首先,遍历的顺序是	按照各个成员被添加进数据结构的顺序。其次,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

16.Generator 函数的语法

// 生成器(generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就比普通的function多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yiel后暂停。

// 这里生活中有一个比较形象的例子。咱们到银行办理业务时候都得向大厅的机器取一张排队号。拿到你的排队号,机器并不会自动为你再出下一张票。也就是说取票机“暂停”住了,直到下一个人次唤起才会继续吐票。

// OK。说说迭代器。当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止供值。继续用刚刚取票的例子,每张排队号就是这里的value,打印票的纸是否用完就这是这里done。
	
// 生成器
function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}
	
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();

console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
// 那生成器和迭代器又有什么用处呢?

// 围绕着生成器的许多兴奋点都与异步编程直接相关。异步调用对于我们来说是很困难的事,我们的数并不会等待异步调用完再执行,你可能会想到用回调函数,(当然还有其他方案比如Promise比Async/await)。

// 生成器可以让我们的代码进行等待。就不用嵌套的回调函数。使用generator可以确保当异步调用我们的generator函数运行一下行代码之前完成时暂停函数的执行。

// 那么问题来了,咱们也不能手动一直调用next()方法,你需要一个能够调用生成器并启动迭代器的法。就像这样子的
	
function run(taskDef) { //taskDef即一个生成器函数

    // 创建迭代器,让它在别处可用
    let task = taskDef();

    // 启动任务
    let result = task.next();

    // 递归使用函数来保持对 next() 的调用
    function step() {

        // 如果还有更多要做的
        if (!result.done) {
            result = task.next();
            step();
        }
    }

    // 开始处理过程
    step();

}
// 生成器与迭代器最有趣、最令人激动的方面,或许就是可创建外观清晰的异步操作代码。你不必到使用回调函数,而是可以建立貌似同步的代码,但实际上却使用 yield 来等待异步操作结束。

// for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next法。
	
function* foo() {
	  yield 1;
	  yield 2;
	  yield 3;
	  yield 4;
	  yield 5;
	  return 6;
	}
	
for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5
  // 16.1 Generator.prototype.throw()  	
// Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator 函数体内捕获。
var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获', e);
  }
};

var i = g();
i.next();

try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b

// 上面代码中,遍历器对象i连续抛出两个错误。第一个错误被 Generator 函数体内的catch语句获。i第二次抛出错误,由于 Generator 函数内部的catch语句已经执行过了,不会再捕捉到这个错了,所以这个错误就被抛出了 Generator 函数体,被函数体外的catch语句捕获。
// throw方法可以接受一个参数,该参数会被catch语句接收,建议抛出Error对象的实例。
var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log(e);
  }
};

var i = g();
i.next();
i.throw(new Error('出错了!'));
// Error: 出错了!(…)

// 16.2 Generator.prototype.return() 
// Generator 函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator 函数。

17 Generator 函数的异步应用

// 17.1 协程的 Generator 函数实现

// Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

// 整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停地方,都用yield语句注明。Generator 函数的执行方法如下。

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

// 上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器)g。这是 Generator 数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x +2为止。

// next()还可以传值
var g = gen(1);
g.next(); // { value: 3, done: false }
g.next(5);// { value: 5, done: true }
function* gen(x){
    try {
      var y = yield x + 2;
    } catch (e){
      console.log(e);
    }
    return y;
  }
  
  var g = gen(1);
  console.log(g.next());// 3
  g.throw('出错了');

  throw(); // 出错的代码与处理错误的代码,实现了时间和空间上的分离,这对于异步编程无疑是很重要的

18. async函数

// 一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield换成await,仅此而已。

19. class 的基本语法

// 1.定义类 通过class关键字,可以定义类(就是在构造函数的原型上定义方法 ===function.prototype.方法())
class point {
    constructor(a,b){
        this.a = a;
        this.b = b;
    }

    toString(){
        return this.a + this.b;
    }
}
var num = new point(1,2);
console.log(num.toString());

// 2.constructor 方法
	//constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Point {
}

// 等同于
class Point {
  constructor() {}
}

// 3. 取值函数(getter)和存值函数(setter)

// 与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数拦截该属性的存取行为。

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'


// 4. Class 表达式

// 与函数一样,类也可以使用表达式的形式定义。

const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};

// 上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是Me,但是Me只在 Class 的部可用,指代当前类。在 Class 外部,这个类只能用MyClass引用。

let inst = new MyClass();
inst.getClassName() // Me
Me.name // ReferenceError: Me is not defined

// 上面代码表示,Me只在 Class 内部有定义。

// 如果类的内部没用到的话,可以省略Me,也就是可以写成下面的形式。

const MyClass = class { /* ... */ };
// 采用 Class 表达式,可以写出立即执行的 Class。

let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}('张三');
		
person.sayName(); // "张三"
// 上面代码中,person是一个立即执行的类的实例。
// 3.class的静态方法
class Foo {
  static classMethod() { 
  // 加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
    return 'hello';
  }
}

Foo.classMethod()

20. class的继承

	
// super()方法继承    
class point {
    constructor(a,b){
        this.a = 4;
        this.b = 2;
    }

    toString(){
        // alert(1);
        return this.a + this.b;
    }
}
	
class colorpoint extends point {
    constructor(a,b){
        super(a,b);
        // console.log(a)
    }
    toNum(){
        return this.a * this.b
    }
}

let n = new colorpoint();
console.log(n.toNum());

21. decorator修饰器

// 21.1 类的修饰
@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true

// @testable 定义修饰器 它修改了MyTestableClass,加上了isTestable属性, target参数是MyTestableClass

// 21.2 类的属性的修饰

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}
	
function readonly(target, name, descriptor){ // 1.类的原型对象 2. 修饰的属性名 3. 属性的描述对象
  // descriptor对象原来的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  descriptor.writable = false;
  return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值