ES6基础总结

参考阮一峰的ES6文档(http://es6.ruanyifeng.com/#docs/let)

一:let 和 const 命令
1 let 命令

​ <1> 用于声明变量,但声明的变量只在当前代码块有效(块级作用域),即作用域在 { } 内
​ <2> 不存在变量提示,即:无预解析
​ <3> 暂时性死区,即:只要 **块级作用域 ** 中存在 let 命令,那么他所声明的变量就"绑定"到当前区域,不在受外部的影响,若 let 声明的变量在此之前使用,则会报错
​ <4> 不允许在同一个作用域中多次声明
​ <5> 【注:】ES5 只有函数作用域 和 全局作用域,没有块级作用域

var tem = 123;
if(true){
    console.log(tem); // 报错
    let tem = 0;
}

let a = a ; // 报错

let b = 0;
let b = 1; // 报错
2 函数声明

​ 【注】ES5 中 不允许块级作用域中声明函数,但为了兼容老代码,基本上所有的浏览器都无视了这一个规定

​ ES6 中 允许块级作用域中声明函数,但是函数的声明相当于 let 声明一样,只作用于当前的块级作用域,但是为了兼容老代码,所以ES6的规定中浏览器可以不遵守此规定,有自己的行为方式,所以一般函数的声明会像 var 一样会进行函数名的提升
​ 【注】ES6 中 块级作用域中函数的声明必须包含在 { } 中,如 if(true) f(){}; 会报错

3 const 命令

​ <1> 用于声明一个常量的值,一旦声明,常量的值就不能改变,若改变则会报错
​ <2> 声明时必须赋值,若只声明不赋值,就会报错
​ <3> 作用域与 let 相同,只存在于当前作用域中
​ <4> 没有预解析
​ <5> 存在暂时性死区的特性
​ <6> 同一个变量不能多次声明

本质:const实际上保证的是变量指向的那个内存地址所保存的值是不能改变的,所以:
当保存为基本类型时,内存地址保存的就是一个值,此时就为常量
当保存为复杂类型时,内存地址保存的就是一个地址,即:指针,他只能保证这个地址不能改变,但不能保证这个地址指向堆中的数据不能改变,所以,将对象保存为常量时需小心使用

const PI = 3.141592654;
console.log(PI);
PI = 3 ; // 报错

const a ; //报错

const b = [];
b.push('hello');  // 可执行
b.length = 10; // 可执行
b = ['aaa']  // 报错

// 如果想将对象冻结,需使用 Object.freeze() 方法  freeze:冻结
var c = Object.freeze({});
// 常规模式下,下面这行代码不起作用
// 严格模式下会报错
c.prop = 123;
6 顶层对象

​ 在 ES5 中 浏览器的顶层对象就是 window ,顶层对象的属性与全局变量时等价的
​ Node 里面,是 global
​ 在 ES6 中 ,var 和 function声明的变量依旧是顶层对象的属性,但 let const class 声明的全局变量不属于顶层对象的属性

二:变量的解构赋值
1 数组的解构赋值

解构:ES6中允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这称之为解构
数组的解构赋值是按照元素的顺序依次赋值

let [a,b,c] = [1,2,3]
	// a:1  b:2  c:3
let [,,a] = [1,2,3]
	// a:3
let [a,b] = [1,2,3]
	// a:1 b:2
let [a,...b] = [1,2,3,4]
	// a:1  b:[2,3,4]
	// ... 扩展运算符,将一个数组变成参数序列
let [a,b,...c] = [1]
	// a:1 b:undefined c:[]
let [a] = [];
	// a:undefined
let [a] = 1;
	// 报错

// 默认值
	//**注意:当一个数组成员严格等于( === )undefined时,默认值才会生效**
let [x=1] = [undefined];
	// x:1
let [x=1] = ['undefined'];
	// x:'undefined'
let [x=1] = [null];
	// x:null;
let [x=1] = [2];
	// x:2
2 对象的解构赋值

变量与对象的解构赋值是变量与属性同名,再进行赋值 例:let {a} = {a:1}
对象与对象的解构赋值是先找到同名的属性,再赋值给对应的变量 例:let {a:b} = {a:1} b=>1

let {x,y}={x:1,y:2};
	// x:1  y:2;
// 当变量名与属性名不一致时
let a = {x:1,y:2};
let {x:a,y:b}=a;
	// a:1  b:2

let {a:b} = {a:2};
	// b:2
	// a:报错,not undefined
// 对象与对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

// 例:
	const node = {loc: {start: {line: 1,column: 5}}};
    let { loc, loc: { start }, loc: { start: { line }} } = node;
         console.log(loc); 		{start:{line:1,column:5}}
         console.log(start);	{line:1,column:5}
         console.log(line);		1
	let { loc, loc: { start }, loc: { start: { line }} } = 
        {loc: {start: {line: 1,column: 5}}};

// 例2
	let obj = {p: ['Hello',{ y: 'World' }]};    
    let { p, p: [x, { y }] } = obj;
    	console.log(p);		['Hello',{ y: 'World' }]
    	console.log(x);		'Hello'
    	console.log(y);		'World'

// 例3
	let obj = {};
    let arr = [];
    ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
        console.log(obj);	{prop:123}
        console.log(arr);	[true]
	不加一对小括号时,js在行首遇到{}会解析成代码块,从而引发语法错误

// 有默认值时
	//**注意:当一个数组成员严格等于( === )undefined时,默认值才会生效**
	let {a,b=1}={a:1};
		// a:1  b:1
	let {x: y = 3} = {};
		// y:3  x:报错,not undefined
	let {x,y = 3} = {};
		// y:3  x:undefined
	let {foo: {bar}} = {baz: 'baz'};
		// 报错,因为foo是undefined,所以它的子对象就会报错

// 数组的解构赋值
	let arr = [1, 2, 3];
	let {0 : first, [arr.length - 1] : last} = arr;
	// 由于数组的本质是一个特殊的对象,所以  arr=>{0:1,1:2,2:3} ?
	console.log(first);
	console.log(last); 
3 字符串的解构赋值
const [a,b,c,d,e] = 'hello';
	// a:'h'  
	// b:'e'  
	// c:'l'  
	// d:'l' 
	// e:'o'

let {length:len} = 'hello'
	// len:5
	// 字符串是一个类数组,所以具有 length 属性
4 数值和布尔值的解构赋值

解构赋值的规则:只要等号右边的值不是对象或者数组,就先将其转换成对象,在进行赋值

let {toString:s} = 123;
	s === Number.prototype.toString // true
let {toString:s} = true
	s === Boolean.prototype.toString // true
	// 因为 数值和布尔 的包装对象都具有 toString 属性,所以 变量s 都会被赋值

let {toString:x} = undefined ; // 报错:TypeError
let {toString:x} = null ; // 报错:TypeError
5 函数的解构赋值
function xy([a,b]){
    return a+b;
}
xy([1,2]) ===> 3

let arr = [[1,2],[3,4]].map(([a,b])=>a+b);
	// .map()方法:数组中的每一个元素执行map方法中的函数或者方法,返回一个新的数组
	// arr ===> [3,7]

// 函数参数也可以使用默认值
// 变量默认值
function xy({x=0,y=0}={}){
    return x+y;
}
	xy({x:1,y:2}) ===> 3
	xy({x:1,a:2}) ===> 1
	xy({}) ===> 0
	xy() ===> 0

// 参数默认值
function move({x,y}={x:0,y:0}){
    return x+y;
}
	move({x:1,y:2}) ===> 3
	move({x:1}) ===> NaN 因为 y的值是undefined,所以最终结果为NaN
    move({}) ===> NaN
	// 注意:上述是为参数指定默认值,而不是变量
6 用途
// (1). 交换变量的值
	let x = 1;
	let y = 2;
	[x,y] = [y,x];

// (2). 函数返回多个值
    function xy(){
		return [1,2,3,4];
    }
	let [a,b,c,d] = xy(); 
	// a:1 b:2 c:3 d:4

// (3). 函数参数的定义
	function xy([x,y,z]){ ... }
    	xy([1,2,3])
    function xy({x,y,z}){ ... }
    	xy({z:1 , y:2 , x:3})
                         
// (4). 提取 JSON 数据
    let jsonData = {
		id:1,
        status:'OK',
        data:[111,222]
    }
    let {id,status,data:arr} = jsonData;
     // id:1  status:'OK  arr:[111,222]
                         
// (5). 函数参数的默认值
    let ajax = function(url,{
        async = true, // async 异步
        beforeSend = function(){},
        cache = true, // 隐藏
        complete = function(){}, // complete 完整的,完成
        crossDomain = false, // cross 交叉  domain 领域
        global = true, // 全球的,总体的
    } = {} ){ ... }
        
// (6). 遍历map解构
        const map = new Map([
        	[{a:1},1];  // 键是对象
        	['a',1]		// 键是字符串
        ]);
    /* Map()构造函数,是一个键值对的集合,其中键可以时任意类型
       集合是一个数组,其中数组中的每一个数组项都是Map实例的键和值*/
    map.set('first','hello');  	// 添加一组键值对
    map.get('first')		   	// 获取指定的属性值
    map.has('first')			// 判断是否存在某个属性,返回bool
    map.delete('first')			// 删除指定的属性,返回bool
    map.size;				   // 获取数组的长度
    map.clear()					// 清除所有
    // map 的遍历方法--返回一个迭代器,类似一个对象?
       .keys()	// 返回键名的遍历器
       .values()	// 返回键值的遍历器
       .entries()// 返回所有成员(键值对)的遍历器
       .forEach()// 遍历Map的所有成员
    // 例:
       for(let val of map.kes()){
       		console.lof(val);                    
       }
       for(let [key,value] of map.entries()){
       		console.lof(key+"==="+value);                    
       }  
       // 只想获得键名
       for(let [key] of map){
        	console.log(key);
       }
       // 只想获得值
       for(let [,val] of map){
            console.log(value);                 
       }      
// Set() 构造函数
三:字符串的扩展
1. 字符的 Unicode 表示法
// 可以以 \uxxxx 表示一个字符,其中 xxxx 时 Unicode码点
'\u{7A}' === 'z' 

2. codePointAt()
// 对于字节超出 Unicode 码点的字符,charAt()方法无法读取
// 所以提供了 codePointAt()方法来读取码点

3. String.fromCodePoint()
// 通过码点获取字符,可以识别编号大于 0xFFFF 的字符编码
String.fromCodePoint(0x20BB7);

4. 字符串的遍历器接口
// 使用 for of 遍历字符串,可以识别大于 0xFFFF 码点的字符
for(let code of 'foo'){
    console.log(code); // 'f'  'o'   'o';
}

5. normalize()
/* 用于他国语言的语调符号和重音符号  Ǒ(\u01D1)和 O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)
	两种合成符的判断,js中直接判断为false ,加上 .noemalize()方法后为true */

6. 判断字符串是否存在
let s = "Hello World";
s.startsWith("Hello")  // true 判断参数是否在字符串的开头
s.endWith("World")  // true 判断参数是否在字符串的结尾
s.includes('o')  // true 判断是否存在该字符串
s.startWith("World",6) // true 从第6个位置到结束是否是以"World"开头
s.includes("Hello",6) // false 从第6个位置到结束中是否包含指定字符串
// 注意--------------
s.endWith("Hello",5) // 前5个字符中是否包含指定字符串

7. repeat()
// 返回一个新字符串,表示将指定字符串复制n次
let str = "a".repeat(2); // "aa";
let str = "xy".repeat(2); // "xyxy";
// 参数为小数则会取整,负数则报错,0则是一个空字符串
let str = "xy".repeat(2.9); //"xyxy"
let str = "xy".repeat(-1); // 报错
let str = "xy".repeat(0); // 空字符串
// 如果参数是 0~-1之间的小数,则会先取整,也就是按 0 计算
// 如果参数是 NaN 则会按 0 进行计算
// 如果参数是字符串,则先会用 Number() 进行转换再进行计算

8. padStart() padEnd()
// 字符串填充
.padStart()  // 头部补全
.padEnd()	 // 尾部补全
'xy'.padStart(3,'0'); // "0xy"
'xy'.padEnd(4,'0'); // "xy00"
'xy'.padStart(5,'ab'); // "abaxy"
'xy'.padEnd(5,'ab'); // "xyaba"
// 第二个参数省略,默认用空格补全
'xy'.padEnd(5); // "xy   "

9. matchAll()
// 返回一个正则表达式在当前字符串的所有匹配

10. 模板字符串
let str = `<div>${a+1}</div>`;
let str = `<div>${fn()}</div>`;

11. at()
// 返回指定索引对应的字符
let str = "123456"
at(0)  // 1

四 数值的扩展
1. Number.isInteger()
// 判断一个数值是否为整数
Number.isInteger(25); // true
Number.isInteger(25.5); // false

2. Number.EPSILON
/* 能够接受的误差范围
   Number.EPAILON  是一个常量,表示1与大于1的最小浮点数之差,即 1 与 1.000...0001(51个零)
   等同于 Math.pow(2,-52)
   常用于浮点数计算后的误差判断,如果小于这个值,则表示误差没有意义,即:不存在误差*/

   	function withinErrorMargin (left, right) {
  		return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
	}

3. Number.isSafeInteger()
// 判断整数是不是在 -2^53  到 2^53 之间,因为在js内超过这个数值,判断就会出错
Number.isSafeInteger(num)  // 返回一个布尔值,注意,只判断数字,其余都为 false

4. Math静态方法的扩展
// 1. Math.trunc()  
	// 用于去除一个数的小数部分,返回整数部分
		Math.trunc(1.222)  // 1
		Math.trunc(-0.111) // 0
	// 对于非数字,会先用 Number() 进行内部转换
		Math.trunc(true) // 1
		Math.trunc(false) // 0
	// 转换失败则返回 NaN
		Math.trunc(NaN)  // NaN
		Math.trunc('foo') // NaN
		Math.trunc()	 // NaN
		Math.trunc(undefined)  // NaN

// 2. Math.sign() 
	// 判断一个数是 正数 负数 零 ,如果是非数值,则会先进行 Number() 转换
	// 返回 1:正数  -1:负数  0:零  -0:(输入-0的时候)  NaN:转换失败  报错:参数语法错误时
		Math.sign(55)      // 1
		Math.sign(-1546)   // -1
		Math.sign(0)	   // 0
		Math.sign(-0)	   // -0
		Math.sign('ss')	   // NaN
		Math.sign(2aa)	   // 报错

// 3. Math.cbrt()
	// 用于计算一个数的立方根 , 如果是非数值,则会先进行 Number() 转换
	// 返回: 计算后的值  NaN
		Math.cbrt(8)	// 2
		Math.cbrt('8')	// 2
		Math.cbrt('ss')	// NaN
// 4. Math.hypot()
	// 返回所有参数的平方和的平方根
	// 如果参数不是数值,则会先用 Number() 进行转换,只要有一个为NaN,则返回 NaN
	// 可以写多个参数
		Math.hypot(3,4) // 5
		Math.hypot()    // 0
		Math.hypot(NaN)	// NaN
		Math.hypot(3,4,'a')  // NaN


5. ** 指数运算符
2 ** 2 // 4
2 ** 3 // 8
2 ** 4 // 16
// 计算方式是右结合,即从右往左计算
	2 ** 3 ** 2 ==> 2 ** (3 ** 2) // 512
// 可以与 = 结合
	let a = 2;
	a **= 3 // 8 

五:函数的扩展
1 参数默认值
function abc(a=1, b=2){
    ruturn a+b;
}
// 注意:使用参数默认值时,函数不能有同名的形参
// 参数也可以是一个表达式
	let i = 100;
    function abs(y = i + 1){
		console.log(y);
    }
	abs(); // 101

// 与解构赋值一起使用
    function foo({x,y = 5} = {}){
        console.log(x,y);
    }
	foo({x:1, y:2}); // 1,2
	foo();			 // undefined 5
	// 注意,如果不设置默认值是 foo()调用会出错,因为只有foo的参数是一个对象时,x,y才会通过结构赋值生成,若果不生成就会报错

// 当知道了默认值后,函数length属性的计算将去掉该参数
// 函数.length : 该函数预定义传入参数的个数
	(function(a, b = 1){}).length  // 1

// 作用域
// 当设置的参数的默认值时,函数声明初始化时会形成一个单独的 参数 作用域,当初始化结束,该作用域消失
// 如果没有设置默认值,则不存在该作用域
	function abc(x, y = x){ return y;}
    	// 当abs进行初始化时,参数会形成一个单独的作用域,此时 y 的默认值 x 指向 第一个参数 x
		// 所以 abc(90) 会返回 90 
	let x = 1;
    function foo(y = x){
		let x = 2;
        console.log(y);
    }
		// 当foo进行初始化时,参数会形成一个单独的作用域,由于作用域链的问题,所以 x 会像上查找,
		// 查找不到则会报错,即:此时初始化结束后就相当于 y = 1 ; 


2. rest参数

ES6中引入了rest参数(形式为 …变量名),用于获取函数的多余参数,获取后的格式为一个数组

function foo(x, ...values){
    let sum = 0 + x;
    for(let val of values){
        sum += val;
    }
    return sum;
}
foo(1,2,3,4,5,6)   21
// 函数的 length 属性不包括 rest 参数

3. name属性
// 返回该函数的函数名
	function foo(){};
	foo.name; // 'foo'
	
	var a = function(){};
    a.name;   // ES5总为 '' ES6中为 'a'

4. 箭头函数

注意:使用了箭头函数后,这个函数里面就没有自己的this,里面所出现的this是外部函数的this,而不是指向的this,这种情况也称为this指向的固定化

// 只有一个参数
    let f = v => v;
    // 等同于
    let f = function (v) {
        return v;
    }

// 无参数
    let f = () => 5;
    // 等同于
    let f = function () {
        return 5;
    }

// 多个参数
    let sum = (num1,num2) => num1 + num2;
    // 等同于
    let sum  = function(num1, num2){
        return num1 + num2;
    }
    
// 返回一个对象
    // 由于代码块是用 {} 包含起来的,所以返回一个对象时需用 () 包含起来,否则对象的 {} 会被解析成函数体
    let a = id => ({id:id,num:1})

// 与解构赋值一起使用
    let a = ({first,last}) => first + '' + last;

// 与 rest 参数一起使用
	let a = (...arr) => arr;

5. 双冒号运算符

改变this指向,即:双冒号左边的对象是右边函数中的this

// 1. obj::foo
	等同于 foo.bind(obj)

6. 尾调用优化

尾调用:指某个函数的最后一步调用了一个函数,即:return y(x); 的严格格式
尾调用优化:每一次函数调用都会在内存中形成一个"调用记录",又称“调用帧”,保存调用位置和内部变量等信息,所有的调用帧称之为 调用栈 ,如果内部函数用到外部函数的变量,就不会进行尾调用优化

尾递归:函数调用自身,称之为递归,尾调用自身,称之为尾递归

六:数组的扩展
1. 扩展运算符
// ...  ,将一个数组转为用逗号分割的参数列表,内部使用for...of循环
	console.log(...[1,2,3]) // 1 2 3
// 一般用于函数调用
	let arr = [1,2,3,4,5,6];
	foo(...arr)
// 数组最大值判断
	let arr = [1,2,3,4,5,6];
	Math.max(...arr);
// 数组中的每一项添加到另一个数组中
	let arr = [1,2,3,4];
	let brr = [5,6,7,8];
	arr.push(...brr);
// 数组复制--浅拷贝
	let arr = [1,2,3];
	let brr = [];
	brr = [...arr];
// 数组合并--浅拷贝
	let arr = [1,2,3,4];
	let brr = [5,6,7,8];
	let crr = [...arr , ...brr];
// 数组与结构赋值一起使用
	const [first, ...rest] = [1, 2, 3, 4, 5];
	first // 1
	rest  // [2, 3, 4, 5]
// 字符串转数组
    let arr = [...'hello']
    // ['h','e','l','l','o']
// 类数组转换成规定的数组
    let liArr = document.querySelectorAll('li');
	let arr = [...liArr];
	/* query.selectorAll 方法返回的是一个NodeList对象。不是一个数组,而是一个类似数组的对象,
	   之所以可以用数组的方法,是因为nodeList对象实现了Iterator(迭代)接口*/

2. Array.from()

将两类对象转换成规定的数组:类似数组的对象(具有length属性),可遍历迭代对象
如果参数是一个规定的数组,该方法会返回一个一模一样的新数组------即:复制

let arrayLike = {
    '0':'a',
    '1':'b',
    '2':'c',
    length:3
}
let arr = Array.from(arrayLike);
// ['a','b','c','d']

Array.from()方法还有第二个参数,作用类似于map()方法,将数组中的被一个元素进行处理并返回
Array.from(arr,x=>x*x)
// 等同于
Array.from(arr).map(x=>x*x);

// 例:取出每个节点的内容,并形成一个数组
	let liObj = document.querySelectorAll('li');
	let arr = Array.from(li,v=>v.innerHTML);
// 例:将布尔值为false的数组项转换成0
	let arr = [1,2,,3,,5,,0,];
	let brr = Array.from(arr,v=>v||0);
	// [1, 2, 0, 3, 0, 5, 0, 0]

3. Array.of()

用于将一组值转换成数组

Array.of(1,2,3);
	// [1,2,3]

4. entries() , keys() , values()
var arr = ['a','b','c', 1,2,3]
arr.keys(); 	// 返回一个对键名的遍历器---- 一个对象
arr.values();	// 返回一个对键值的遍历器---- 一个对象
arr.entries();	// 返回一个对键值队的遍历器---- 一个对象
// 遍历器中有一个next()方法,里面存放着一条数据,可循环调用来一次输出结果
arr.keys().next(); === {value: 0, done: false}
arr.values().next(); === {value: "a", done: false}
arr.entries().next();  === {value: Array(2), done: false}
arr.entries().next().value; === [ 0,'a']
// 例:用 for of 来循环遍历
    for(var [key,value] of arr.entries()){
		console.log(key+"="+value);
    }
	// 0='a'  1='b'  2='c'  3=1  4=2  5=3
 	

5. flat()
// 将多维数组“拉平”
	[1,[2]].flat(); 
	// [1,1]
	[1,[2,[3]]].flat(); 
	// [1,2,[3]]  默认拉平一层,从外向内拉平
// flat(number);
	number:设置拉平的层数
    Infinity 关键字,表示无限
// 如果数组有空位,flat()方法会跳过空位
    [1,2,3,,4].flat(); 
	// [1,2,3,4]

6. flatMap()
// 对原数组的每一个成员执行一个函数(相当于执行了map()方法),返回一个新数组
// 执行完后再执行flat()方法
[1,2,3,4,5,6].flatMap(x=>x*x); //[1,4,9,16,25,36]
[1,2,3,4].flatMao(x=>[x,x*x]); //[1,1,2,4,3,9,4,16]

7. 数组的空位
/* ES6中 Array.from(),(...),entries(),keys(),values(),find(),findIndex()
	等会将空位处理成 undefined */
	[...[,1]]  // [undefined,1]

七. 对象的扩展
1. 属性简洁写法
// ES6 允许直接在对象中直接写入变量,此时,变量名为属性名,变量值为属性值
	let aaa = 1;
	let obj = {aaa}; // {'aaa':1}

2. 属性名表达式
let aaa = 'a'
let obj = {
    ['a'+'bc']:1,
    [aaa]:2
}
obj.abc // 1
obj.a   // 2
obj.aaa // undefined
obj[aaa]// 2


3. 可枚举性
// 对象中的每个属性的都有一个描述对象(Descriptor),用来控制该属性的行为
	Object.getOwnPropertyDescriptor() 方法可以获取该属性的描述对象
    let obj = {foo:123};
	Object.getOwnPropertyDescriptor(obj,'foo');  // 返回一个对象
	//  {
	//    value: 123,
	//    writable: true,
	//    enumerable: true,  //可枚举性
	//    configurable: true
	//  }
	// 因为for in 遍历会返回继承的属性,所以可以通过设置 enumerable 属性值为 false 来让其忽略该属性
	Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
// ES7 引入了Object.getOwnPropertyDescriptors方法,
	// 返回指定对象所有自身属性(非继承属性)的描述对象

4. 属性的遍历
(1) for...in
    循环遍历对象自身的和继承的可枚举属性(不包含Symbol属性)

(2) Object.keys(obj)
	返回一个数组,包含对象自身的所有属性的(不含继承)所有可枚举属性(不含Symbol属性)的键名
   
(3) Object.getOwnPropertyNames(obj)
	返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)的键名
    
(4) Object.getOwnPropertySymbols(obj)
	返回一个数组,包含对象自身的所有键名,不管键名时Symbol 或字符串,也不管是否可枚举
  
(5) Reflect.ownKeys(obj)
	返回一个数组,包含对象自身的所有键名,不管键名时Symbol或者字符串,也不管是否可枚举
 
// 以上 5 种遍历对象的键名都遵守同样的属性遍历的次序规则
    - 首先遍历所有的数值键,按照数值升序排序
	- 其次遍历所有的字符串键,按照加入时间升序排列
	- 最后遍历所有订单 Symbol 键,按加入时间升序排序
    // 例:
    Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
	// ['2','10','b','a',Symbol()]

5. super 关键字

表示指向当前对象的原型对象

let obj = {
    foo:123,
    foo(){
      return super.name;
	}
};
    Object.prototype.name="ybm";
    console.log(obj.foo());
// 注意,此关键词只能用于对象的方法中,在其他地方使用会报错
    let obj = {
        foo:super.name
    }
    // 报错,该关键词用于对象的属性中
    let obj = {
        foo:()=>super.name
    }
    let obj = {
        foo:function (){
            return super.name;
        }
    }
    // 报错,因为在JS引擎的解析中,此时的super用在一个函数中,然后赋值给foo属性。
    // 所以,目前只有对象方法的简写形式可以让js引擎确认定义的是对象的方法
    /*
    	在JS引擎的内部,super.foo等同于 
    			Object.getPrototypeOf(this).foo   属性
    		 或 Object.getPrototypeOf(this).foo.call(this)  方法
    */

八. 对象的新增方法
1. Object.is()
// 比较两个值是否相等 可比较特殊的字符
Object.is(NaN,NaN) 	// true
Object.is(+0,-0)   	// false
Object.is({},{})	// false
Object.is(+0,0)		// true
Object.is(-0,0)		// false


2. Object.assign()
// 用于的对象的合并,将源对象的所有可枚举的属性复制到目标对象中,不拷贝继承迭代属性
// 若属性名相同,则后面对象的属性值会覆盖前面的属性值
    Object.assign(目标对象,源对象)
    const target = {a:1};
    const target1 = {b:2};
    const target2 = {c:3};
    const target3 = {a:4};
    Object.assign(target,target1,target2,target3)
// target {a:4,b:2,c:3};
// 第二个参数可以为一个字符串,如果是字符串,就会以数组的形式拷贝进目标的对象
// 数值和布尔类型则不会起作用
	let str = "abc";
	let obj = {a:1};
	Object.assign(obj,str);
	// obj {'0':'a', '1':'b', '2':'c', a:1}

// 1. 为对象添加方法
    Object.assign(SomeClass.prototype,{
        someMethod(a,b){

        },
        anotherMethod(){

        }
    })
	// 等同于下面的写法
	SomeClass.prototype.someMethod = function(a,b){};
	someClass.prototype.anotherMethod = function(){};

// 2. 拷贝对象
    function clone(obj){
        return Object.assign({},obj);
        // 此方法只能拷贝自身的值,不能拷贝继承的值
    }

// 3. 合并多个对象
	const merge = (target,...sources) => Object.assign(target,...sources);


3. Object.getOwnPropertyDescriptors()
// 返回指定对象所有自身属性(非继承属性)的描述对象
const obj = {
    foo:123,
    get bar(){return 'abc'}
};
Object.getOwnPropertyDescriptors(obj);
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

4. Object.create()
// 创建一个新对象,并指定新对象的 __proto__ 指向
// 语法 Object.create(proto); 第一个参数为新对象的原型指向
	let obj1 = {a:1};
	let obj2 = Object.create(obj);
	obj2.a; // 1

5. Object.setPrototypeOf()
// 设置指定对象的原型对象,并返回当前对象
// 语法:Object.setPrototypeOf(object,prototype);
	let obj1 = {a:1};
	let obj2 = {b:2};
	Object.setPrototypeOf(obj1, obj2);
	obj1.b; // 2

6. Object.getPrototypeOf()
// 返回指定对象的原型对象
let obj = Object,getPrototypeOf(obj1);
obj {b:2}
7. keys() values() entries()
// Object.keys(obj) 
// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键名
	var obj = {foo:'bar',baz:44};
	var arr = Object.kes(obj);
	arr ['foo','baz'];

// Object.values()
// 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的属性值
	var obj = {a:1,b:2};
	var arr = Object.values(obj);
	arr [1,2];

// Object.entries() 不支持IE
// 返回一个二维数组,每一个数组项是参数对象自身的(不含继承的)所有可遍历属性
//    的键值对形成的数组,忽略 Symbol
	var obj = {a:1,b:2};
	var arr = Object.entries(obj);
	arr [['a',1],['b',2]]

8. Object.fromEntries()
// Object.fromEntries() 方法是 Object.entries() 的逆操作,
//    用于将一个键值对数组转换成一个键值对形式的对象
// 注:只兼容火狐浏览器
	Object.fromEntries([
  		['foo', 'bar'],
  		['baz', 42]
	])
    // { foo: "bar", baz: 42 }

九. Symbol
/*
	1. Symbol 值通过 Symbol()函数生成
	2. Symbol 函数可以接受一个字符串作为参数,表示对Symbol实例的描述
	3. Symbol 可以显式转换成字符串,布尔值,但不能转换成数字
	
*/
let si = Symbol('foo')
console.log(si)// Symbol(foo)

/*用法?*/
let abc = Symbol('abc');
let obj = {
    [abc]:'456',
    'abc':'457',
}

十 Set和Map 构造函数
1. Set构造函数

new Set() 生成的是Set的数据结构,其结构内不会有重复的值,是一个类数组对象

内部判断两个值是否相同使用的算法叫做“Same-value-zero equality”,类似于 === 比较,但是该算法中 NaN与NaN相等

let arr = [1,1,2,2,3,3]
const a = new Set();
arr.forEach(x=>a.add(x));
for(let i of a){
    console.log(i)
}//1 2 3 4

//1. Set函数可以接受一个数组或具有iterable接口的其他数据结构作为参数
	例一:数组为参数
        const set = new Set([1,2,3,4,5,6]);
        [...set] // [1,2,3,4,5,6]
	例二:具有iterable接口的伪数组作为参数
    	const set = new Set(document.querySelectorAll('div'));
		set.size; // 110

//2. 数组去重
	var arr = [1,2,3,1,1,2,3,4];
	[...new Set(arr)]; // [1,2,3,4]

//3. 字符串去重
	var str = '123132451234561321';
	[...new Set(str)].join(''); // "123456"

//4. NaN
	var [a,b] = [NaN,NaN];
	let set = new Set();
	set.add(a);set.add(b);
	[...set]; // [NaN]
	
//5. 并集,交集,差集
	let a = new Set([1,2,3]);
	let b = new Set([2,3,4]);
	// 并集
		[...new Set(...a,...b)]; //[1,2,3,4]
	// 交集
		[...new Set([...a].filter(x=>b.has(x)))]; //[2,3]
	// 差集
		[...new Set([...a].filter(x=>!b.has(x)))]; //[1]

// Set 结构的实例属性
	Set.prototype.constructor :构造函数,默认就是Set函数
    Set.prototype.size :返回 Set 实例的成员总数
// Set 结构的实例方法
    // 1> 操作方法
    	.add(value) :添加某个值,返回Set结构本身
    	.delete(value) :删除某个值,返回一个布尔值,表示删除是否成功
    	.has(value) :判断是否存在这个值,返回一个布尔值
    	.clear() :清除所有的成员,无返回值
    // 2> 遍历方法、
    	.keys() :返回键名的遍历器
    	.value() :返回键值的遍历器
    	.entries() :返回键值对的遍历器
    	.forEach() ;使用回调函数遍历每个成员
    	// 由于 Set 结构没有键名(或者说键名和键值是一样的),所以keys() 和 values() 方法得到的结果一样
    	var arr = [1,2,3];
		let set = new Set();
    	for (let item of set.entries()) {
          	console.log(item); // [1,1] [2,2] [3,3]
        }
    // Set 结构的实例默认是可遍历的,默认遍历器生成函数就是它的values方法
    // 所以可以直接省略values方法,直接用 for...of遍历
        for (let item of set) {
			console.log(item); // 1 2 3 
        }

2. WeakSet结构

1. 与Set类似,也是不重复的值的集合,但是WeakSet的成员只能是对象
2. WeakSet为弱引用,即:垃圾回收机制不计算WeakSet对该对象的引用次数。所以,WeakSet适合存储一些临时数组,当该数组在外部引用此时为0时,他在WeakSet中的引用就会自动消失
3. 由于成员是弱引用,随时会消失,所以ES6规定WeakSet不可遍历
4. 该构造函数可以接受一个数组,或者一个类数组(具有 Iterable 接口)对象,该数组的成员会自动转换成WeakSet实例对象的成员

// WeakSet结构的三个方法
	-WeakSet.prototype.add(value);
	-WeakSet.prototype.delete(value);
	-WeakSet.prototype.has(value);

let arr = [[1,2],[2,3]];
var weak = new WeakSet(arr);
// {[1,2],[2,3]}

3. Map数据结构

1. js对象本质上是键值对的集合,但是传统上只能用字符串作为键,所以限制性大,因此ES6提供了一种新的数据结构,类似对象,也是一个键值对的集合,但键名可以是任意类型

// 实例的属性和操作方法
	.size  //放回Map结构的成员总数
    .set(key,value)  //添加或者更新一组键值对,返回整个Map数据结构
	.get(key)  //返回指定键的值,若不存在,则返回undefined
	.has(key)  //判断是否存在指定键,返回布尔值
	.delete(key)  //删除指定键
	.clear(key)   //删除所有的键,无返回值
// 遍历方法---遍历顺序就是插入顺序
	keys():返回键名的遍历器。
	values():返回键值的遍历器。
	entries():返回所有成员的遍历器。
	forEach():遍历 Map 的所有成员。
    // 默认的遍历器接口为entries()


// 1. 基本使用方法
	let map = new Map();
	let m = {a:'1'};
	map.set(m, 'content');
	map.get(m); // content;
	map.has(m); // true;
	map.delete(m); // true;
	map.has(m); // false
// 2. 可以接受一个双成员的二维数组,或者具有Iterable接口,且每个成员都是一个双元素的类数组对象作为参数
	let map2 = new Map([['a',1],['b',10]]);
	map2.get('a'); // 1
	map2.get('b'); // 10

	// 使用Set 和 Map 实例作为参数
        const set = new Set([
          ['foo', 1],
          ['bar', 2]
        ]);
        const m1 = new Map(set);
        m1.get('foo') // 1

        const m2 = new Map([['baz', 3]]);
        const m3 = new Map(m2);
        m3.get('baz') // 3

// 3. 读取一个未知键,则返回undefined
	new Map().get('a'); // undefined

// 4. 在Map中 NaN与NaN相等
	let mp = new Map();
	mp.set(NaN,'1');
	mp.set(NaN,'2');
	mp.get(NaN); // 2

4. WeakMap

WeakMap结构与Map结构相同,也是用于生成键值对的集合
1. 只接受键名作为键名,2. 键名所对应的值不计入垃圾回收机制

// 一般用于对象在将来会消失,这样这个对象所对应的键值也会被回收,不需要手动回收
    let myElement = document.getElementById('logo');
	let myWeakmap = new WeakMap();
	myWeakmap.set(myElement, {timesClicked: 0});
	myElement.addEventListener('click', function() {
  		let logoData = myWeakmap.get(myElement);
  		logoData.timesClicked++;
	}, false);
    // 当DOM元素被删除时,WeakMap中该对象所对应的键值对也会自动删除

// 操作方法
	.set()
	.get()
	.has()
	.delete()

十一:Promise

一种异步解决的方案,一个容器,保存着未来某个时间段某个异步操作的结果

特点:
1. promise对象的状态不受外界所改变,它代表着一种异步操作,有三种状态:pending(进行中),fulfilled(成功),rejected(失败),只有结果才能获取是哪一种状态
2. 状态一旦改变一次,就不可再次更改,状态改变只有两种:pending–fulfilled,pending–rejected。只要这两种状况发生,状态就定型了,这是就称为resolved(已定型)

缺点
1. 无法取消,一旦新建就会立即执行,无法中途取消
2. 如果不设置回调函数,promise内部出现的错误不会反应到外部
3. 当处于pending状态时,不知道目标是处于那一阶段(刚开始or已完成)

// 基本使用:
const promise = new Promise(function(resolve, reject){
    // 异步代码...
    if('异步成功时'){
        resolve();//成功的回调函数调用
    }else{
        reject();//失败的回调函数调用
    }
})
promise.then(callback1,callback2);
//.then()方法用于绑定该对象结果的两个阶段所对应的回调函数
//.then()方法会在当前脚本所有同步任务执行完后才会执行
//如果第一个.then()方法中返回的是一个promise对象,
//   那么第二个.then()方法会等待第一个的状态改变时再来判断是否执行
promise.then().catch(callback)
//.catch()方法:当异步操作抛出错误或者回调函数运行出错时会被被该方法捕获
//如果promise的状态已经为成功状态,那么再抛出异常就会无效                                                               

.finally()
// 用于指定不管promise对象最后的状态如何,都会执行的操作
promise.then(result => {...})
	   .catch(error => {...})
       .finally(()=>{...})
   // 本质是then()方法的特例,等同于
       .then(result => {},error => {}) //成功或者失败的方法各写一遍
       
// 例:请求完后关闭服务器
   promise.then().finally(server.stop);
                   

Promise.all()
// 用于将多个Promise实例,包装成一个新的Promise实例,并返回
const p = Promise.all([p1, p2, p3]);
/* 接受一个数组作为参数,数组中的每一个成员都是一个Promise实例
	p的状态由 p1,p2,p3决定,
		当3个都为fulfilled时,p的状态才为fulfilled,此时p1,p2,p3的返回值会组成一个数组传递给p的回调函数
		当其中一个为reject时,p的状态为rejected,此时第一个rejected状态的实例的返回值会传递给p的回调函数
		注意:若作为参数的promise实例自己定义了catch方法,那么就不会触发Promise.all()所定义的catch()
*/	

Promise.race()
// 用于将多个Promise实例,包装成一个新的Promise实例
const p = Promise.race([p1, p2, p3]);
/* 接受一个数组作为参数,数组中的每一个成员都是一个Promise实例
	当参数中某个Promise实例的状态率先发生改变时,p的状态也会发生改变,率先改变的返回值会传递给p的回调函数
*/	
// 指定时间内没有获得结构就返回rejected
var p = Promise.race([p1,
                      new Promise((res,error)=>{ 
                      	setTimeout(()=>{error();},5000);	
                      })]);
p.then().catch();	// 第一个参数5秒内没有执行成功就抛出失败

Promise.resolve()
将现有对象转换成一个Promise对象
const ps = Promise.resolve(对象)
// 当参数是一个thenable对象,即:具有then()方法的对象时,转换完后会立即执行该方法
// 当参数不是具有then()方法的对象,或根本不是一个对象时,该方法会自动生成一个新的Promise对象,状态为
//    成功状态,并把参数传递给相对应的对调函数
// 当无参数时,该方法会自动创建一个新的Promise对象,状态为成功状态

promise.reject()
返回一个新Promise实例,状态为rejected,并立即执行回调函数
//注:与resolve()不同的是reject()方法会把创建时的参数原封不动的传递给回调函数

promise.try()

为了方便不管是同步还是异步都用promise管理代码

十二:Iterator(遍历器)

是一种接口,为不同的数据结构提供一种统一的访问机制,任何数据只要部署了Iterator接口都可以完成遍历操作

遍历过程:
​ (1)创建了一个指针对象(即:遍历器对象),指向当前数据结构的起始位置
​ (2)第一次调用指针对象的next()方法,指针就指向数据结构的第一个成员
​ (3)第二次调用指针对象的next()方法,指针就指向数据结构的第±q二个成员
​ (4)依次类推,不断调用指针对象的next()方法,知道它指向数据结构的结束位置
​ 每一次调用next方法,都会返回数据结构的当前成员信息。即:一个包含 valuedone 两个属性的对象,其中 value 属性时当前成员的值,done 属性是一个布尔值,表示遍历是否结束

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,这个属性是一个函数表达式,执行它就会返回一个遍历器
for…of循环会调用Symbol.iterator属性,返回返回对象中的value值

// 模拟next()方法
function makeIterator(obj){
    let nextIndex = 0;
    return {
        next:()=>{
            return nextIndex < obj.length ?
                {value:obj[nextIndex++], done:false}:
            	{value:undefined, done:true}
        }
    }
}

var arr = [1,2,3];
var obj = makeIterator(arr);
obj.next(); //{value:1}
obj.next(); //{value:2}
obj.next(); //{value:3}
obj.next(); //{done:true}


1.元素js中具有Iterator接口的数据结构:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象(类似数组的对象)
// 例:调用数组的 Symbol.iterator属性
var arr = [1,2]
var iter = arr[Symbol.iterator]()
	// Symnol值要用[]括起来
iter.next(); // {value:1, done:false}
iter.next(); // {value:1, done:false}
iter.next(); // {value:undefined, done:false}

2.调用Iterator接口的场合
1. 解构赋值
var arr = new Set().add(1).add(2).add(3)
var [x,y] = arr // x:1 y:2
var [first,...rest] = arr  //first:1  rest:[2,3]

2. 扩展运算符
var arr = "hello"
[...arr] // ['h','l','l','o']

3. 特殊的类数组对象:
	例:querySelectorAll()
	例:
    	var arrayLike1 = {  
            0: 'a', 
            1: 'b',
            length: 2,
        	[Symbol.iterator]: Array.prototype[Symbol.iterator] 
            // 需要具有Symbol.iterator属性
        };

        // 报错
        for (let x of arrayLike1) {
          console.log(x);
        }

3. 任何接收数组作为参数的场合
-for...of
-Array.from()
-Map(),Set(),WeakMap(),WeakSet()
-Promise.all()
-Promise.race()

4.遍历器对象的return(),throw()方法
/* 遍历器除了具有next()方法,还具有return()方法和throw()方法
   当for...of循环提前退出(出错时 或 break语句),就会调用 return 方法
   注意:return方法必须返回一个对象,这是Generator规格规定
*/
function readLinesSync(file) {
  return {
    [Symbol.iterator]() {
      return {
        next() {
          return { done: false };
        },
        return() {
          file.close();
          return { done: true };
        }
      };
    },
  };
}
// 情况一
for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}
/* 当执行break方法后就会执行遍历器的return()方法 */
// 情况二
for (let line of readLinesSync(fileName)) {
  console.log(line);
  throw new Error();
}
/* 情况二在执行return方法关闭文件之后,再抛出错误 */

十三:Generator 函数
1.函数定义
// 1. function关键字与函数名的中间用 * 号连接
// 2. 函数体内部用 yield 表达式来表示不同的状态
// 3. 调用Generator函数后返回一个遍历器对象,即:指针对象,注:此时Generator函数内部代码还没有执行
// 4. 遍历器对象使用.next()方法进行执行函数体代码,直到遇到 yield 表达式 或 return 语句停止
// 5. 遇到yield表达式时next()方法返回一个对象,{value:当前yield表达式的值, done:布尔值(当前遍历的状态)}
// 6. yield 关键字相当于一个暂停符,.next()方法就为恢复执行
// 7. 返回的遍历器对象不是this对象
// 8. 遍历器对象可以调用函数原型上的方法

function* hello(){
    console.log(1)
    field 'a'
    console.log(2)
    field 'b'
    console.log(3)
    return 'c'+'d'
    console.log(4)
}
var hl = hello()
hl.next() // 1 {value:a, done:false}
hl.next() // 2 {value:b, done:false}
hl.next() // 3 {value:cd, done:true} 遇到return时 done的值就为 true
hl.next() // {value:undefined, done:true} // 因为上一句已经return了,所以4不会被输出


2.next()方法的参数
// 1. yield 表达式本身是没有返回值的,或者说总是undefined
// 2. next()方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
// 3. 因此第一个调用.next()方法里面传参是无效的,V8引擎会直接忽略第一次调用的参数
// 4. 调用函数后需调用一次next()方法来启动Generator函数的内部代码

function* f() {
  for(var i = 0; true; i++) {
    var reset = yield i;
    if(reset) { i = -1; }
  }
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.next(true) // { value: 0, done: false }
    // 前三次执行时,reset为undefined,第四次执行时,next()方法传了一个参数true,
	// 所以yield表达式的,返回值就为true,i=1,i++就为0

// 此特性可以在特定阶段从函数外部向内部传入值
    function* foo(x) {
      var y = 2 * (yield (x + 1));
      var z = yield (y / 3);
      return (x + y + z);
    }
    var b = foo(5);
    b.next() // { value:6, done:false }
    b.next(12) // { value:8, done:false }
    b.next(13) // { value:42, done:true }

3.for … of 循环
// for ... of 循环可以自动遍历Generator函数时所生成的iterator对象
// 例:实现斐波那锲数列
function* fbnq(){
    let [a,b] = [0,1]
    for(;;){
        yield b
        [a,b] = [b,a+b] 
    }
}
for(var n of fbnq()){
    if(n>1000) break
    console.log(n)
}

4. return()方法
// 调用遍历器对象的return()方法后返回对象中的value值就是return()方法的参数,并且done的值为true
// 此时,Generator函数的遍历就会停止,如果无参数,则value值为undefined
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
var g = gen();
g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

5.yield* 表达式
// 用于在一个Generator函数中调用另外一个Generator函数
// yield* 表示返回的是一个遍历器对象,若不加js不会自动识别,会输出这个对象

// 无 return 语句时
function* foo(){
    yield 'a'
    yield 'b'
}
function* bar(){
    yield 'x'
    yield* foo()
    yield 'y'
}
for(let n of bar()){
    console.log(n) // 'x' 'a' 'b' 'y'
}

// 有 return 语句时,可以用一个变量来接收 field* foo 表达式的返回值
function* foo(){
    yield 'a'
    yield 'b'
    return 'c'
}
function* bar(){
    yield 'x'
    let a = field* foo()
    console.log(a)
    yield 'y'
}
for(let n of bar()){
    console.log(n) // 'x' 'a' 'b' 'c'y'
}

// 例:利用yield* 命令遍历取出二维数组
function* iterTree(tree) {
  if (Array.isArray(tree)) {
    for(let i=0; i < tree.length; i++) {
      yield* iterTree(tree[i]);
    }
  } else {
    yield tree;
  }
}
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
for(let x of iterTree(tree)) {
  console.log(x);
}
or  [...iterTree(tree)]

6 作为对象属性的 Generator 函数
let obj = {
    * myGeneratorMethod(){}  // myGeneratorMethod: function* (){}
}

7 状态判断保存
var clock = function* () {
  while (true) {
    console.log('Tick!');
    yield;
    console.log('Tock!');
    yield;
  }
};

8 Thunk 函数
// 传值调用:参数为表达式时,表达式在进入函数体之前就已经计算完成
// 传名调用:参数为表达式时,表达式只有在函数体中使用到时才进行计算

Thunk 函数的定义:将一个参数放到一个临时函数内,再把临时函数当参数传入函数体中,
				这个临时函数就是Thunk函数

十四:async函数
// 1. 语法与 Generator 函数相似
var asyncRead = async function(){
    const f1 = await f1()
}
// 1. 返回一个promise对象
// 2. 有两个关键字 async await 分别对应 * yield
// 3. async 函数内置执行器,即:不需要像Generator函数需要.next()方法执行
// 4. await命令后面可以是promise对象和原始数据类型(但是这时会立即自动转换成resolved 的 promise对象)
// 5. async函数内部return 语句返回的值会成为then方法回调函数的参数
// 6. async函数内部抛出错误会直接返回的Promise对象的变成reject状态,然后被catch捕获
// 7. 当遇到第一个await时,后面的代码都会被放入队列中执行
// 8. 只有当await后面的promise对象执行完后,他的状态才会改变
// 9. 只用当async函数内部的所有异步操作执行完后才会执行then方法
// 10 若将异步操作放到try...catch里面,则该异步操作无论是否成功,后面的异步操作都会继续执行


1. 异步函数的触发
// 1. 顺序触发
async function myFunction() {
    await getFoo()
    await getBar()
} // 只用getFoo()执行完后 getBar()才会执行
// 2. 同时触发
async function myFunction() {
    var fooPromise = getFoo()
    var barPromise = getBar()
    await fooPromise
    await barPromise
    // 此时就相当于两个异步函数的执行结果被await拦截了
}
async function myFunction() {
	var [foo,bar] = await Promise.all([getFoo(),getBar()])
}


十五:Class类
1.定义
// Class 类的基本使用
class Point{
    constructor() {
        // 实例属性
        this.name = 2
    }
    // 原型对象上的实例方法
    toString() {}
    // 静态属性
    static a = '41'
	// 静态方法
	static show(){ }
}

var p = new Point()
p.toString()
// 1. 使用方法与构造函数相同
// 2. 方法之间不需要用逗号分隔
// 3. 类中的所有方法实际上是定义在 类名的prototype上
    即等同于:Point.prototype = {
		constructor(){},
        toString(){}
    } 
	所以类的实例调用方法其实就是调用原型上的方法
// 4. 类中的所有的方法都是不可枚举的
    Object.keys(Point.prototype) //[]
// 5. 类没有变量提升

// 类名表达式
	var cls = class Me{}
	var cls = class {}
	var cls = new class {} // 这样就可以直接调用class里面的方法了


2. constructor() 方法
// 1. 每个类在创建的时候必须有一个constructor()方法
// 2. 如果没有显示创建,则系统会默认创建一个
// 3. 该方法会在 new 命令后自动调用,默认返回实例对象,即:this,也可以改写此属性,返回另一个对象
class Point {
    z = 0;
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return '(' + this.x + ', ' + this.y + ','this.z+')';
  }
}
var p = new Point(1,2)
p.toString() // (1,2)

3. get set关键字
// 在类的内部通过 set get 关键字来拦截某个属性的存取值的操作
// 调用时不能加括号,当表达式调用
class MyClass{
    set prop(val){
        this.val = val
        console.log(this.val+'a')
    }
    get prop(){
        console.log(this.val)
    }
}
var myclass = new MyClass()
myclass.prop = 1 // 注意,此时没有加小括号  '1a'
myclass.prop 1


4. extends
// 继承
	class Bar{}
    class aaa extends Bar {
        constructor(){
            super(); // 该关键词的作用是调用父类的constructor方法,子类中必须写,
            				// 否则创建实例时报错---重写constructor()时
        			 // 作为对象时代表指向父类的原型对象,在静态方法中,指向父类
            		 // 因为指向的是原型对象,所以父类上实例的属性和方法,super访问不到
            		 // 通过super调用父类上方法中的this代表的是当前子类的实例
        }
    }

// super()当函数时使用
class A {
    constructor(name,age){
        this.name = name
        this.age = age
    }
    say(){
        console.log(this.name,this.age)
    }
}
class B extends A{
    constructor(...args){
        super(...args) // 分配内存空间,指向this,同时将父类的属性挂载到this上
        			   // super时父类构造器的一个引用,调用super就是调用父类的构造函数
        			   // super前不能使用this
    }
}
const b = new B('xy', 18)


// super当对象时使用
class A {
  p() {
    return 2;
  }
}
class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}
let b = new B();

5. 属性
// name属性
	class Bar{}
	Bar.name // 'Bar'

// this
	class 中的 this 是指向当前实例
    
// static 
    // 当一个方法名前加了static关键词时,该方法就不会被实例所继承,但可以被他的 子类 继承
    class Bar{
        static add(){this}
        // 注意:其中的this是指类,而不是指实例
    }
// 静态属性的定义
    class Bar{
    }
	Bar.a = 1 //注意,写在外面

6. Object.getPrototypeOf()
// 该方法可以从子类上获取父类
Object.getPrototypeOf(子类名) === 父类名
// 可以用该方法来判断一个类是否继承了另一个类
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值