ES6

ES6 ⭐⭐⭐⭐

变量

let

let特性:
1,变量不能重复声明
2.块级作用域 (只在代码块里面有效,全局无法获取)
3.不存在变量提升
4.不影响作用域链
5.暂时性死区:如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

const

//声明常量
1.一定要赋初始值,必须大写
2.声明时必须赋值
3.常量的值不能修改
4.块级作用域
5.对于数组和对象的元素修改,不算做对常量的修改,不会报错
6.冻结对象Object.freeze(),无法更改对象的属性
eg:const TEAM=['UZI',"MXLG",'Ming',"Letme"];
TEAM =100;
//报错
eg:
	const foo = {};
	// 为 foo 添加一个属性,可以成功
	foo.prop = 123;
	foo.prop // 123
	const foo = Object.freeze({});
	// 常规模式时,下面一行不起作用;
	// 严格模式时,该行会报错
	foo.prop = 123;

globalThis对象

	JavaScript 语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。 但是,顶层对象在各种实现里面是不统一的。
	-浏览器里面,顶层对象是,但 Node 和 Web Worker 没有。	window.window
	-浏览器和 Web Worker 里面,也指向顶层对象,但是 Node 没有。selfself
	-Node 里面,顶层对象是,但其他环境都不支持。global

	同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用关键字,但是有局限性。this
	-全局环境中,会返回顶层对象。 但是,Node.js 模块中返回的是当前模块,ES6 模块中返回的是。this this this undefined
	-函数里面的,如果函数不是作为对象的方法运行,而是单纯作为函数运行,会指向顶层对象。 但是,严格模式下,这时会返回。thisthisthisundefined
	-不管是严格模式,还是普通模式,总是会返回全局对象。 但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么、这些方法都可能无法使用。new Function('return this')()evalnew Function
	综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。 下面是两种勉强可以使用的方法。
 // 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

BigInt

	(1)BigInt数据类型
	JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的
    ES2020引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。为了与 Number类型区别,BigInt 类型的数据必须添加后缀n。
    // BigInt 的运算
	1n + 2n // 3n
	注意:BigInt 可以使用负号(-),但是不能使用正号(+),因为会与 asm.js 冲突
    -42n // 正确
	+42n // 报错
	
	(2)BigInt对象
	JavaScript 原生提供BigInt对象,可以用作构造函数生成 BigInt 类型的数值。转换规则基本与Number()一致,将其他类型的值转为 BigInt。
    `注意`:BigInt()构造函数必须有参数,而且参数必须可以正常转为数值
    BigInt(123) // 123n
    BigInt('123') // 123n
    BigInt(false) // 0n
    BigInt(true) // 1n
	
  

Symbol

	es6引入了一个新的原始数据类型Symbol,表示独一无二的值,它是一种类似于字符串的数据类型
	特点:(1)Symbol值是唯一的,用来解决命名冲突的问题
		 (2)Symbol值不能与其他数据进行运算
		 (3)Symbol定义的对象属性不能用for...in循环遍历,但可以使用Reflect.ownKeys来获取对象的所有属性名
	作用:给对象添加属性和方法
创建Symbol
   //第一种:let s=Symbol();
   //第二种:
			let s2=Symbol('残星落影');
			let s3=Symbol('残星落影');
			console.log(s2===s3);//false
   //第三种:
			let s4=Symbol.for('残星落影');
			let s5=Symbol.for('残星落影');				        console.log(s4==s5);//true;

Symbol创建对象属性
	let youxi={
        name:'狼人杀',
        [Symbol('say')]:function(){
            console.log('发言')
        }
    }
    console.log(youxi);// name:'...';
					   // Symbol(say):f()

1.Symbol.description返回 Symbol 的描述。
    const sym = Symbol('foo');
    sym.description // "foo"

2.作为属性名的 Symbol
	Symbol 值作为属性名时,该属性还是公开属性,不是私有属性
    
3.消除魔术字符串
 	魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。常用的消除魔术字符串的方法,就是把它写成一个变量
    
4.属性名的遍历
	getOwnPropertySymbols()方法,该方法返回一个数组,可以获取所有 Symbol 属性名。该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。

5.Symbol.for()和Symbol.keyFor()
	(1)Symbol.for()接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局
	(2)Symbol.for()与Symbol()的区别:
    前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值
   	 (3)Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
    let s1 = Symbol.for("foo");
	Symbol.keyFor(s1) // "foo"
    let s2 = Symbol("foo");
    Symbol.keyFor(s2) // undefined
变量s2属于未登记的 Symbol 值,所以返回undefined

箭头函数

利用箭头=>定义函数
(1)var f = () => 5;
// 等同于
var f = function () { return 5 };

(2)//声明一个函数
let fn=function(a,b){
	result a+b;
}
等同于
let fn=(a,b)=>{
	result a+b;
}
//调用函数
let result =fn(1,2)
console.log(result); //3

1.this是静态的,this始终指向函数声明时所在作用域下的this的值
function getName(){
    console.log(this.name);
}
function getName2=()=>{
    console.log(this.name);
}
//设置window对象的name值
window.name='1111';
const school={
    name:"222222";
}
//直接调用
getName(); //----1111
getName2(); //----1111
//call方法调用
getName.call(school);//222222
getName2.call(school);//1111

2.不能作为构造函数实例化对象
let Person=(name,age)=>{
    this.name=name;
    this.age=age
}
let me =new Person('liu','23');
consle.log(me);// 报错

3.不能使用arguments变量
let fn=()=>{
    console.log(arguments);//报错
}
fn(1,2,3)

4.箭头函数的简写
(1)省略小括号,当形参只有一个的时候;
	let add=n=>{
        return n+n;
    }
    console.log(add(9));//18
 (2)省略花括号 当代码体只有一条语句的时候,return必须省略
 	let pow =n=>n*n;
	console.log(pow(2))//4

`注意`:由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
	let getTempItem = id => ({ id: id, name: "Temp" });

5.注意事项
	(1)箭头函数没有自己的this对象,它里面的this指向定义时上层作用域的this
	(2)不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误。
	(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
	(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。解构赋值
1.数组的解构赋值

let [xiao,liu,zhao,song]=['小沈阳','刘能','赵四','宋小宝'];
console.log(xiao);  //小沈阳
console.log(liu); //刘能

2.对象的解构赋值(属性名与变量名一致)
eg:let {a:x,b:y,z}={c:1,a:3,z:2,b:4};
	console.log(a,b,c);// 3,4,1

let {name,age,xiaopin} ={
	name:'赵本山',
	age:'58',
	xiaopin:function(){
		console.log("小品")
	}
};
console.log(name);//赵本山
console.log(age);//58


3.字符串的结构赋值
	字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
  const [a, b, c, d, e] = 'hello';
	a // "h"
	b // "e"
	c // "l"
	d // "l"
	e // "o"  
4.默认值
解构赋值允许指定默认值undefined。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

var {x: y = 3} = {x: 5};
y // 5
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null

模板字符串``

特点:1,使用反引号表示字符串
	 2,直接换行,保留格式
     3,可以直接${}嵌入变量或js代码
	 4,``.trim()方法去掉执行代码前面的换行
1.声明
let str=`字符串`;

2.内容中可以直接出现换行符
let str=`<ul><li></li></ul> `;

3.变量拼接
let lover="666";
let out=`${lover}四十三`;
console.log(666四十三);

扩展运算符

扩展运算符:‘...’ 能将数组或对象和字符串转换为逗号分隔的参数序列,是一种拆包解构方法
	运用范围:
		(1)数组:将数组元素分隔开,变为用逗号分隔的参数序列
		(2)对象:将对象属性展开,变为用逗号分隔开的参数序列
		(3)字符串:将字符串用逗号分隔开,变为用逗号分隔开的参数数组
	const tfboys=['易','王源','王俊凯'];
	function chunwan(){
        console.log(arguments);
    }
	chunwan(tfboys);//只显示一个参数 里面装一个数组
	chunwan(...tfboys);//相当于chunwan('易','王源','王俊凯'),显示数组里面的三个参数
    

image-20210606141227945

image-20210606141503274

扩展运算符的应用
1.数组的合并
const arr1=[1,2,3];
const arr2=[4,5,6];
const newArr=[...arr1,...arr2];//[1,2,3,4,5,6]

2.数组的克隆 //只能实现浅拷贝,引用数据类型无法拷贝
const arr=[1,2,3];
const newArr=[...arr];//[1,2,3]

3.将伪数组(Nodelist.Htmlcollection之类的集合数组)转换为真数组
	const divs=document.querySelectorAll('div');
	const divArr[...divs];
	console.log(divArr);//[div,div,div....];

字符串的扩展

1.includes():includes():返回布尔值,表示是否找到了参数字符串。

2.startsWith():返回布尔值,表示参数字符串是否在原字符串的头部

3.endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

4.repeat(n):返回一个新字符串,表示将原字符串重复n次

5.padStart(length,str)和padEnd(length,str):一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串
'x'.padStart(4, 'ab') // 'abax'

6.trimStart()和trimEnd():trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串

7.replaceAll():可以一次性替换所有匹配。

数组的扩展

1.扩展运算符...
2.Array.from()方法用于将两类(类似数组的对象和遍历的对象)对象转为真正的数组
	(1)let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
	(2)let ps = document.querySelectorAll('p');
			Array.from(ps).filter(p => {
 			 return p.textContent.length > 100;
		});
	(3)function foo() {
  		var args = Array.from(arguments);
  			// ...
		}
3.Array.of()方法总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

4.数组实例的 copyWithin()
	写法:Array.prototype.copyWithin(target, start = 0, end = this.length
     接受三个参数:
	target(必需):从该位置开始替换数据。如果为负值,表示倒数。
	start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
	end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。                          
5.数组实例的find()和findIndex()
	(1)数组实例的find方法,用于找出第一个符合条件的数组成员,它的参数是一个回调函数,直到找出第一个返回值为true的成员,然后返回该成员
[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10
	(2)数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2
	(3)find和findIndex方法都可以接受第二个参数,用来绑定回调函数的this对象
function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26

6.数组实例的fill()
	fill方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)// [7, 7, 7]
new Array(3).fill(7)// [7, 7, 7]
	fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
['a', 'b', 'c'].fill(7, 1, 2)// ['a', 7, 'c']

7.数组实例的 entries(),keys() 和 values()
	keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
    (1)for (let index of ['a', 'b'].keys()) {
  console.log(index);
}// 0 1
	(2)for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}// 'a' 'b'
	(3)for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}// 0 "a"  1 "b"

8.数组实例的 includes(elm,start)
	表示某个数组是否包含给定的值,第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度,则会重置为从0开始。
[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

9.数组实例的 flat()和flatMap()
	(1)flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响
[1, 2, [3, 4]].flat()// [1, 2, 3, 4];
	flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
[1, 2, [3, [4, 5]]].flat()// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)// [1, 2, 3, 4, 5]
	如果不管有`多少层嵌套`,都要转成一维数组,可以用Infinity关键字作为参数。
[1, [2, [3]]].flat(Infinity)// [1, 2, 3]
	(2)flatMap()对原数组的每个成员执行一个函数,然后对返回值组成的数组执行flat()方法。该方法返回一个新数组
 // 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]

数值的扩展

1.二进制和八进制表示法
	分别用前缀0b(或0B)和0o(或0O)表示。
    0b111110111 === 503 // true
	0o767 === 503 // true

2.Number.isFinite()和Number.isNaN()
	Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity
    Number.isNaN()用来检查一个值是否为NaN
    
3.Number.parseInt()和Number.parseFloat()
	目的:是逐步减少全局性方法,使得语言逐步模块化。
    Number.parseInt === parseInt // true
	Number.parseFloat === parseFloat // true

4.Number.isInteger()用来判断一个数值是否为整数,不能超过53的二进制位

5.安全整数和 Number.isSafeInteger()
	能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
    
6.Math对象的扩展
	(1)Math.trunc()方法用于去除一个数的小数部分,返回整数部分
	(2)Math.sign()方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。会返回五种值:
		参数为正数,返回+1;
        参数为负数,返回-1;
        参数为 0,返回0;
        参数为-0,返回-0;
        其他值,返回NaN。
	(3)Math.cbrt()方法用于计算一个数的立方根。
        Math.cbrt(2)  // 1.2599210498948732
	(4)Math.expm1()返回 ex - 1,即Math.exp(x) - 1。
        Math.expm1(1)  // 1.718281828459045
	(5)Math.log1p()方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。
    (6)Math.sinh(x) 返回x的双曲正弦
	(7)Math.cosh(x) 返回x的双曲余弦
	(8)Math.tanh(x) 返回x的双曲正切
	(9)Math.asinh(x) 返回x的反双曲正弦

7.指数运算符**
    2 ** 2 // 2的2次方 4
	2 ** 3 // 2的3次方 8
	2 ** 3 ** 2//2的3的2次方  512


正则的扩展

1.RegExp 构造函数
	RegExp构造函数的参数两种情况
	Es5:(1)参数是字符串
		var regex = new RegExp('xyz', 'i');
// 等价于
		var regex = /xyz/i;
		(2)参数是一个正则表示式
        var regex = new RegExp(/xyz/i);
// 等价于
		var regex = /xyz/i;
		ES5 不允许此时使用第二个参数添加修饰符,否则会报错
	ES6:如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。
    new RegExp(/abc/ig, 'i').flags// "i"

2. y修饰符
	叫做“粘连”(sticky)修饰符。y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
	

函数的扩展

1.函数参数的默认值
	ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面
	function log(x, y = 'World') {
 		 console.log(x, y);
	}
	log('Hello') // Hello World
	log('Hello', 'China') // Hello China
2.函数的 length 属性
	指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,`指定了默认值后,length属性将失真,默认值后面的参数不计入length中`。
    (function (a) {}).length // 1
	(function (a = 5) {}).length // 0
	(function (a, b, c = 5,d,e,f) {}).length // 2
3.rest 参数
	ES6 引入 rest 参数(形式为...变量名)跟扩展运算符类似,用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中
4.name属性
	函数的name属性,返回该函数的函数名

对象的扩展

1.属性的简洁写法
let name="刘宏涛";
let change=function(){
console.log('6666');
}
(1)简化对象
const school ={
	name,   // -----name:name,
	change, //-----change:change,
	improve:function(){ //-----improve(){...}
		console.log(sss);
	}
}
(2)简化方法
    const o = {
      method() {
        return "Hello!";
      }
    };
// 等同于
    const o = {
      method: function() {
        return "Hello!";
      }
    };

2.属性名表达式
JavaScript 定义对象的属性,有两种方法
    // 方法一
    obj.foo = true;
    // 方法二
    obj['a' + 'bc'] = 123;
	ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内
    let propKey = 'foo';
	let obj = {
      [propKey]: true,
      ['a' + 'bc']: 123
    };
	log.obj
	//{foo:true;abc:123}
	表达式还可以用于定义方法名。
    let obj = {
      ['h' + 'ello']() {
        return 'hi';
      }
    };
	obj.hello() // hi

3.方法的 name 属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性

4.属性的可枚举性和遍历
	(1)可枚举性:对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象
      eg: let obj = { foo: 123 };
        Object.getOwnPropertyDescriptor(obj, 'foo')
        //  {
        //    value: 123,
        //    writable: true,
        //    enumerable: true,
        //    configurable: true
        //  }
		有四个操作会忽略enumerable为false的属性。
            for...in循环:只遍历对象自身的和继承的可枚举的属性。
            Object.keys():返回对象自身的所有可枚举的属性的键名。
            JSON.stringify():只串行化对象自身的可枚举的属性。
            Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
    (2)遍历
     ES6 一共有 5 种方法可以遍历对象的属性。
              /1.for...in:for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
			  /2.Object.keys(obj)返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
			  /3.Object.getOwnPropertyNames(obj),返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
			  /4.Object.getOwnPropertySymbols(obj)返回一个数组,包含对象自身的所有 Symbol 属性的键名。
			  /5.Reflect.ownKeys(obj)返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。 
  
5.super关键字
	this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
    const proto = {
          foo: 'hello'
        };
    const obj = {
          foo: 'world',
          find() {//如果使用function表达式则报错
            return super.foo;
          }
    };
    Object.setPrototypeOf(obj, proto);
    obj.find() // "hello"

6.链判断运算符`?.`
读取对象内部的某个属性,往往需要判断一下该对象是否存在
链判断运算符有三种用法:
    obj?.prop // 对象属性
    obj?.[expr] // 表达式
    func?.(...args) // 函数或对象方法的调用
eg:(1)a?.b// 等同于a == null ? undefined : a.b
   (2)a?.[x]// 等同于a == null ? undefined:a[x]
   (3)a?.b()// 等同于a == null ? undefined :a.b()   
   (4)a?.()// 等同于a == null ? undefined : a()

7.Null判断运算符??
	新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。这个运算符的一个目的,就是跟链判断运算符?.配合使用,为null或undefined的值设置默认值
    const animationDuration = response.settings?.animationDuration ?? 300;


对象的新增方法

1.Object.is()
Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
    Object.is('foo', 'foo')
    // true
    Object.is({}, {})
    // false
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
    +0 === -0 //true
    NaN === NaN // false
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true

2.Object.assign(target,source1)
	基本用法:方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
    Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。
    
3.__proto__属性和Object.setPrototypeOf()和Object.getPrototypeOf()
	
	(1)__proto__:标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性.而是使用下面的	  Object.setPrototypeOf()(写操作)
		Object.getPrototypeOf()(读操作)
        Object.create()(生成操作)代替
	
    (2)Object.setPrototypeOf():
	作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法
    function setPrototypeOf(obj, proto) {
      obj.__proto__ = proto;
       // Object.setPrototypeOf(obj, proto);
      return obj;
    }

    (3)Object.getPrototypeOf():
    该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。如果参数是undefined或null,它们无法转为对象,所以会报错。
	Object.getPrototypeOf(null)
// TypeError: Cannot convert undefined or null to object

4.Object.keys(),Object.values()和Object.entries()
	(1)Object.keys():返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的`键名`
    var obj = { foo: 'bar', baz: 42 };
    Object.keys(obj)
    // ["foo", "baz"] 键名
	(2)Object.values():方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的`键值`。
    const obj = { foo: 'bar', baz: 42 };
    Object.values(obj)
    // ["bar", 42]
	(3)Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的`键值对数组`
    用途:
   	一:Object.entries的基本用途是遍历对象的属性,
        const obj = { foo: 'bar', baz: 42 };
    	Object.entries(obj)
    	// [ ["foo", "bar"], ["baz", 42] ]
   	二:将对象转为真正的Map结构。
	const obj = { foo: 'bar', baz: 42 };
    const map = new Map(Object.entries(obj));
    map // Map { foo: "bar", baz: 42 }
    
    

5.Object.fromEntries()
	Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象
    Object.fromEntries([
      ['foo', 'bar'],
      ['baz', 42]
    ])
    // { foo: "bar", baz: 42 }
	可以配合URLSearchParams对象,将查询字符串转为对象Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }

迭代器iteratorl

	迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的`访问机制`。
	迭代器的遍历是由for...of专门实现的,原生具有迭代器iterator结构的数据有:Array,Arguments,Set,Map,String, TypeArray,NodeList等
`注:Object原生对象不具备有迭代器,无法遍历,但可以通过在对象里面设置[symbol.iterator]方法设置一个遍历器`
    let obj={
        x:1,y:2,z:3,
        [Symbol.iterator](){
            return{
                next(){
                    return{value:属性值,done:true}
                }
            }
        }
    }
 工作原理:
   	(1)创建一个指针对象,指向当前数据结构的起始位置
	(2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员
	(3)接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
	(4)每调用next方法返回一个包含value和done属性的对象
 	作用:需要`自定义遍历数据`的时候 用到迭代器
 	对于遍历器对象来说,done: false和value: undefined属性都是可以省略的
 eg: 
	const xiyou=['唐','孙','猪','沙'];
	let iterator=xiyou[Symbol.iterator]();
	//调用对象方法
console.log(iterator.next());//value:'唐',done:false
console.log(iterator.next());//value:'孙',done:false
console.log(iterator.next());//value:'猪',done:false
console.log(iterator.next());//value:'沙',done:false
console.log(iterator.next());//undefined,done:true;

1.默认 Iterator 接口
	为所有数据结构,提供了一种统一的访问机制,即for...of循环,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有`Symbol.iterator属性`,就可以认为是“可遍历的”(iterable)

2.调用 Iterator 接口的场合
	(1)解构赋值
    对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
	(2)扩展运算符
    扩展运算符(...)也会调用默认的 Iterator 接口
    (3)yield*
     yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
        let generator = function* () {
          yield 1;
          yield* [2,3,4];
          yield 5;
        };
		var iterator = generator();
		iterator.next() // { value: 1, done: false }
		iterator.next() // { value: 2, done: false }
	(4)

迭代器自定义遍历数据

const banji={
	name:'终极一班',
	stus:['小明','小绿','小花','小杜'],
	[Symbol.iterator](){
		//索引
        let index=0;
        let _this=this;
        return:{
            next:function(){
                if(index<_this.stus.length){
                    const result={                    	      				value:_this.stus[index],done:false
                    }
                    //索引自加
                    idnex++;
                    //返回结构
                    return result;
                }else{
                    return{
                        value:undefined,done:true
                    };
                }
            }
        }
	}
}
//遍历这个对象
	for(let v of banji){
        console.log(v);
    } //'小明','小绿','小花','小杜'

for...of遍历

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。
for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

生成器generator

简介

	Generator 函数是 ES6 提供的一种异步编程解决方案,执行 Generator 函数会返回一个遍历器对象,生成器函数不是立即执行函数,必须经过调用才会执行。
    生成器特征:
        一是,function关键字与函数名之间有一个星号;
        二是,函数体内部使用yield表达式,定义不同的内部状态
eg:function* helloWorldGenerator() {
      yield 'hello';
      yield 'world';
      return 'ending';
    }
var hw = helloWorldGenerator(); 
Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象

yield表达式

 	Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,yield表达式就是暂停标志
    yield运行逻辑流程:
    (1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
    (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
	(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
	(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

与 Iterator 接口的关系

	由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
eg:	var myIterable = {};
    myIterable[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
[...myIterable] // [1, 2, 3]

next 方法的参数

	yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

async函数

eS2017 标准引入了 async 函数,使得异步操作变得更加方便,async 函数是 Generator 函数的语法糖。

promise

promise是异步编程的解决方案,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
特点:
	(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败)
	(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果

缺点:
	(1)无法取消Promise,一旦新建它就会立即执行,无法中途取消
    (2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
    (3)当处于pending状态时,无法得知目前进展到哪一个阶段
实例化promise
const p=new Promise(function(resolve,reject){
	setTimeout(function(){
		let data="数据";
		//成功
		resolve(data);
		//失败
		reject(data);
	},1000)
});
//调用promise对象then方法
p.then(function(value){
	//成功一般用value
},function(reason){
	//失败一般用reason   
})

Promise封装Ajax
const p=new Promise((resolve,reject)=>{
    //1.创建对象
    const xhr=new XMLHttpRequset();
    //2.初始化
    xhr.open("GET","服务器路径");
    //3.发送
    xhr.sends();
    //4.绑定事件,处理响应结果
    xhr.onreadystatechange=function(){
        if(xhr.readyState===4){
            if(xhr.staus >=200 &&xhr.status<300){
                //发送成功
                console.log(xhr.response);
                resolve(xhr.response)
            }else{
                console.error(xhr.status);
            }
        }
    }
});
//指定回调
p.then(function(value){
    console.log(value);
},function(reason){
    console.error(reason);
});

Set数据结构

es6提供的新的数据结构Set集合,类似于数组,但成员的值都是唯一的,集合实现了Iterator接口,可以使用扩展运算符和for...of...进行遍历
1.	集合属性和方法:
	(1)size  返回集合的元素的个数
	(2)add   增加一个新元素,返回当前集合
	(3)delete 删除元素,返回布尔值
	(4)has 检测集合中是否包含某个元素怒,返回布尔值
	(5)clear 清空成员
    
	let s=new Set(['小明','小红','小蓝','小花','小明']);
	//元素个数
	s.size;  结果为4,自动除去重复元素返回Set实例的成员总数
	//添加元素
	s.add('小绿');添加某个值,返回 Set 结构本身
	//删除元素
	s.delete('小红');删除某个值,返回一个布尔值,表示删除是否成功。
	//检测has
	s.has('小明');//true 返回一个布尔值,表示该值是否为Set的成员。
    
Set集合实践
	let arr=[1,2,3,4,5,3,2,1];
	//1.数组去重
	let result=[...new Set(arr)];//[1,2,3,4,5]
	//2.去除字符串里面的重复字符
	[...new Set('ababbc')].join('')//'abc'

2.遍历操作 
Set 结构的实例有四个遍历方法,可以用于遍历成员。
    Set.keys():返回键名的遍历器
    Set.values():返回键值的遍历器
    Set.entries():返回键值对的遍历器
    Set.forEach():使用回调函数遍历每个成员

3.WeakSet结构
	WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别
    (1)WeakSet 的成员只能是对象,而不能是其他类型的值。
	(2)WeakSet 不可遍历
    WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。作为构造函数,WeakSet 可以接受一个数组或类似数组的对象作为参数。该数组的所有成员,都会自动成为 WeakSet 实例对象的成员。
    WeakSet 结构有以下三个方法。
    WeakSet.add(value):向 WeakSet 实例添加一个新成员。
    WeakSet.delete(value):清除 WeakSet 实例的指定成员。
    WeakSet.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
    const a = [[1, 2], [3, 4]];
    const ws = new WeakSet(a);
    // WeakSet {[1, 2], [3, 4]}
    const b = [3, 4];
    const ws = new WeakSet(b);
	//报错,数组b的成员不是对象,加入 WeakSet 就会报错
	作用:使用 WeakSet 的好处是对实例的引用不会被计入内存回收机制,删除实例的时候,也不会出现内存泄漏。

Map数据结构

	es6提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应
    const map = new Map([
      ['name', '张三'],
      ['title', 'Author']
    ]);
    map.size // 2
    map.has('name') // true
    map.get('name') // "张三"
    map.has('title') // true
    map.get('title') // "Author"
	Map构造函数接受数组作为参数,实际上执行的是下面的算法,仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构,都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 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
1.Map实例的属性和操作方法
	(1)size属性:返回 Map 结构的成员总数。
    	const map = new Map();
            map.set('foo', true);
            map.set('bar', false);
            map.size // 2
	(2)Map.set(key, value):设置键名key对应的键值为value,然后返回整个 Map 结构,如果key已经有值,则键值会被更新,否则就新生成该键。
    const m = new Map();
	m.text="text";//这种方式添加的值无法绑定到m上,size没有变化
        m.set('edition', 6)        // 键是字符串
        m.set(262, 'standard')     // 键是数值
        m.set(undefined, 'nah')    // 键是 undefined
`·set方法返回的是当前的Map对象,因此可以采用链式写法`
        let map = new Map()
          .set(1, 'a')
          .set(2, 'b')
          .set(3, 'c');
	(3)Map.get(key):读取key对应的键值,如果找不到key,返回undefined。
        const m = new Map();
        const hello = function() {console.log('hello');};
        m.set(hello, 'Hello ES6!') // 键是函数
        m.get(hello)  // Hello ES6!
	(4)Map.has(key):has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
    (5)Map.delete(key):删除某个键,返回true。如果删除失败,返回false
    (6)Map.clear():clear方法清除所有成员,没有返回值
2.Map的遍历
const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);
/map.keys方法
for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"
/map.values方法:
for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"
/map.entries方法
for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

Map的数据类型转换

(1)Map 转为数组,Map 转为数组最方便的方法,就是使用扩展运算符(...)
        const myMap = new Map()
          .set(true, 7)
          .set({foo: 3}, ['abc']);
        [...myMap]
        // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
(2)数组转为Map:将数组传入 Map 构造函数,就可以转为 Map。
        new Map([
          [true, 7],
          [{foo: 3}, ['abc']]
        ])
        // Map {
        //   true => 7,
        //   Object {foo: 3} => ['abc']
        // }
(3)Map转为对象(循环)
eg:   function strMapToObj(strMap) {
      let obj = Object.create(null);
      for (let [k,v] of strMap) {
        obj[k] = v;
      }
      return obj;
    }

    const myMap = new Map()
      .set('yes', true)
      .set('no', false);
    strMapToObj(myMap)
    // { yes: true, no: false }
(4)对象转为Map:对象转为 Map 可以通过Object.entries()。
eg:  let obj = {"a":1, "b":2};
	 let map = new Map(Object.entries(obj));
(5)Map 转为 JSON
	Map 转为 JSON 分两种情况。
    /1.Map 的键名都是字符串,可以选择转为对象JSON。
    function strMapToJson(strMap) {
      return JSON.stringify(strMapToObj(strMap));
    }
    let myMap = new Map().set('yes', true).set('no', false);
    strMapToJson(myMap)
    // '{"yes":true,"no":false}'
	/2.Map 的键名有非字符串,可以选择转为数组 JSON。
    function mapToArrayJson(map) {
      return JSON.stringify([...map]);
    }
    let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
    mapToArrayJson(myMap)
    // '[[true,7],[{"foo":3},["abc"]]]'

(6)JSON 转为 Map
eg:  function jsonToStrMap(jsonStr) {
          return objToStrMap(JSON.parse(jsonStr));
        }
     jsonToStrMap('{"yes": true, "no": false}')
    // Map {'yes' => true, 'no' => false}

WeakMap

1.含义:WeakMap结构与Map结构类似,也是用于生成键值对的集合,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏
2.WeakMap与Map的区别有两点。
	(1)WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
eg:    const map = new WeakMap();
        map.set(1, 2)
        // TypeError: 1 is not an object!
      WeakMap的键名所指向的对象,不计入垃圾回收机制。
     (2)WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
3.WeakMap 的语法      
	WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。
    const wm = new WeakMap();
        // size、forEach、clear 方法都不存在
        wm.size // undefined
        wm.forEach // undefined
        wm.clear // undefined

ES6的面向对象

class类

定义
	通过class关键字,可以定义类,基本上es6的class可以看作只是一个语法糖,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语言
   `注意`:
   		1.class类不需要加上function这个关键字
   		2.方法与方法之间不需要逗号分隔,加了会报错
        3.类必须要用new来调用
        4.class中protoType上得方法不可以遍历
作用
	(1)class声明类
	(2)constructor定义构造函数初始化
	(3)extends继承父类
	(4)super调用父级构造方法
	(5)static定义静态方法和属性
	(6)父类方法可以重写
class Animal{
    _height='50cm';//实例属性
	static _weight='2.5kg';//静态属性---提案
	#age=1;//私有属性
    //构造函数,每个类都必须有,如果不写,js引擎默认添加一个空的构造函数,当new一个对象时,会自动调用constructor
    constructor([args]){
        this.arg1=arg1;
    }
    //方法-不能有function关键字方法与方法之间不能用逗号隔开,类的所有方法都是绑定在prototype上,并且不能被遍历(es5中可以被遍历)
    getkind(){//code}
     //静态方法---不属于实例
        static staticMethod(){}
}

//类只能被new调用
let cat =new Animal([args...]);//实例化
eg:
	function Phone(brand,price){
        this.brand=brand;
        this.price=price;
    }
 	//添加方法
	Phone.protoType.call=function(){
        console.log('我可以打电话')
    }
	//实例化对象
	let HuaWei=new Phone("华为",5999);
	console.log(HuaWei);

----class写法
	class Phone{
        //构造方法,名字不能修改
        constructor(brand,price){
            this.brand=brand;
            this.price=price;
        }
        //方法必须用改方法,不能使用es5的对象完整形式
        call(){
            console.log('我可以打电话')
        }
    }
let onePlus=new shouji("1+",1999)
set和get函数拦截
class Animal{
    constructor(kind,color){
        this.kind=kind;
        this.color=color;
    }
    get Voice(){//拦截获取voice属性
        console.log("获取voice值");
        return 'vioce...'
    }
    set voice(v){//设置获取voice属性
        console.log("设置voice值"+v)
    }
}
let cat=new Animal('猫咪','yellow');
cat.voice='喵喵~';
console.log(cat.voice);//"获取voice值" 'vioce...'
class表达式
const Myclass=class Me{
	getName(){
        return Me.name;
    }
}
//类得名字为Me,但Me只在class得内部使用,指代当前类,在class外部只能使用Myclass引用
let inst=new Myclass();
inst.getName();//Me
Me.name;//报错

class严格模式
 类和模块的内部默认为严格模式,不需要使用'use strict'指定运行模式
 (1)不存在变量提升(主要)
 (2)name属性
 (3)generator方法
 		在class类里面,在某个方法之前加上*号,就表示方法是一个遍历器
	eg: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')){
    console.log(x)
}
//hello
//world
(4)this指向
	类的方法内部如果含有this,它默认指向类的实例。一旦单独使用该方法,很可能报错。
    解决方案:
		/1.在构造方法中绑定this
		/2.使用箭头函数
        /3.使用Proxy,获取方法的时候,自动绑定this

class静态方法

	(1)如果静态方法包含this关键字,这个this指的是类,而不是实例
    (2)父类的静态方法,可以被子类继承
		lass Foo {
              static classMethod() {
                return 'hello';
              }
            }
        class Bar extends Foo {
        }
		Bar.classMethod() // 'hello'
	(3)实例属性的新写法 
    	实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。
        class Counter {
  		_count = 0;//实例属性
		increment() {
            this._count++;
          }
		}
	(4)静态属性
   	 ES6 明确规定,Class 内部只有静态方法,没有静态属性.
     有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。
    class MyClass {
      static myStaticProp = 42;

      constructor() {
        console.log(MyClass.myStaticProp); // 42
      }
    }
    
    
eg:    function Phone(){ 
        }
        Phone.name='手机';
        Phone.Change=function(){
           log("改变世界")
        }
Phone.protoType.size='5.5inch';
let nokia=new Phone();
log(nokia.name);//undefined
log(nokia.size);//5.5inch
//实例化对象无法继承获取到构造函数的静态成员
----class写法
class Phone{
    static name='手机';
    static chang=function(){
        log('改变世界')
    }
}
let nokia=new Phone();
log(nokia.name)//undefined
log(Phone.name)//手机

class私有方法和私有属性

image-20210613160726831

	私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问
  私有方法做法:
	1.在命名上加以区别
    在类的外部,还是可以调用到这个方法
        class Widget {
          // 公有方法
          foo (baz) {
            this._bar(baz);
          }
          // 私有方法
          _bar(baz) {
            return this.snaf = baz;
          }
          // ...
        }
	2.私有方法移出类
    class Widget {
      foo (baz) {
        bar.call(this, baz);
      }
  		// ...
	}
//私有方法移出类
    function bar(baz) {
      return this.snaf = baz;
    }

私有属性做法:
	为class加了私有属性。方法是在属性名之前,使用#表示。
    class IncreasingCounter {
      #count = 0;
      get value() {
        console.log('Getting the current value!');
        return this.#count;
      }
      increment() {
        this.#count++;
      }
}
//只能在类的内部使用(this.#count)。如果在类的外部使用,就会报错。
const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错
	判断私有属性
    	(1)try...catch 可读性差
        (2)in关键字
        class A {
          #foo = 0;
          m() {
            console.log(#foo in this); // true
            console.log(#bar in this); // false
          }
}

class实现继承

1.继承
	Class 可以通过extends关键字实现继承,子类通过extends关键字,继承了Father类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类
    class Father {}
	class Son extends Father {}
	`注意`:父级的构造函数construator无法直接被继承,需要通过子级的构造函数去用super关键字调用,否则新建实例时会报错
    before:class Point { /* ... */ }
	class ColorPoint extends Point {
          constructor() {}
	}
	let cp = new ColorPoint(); // 报错
	after:class ColorPoint extends Point {
          constructor(x, y, color) {
            super(x, y); // 调用父类的constructor(x, y)
            this.color = color;
      }
  	toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

2.Object.getPrototypeOf()
	方法可以用来从子类上获取父类。
    Object.getPrototypeOf(ColorPoint) === Point
// true 可以使用这个方法判断,一个类是否继承了另一个类

3.super关键字
	super这个关键字,既可以当作函数使用,也可以当作对象使用
    (1)第一种情况,super作为函数调用时,代表父类的构造函数,ES6 要求,子类的构造函数必须执行一次super函数。即super()函数调用
    class A {}
    class B extends A {
      constructor() {
        super();
      }
	}
//new.target指向当前正在执行的函数
class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B
	(2)第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。即super.函数名()
    class A {
      A.prototype.x = 2;
      constructor() {
        this.p = 2;
      }
    }
    class B extends A {
      get m() {
        return super.p;
      }
      constructor(){
          super();
          console.log(super.x) // 2
      }
    }
    let b = new B();
	
    b.m // undefined super指向父级的原型对象protoType,所以无法获取。但可以把父级的属性方法写在prototype上就可以通过super关键字获取

4.类的 prototype 属性和__proto__属性
	Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
   (1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
   (2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
   class A {
    }
    class B extends A {
    }
    B.__proto__ === A // true
    B.prototype.__proto__ === A.prototype // true

5.实例的 __proto__ 属性 
	子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性,子类的原型的原型,是父类的原型
var p1 = new Father(2, 3);
var p2 = new Son(2, 3, 'red');

p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true

image-20210613173620309

es5原型继承
function Phone(brand,price){
    this.brand=brand;
    this.price=price;
}
Phone.protoType.call=function(){
    log('打电话')
}
//子集
function SmartPhone(brand,prcie,color,size){
  	Phone.call(this,brand,price);
    this.color;color;
    this.size=size;
  }
//设置子级构造函数的原型
SmartPhone.protoType=new Phone;
SmartPhone.ProtoType.constructor=SmartPhone;
//声明子类的方法
SmartPhone.protoType.photo=function(){
    log('拍照')
}
SmartPhone.protoType.PlayGame=function(){
    log('玩游戏')
}
const chuizi=new SmartPhone('锤子',2499,'黑色','5.5inch')
log(chuizi);
----class继承
 class Phone{
     //构造方法
     constructor(brand,price){
         this.brand=brand;
         this.price=price;
     }
     //父类的成员属性
     call(){
         log('打电话')
     }
 }
class SmartPhone extends Phone{
    //构造方法
    constructor(brand,price,color,size){
        super(brand,price);
        //Phone.call(this,brand,price)
        this.color=color;
        this.size=size;
    }
    Phone(){
        log('拍照')
    }
    PlayGame(){
        log('玩游戏')
    }
}
const xiaomi=new SmartPhone('小米',799,'黑色','4.7inch')
log(xiaomi)

new.target属性

	new命令引入了一个new.target属性,该属性一般用在构造函数之中,`返回new命令作用于的那个构造函数`。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
Class 内部调用new.target,返回当前 Class。
	注意:子类继承父类时,new.target会返回子类

Proxy代理

1.概述:Proxy 用于修改某些操作的默认行为,Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
	(1)声明
	var proxy = new Proxy(target, handler);
	target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。如果handler没有设置任何拦截,那就等同于直接通向原对象。
    var target = {};
    var handler = {};
    var proxy = new Proxy(target, handler);
    proxy.a = 'b';
    target.a // "b"

proxy拦截行为

1.get(目标对象,操作的属性名,[,proxy实例本身]):拦截对象属性的获取操作
		var person = {
              name: "张三"
            };
        var proxy = new Proxy(person, {
          get(target, propKey){
            if (propKey in target) {
              return target[propKey];
            } else {
              throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
            }
          }
        });
        proxy.name // "张三"
        proxy.age // 抛出一个错误

2.set(目标对象,属性名,属性值,[Proxy实例本身]):拦截对象属性的设置操作
 
3.apply(目标对象, 目标对象的上下文对象(this),目标对象的参数数组。):apply方法拦截函数的调用、call和apply操作。
	eg:   
	var target=function(){
        return 'I am the target'; 
    };
    var handler = {
      apply: function () {
        return '拦截成功';
      }
    };
	var p = new Proxy(target, handler);
	p()// "拦截成功" 变量p是 Proxy 的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串 

4.has(目标对象, 需查询的属性名):has()方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,典型的操作就是in运算符,返回一个布尔值。
eg:
	var handler = {
      has (target, key) {
        if (key[0] === '_') {
          return false;
        }
        return key in target;
      }
	};
    var target = { _prop: 'foo', prop: 'foo' };
    var proxy = new Proxy(target, handler);
    '_prop' in proxy // false
    如果原对象的属性名的第一个字符是下划线,proxy.has()就会返回false,从而不会被in运算符发现。
    
5.construct(目标对象, 构造函数的参数数组,[new命令作用的构造函数]):拦截 Proxy 实例作为构造函数调用的操作,比如new,`construct()方法返回的必须是一个对象,否则会报错`。
 eg:  const p = new Proxy({}, {
          construct: function(target, argumentsList) {
          return {};
      }
    });
	new p() // 报错Uncaught TypeError: p is not a constructor

6.deleteProperty()用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除
eg:   var handler = {
      deleteProperty (target, key) {
        invariant(key, 'delete');
        delete target[key];
        return true;
      }
    };
    function invariant (key, action) {
      if (key[0] === '_') {
        throw new Error(`Invalid attempt to ${action} private "${key}" property`);
      }
    }
    var target = { _prop: 'foo' };
    var proxy = new Proxy(target, handler);
    delete proxy._prop;
// Error: Invalid attempt to delete private "_prop" property

7.defineProperty()方法拦截了Object.defineProperty()操作。

8.getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。

9.getPrototypeOf()方法主要用来拦截获取对象原型,具体来说,拦截下面这些操作
        (1)Object.__proto__
        (2)Object.isPrototypeOf()
        (3)Object.getPrototypeOf()
        (4)Reflect.getPrototypeOf()
        (5)instanceof
10.isExtensible()方法拦截Object.isExtensible()操作。

11.ownKeys()方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作
	(1)Object.getOwnPropertyNames()
    (2)Object.getOwnPropertySymbols()
    (3)Object.keys()
    (4)for...in循环
12.preventExtensions()方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值

13.setPrototypeOf()方法主要用来拦截Object.setPrototypeOf()方法。

Reflect反射

	Reflect与ES5的Object有点类似,包含了对象语言内部的方法,Reflect也有13种方法,与proxy中的方法一一对应。Proxy相当于去修改设置对象的属性行为,而Reflect则是获取对象的这些行为。

Module模块化

ES6模块

1.定义
	将大程序拆分为相互依赖的小文件,再用简单的方法拼装起来。常用的模块有CommonJS和AMD模块,COmmonJS用于服务器,AMD用于浏览器。es6中添加了一个模块---es6模块  es6模块的设计思想是`尽量的静态化,使得编译时就能确定模块的依赖的关系以及输入和输出的变量`
    
2.CommonJS和JS模块的区别
	CommonJS模块的实质是利用require方法整体加载fs模块(加载fs的所有方法),生成一个对象
    es6模块不是对象,它是通过export命令显示指定输出的代码,再通过import命令输入

export和improt命令

1.	ES6模块功能主要由两个命令构成:export和import ,export命令用于规定模块的对外接口,import命令用于输出其他模块提供的功能 
    写法:
 //一个js里面---模块js
    export var a='123',
    export var b='456',
    export var c='789',
     //或者写成 export {a,b,c}
 
 //另外一个js里面---载入js
    import {a,b,c} form '模块js',

2.as关键字
可以更改对外接口传的变量名或函数名
//输出
export{
	a as A,
    b as B,
    c as C
}
//输入
import{
    A as AA,
    B as BB,
    C as CC
}
import命令输入的变量都是只读的,因为它的本质是输入接口,无法对变量进行赋值操作,但import获取的是一个对象就可以修改它的属性(建议不要轻易修改属性)。

模块的整体加载

	除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。但无法加载到export default模块化的变量或方法
// circle.js
export function area(radius) {
  return Math.PI * radius * radius;
}
export function circumference(radius) {
  return 2 * Math.PI * radius;
}
// main.js
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
`注意`:对对象进行加载只能是静态分析,不允许运行时改变

export default命令

1.export default命令,为模块指定默认输出。
2.使用export default时,对应的import语句不需要使用大括号
3.一个模块只能有一个默认输出,因此export default命令只能使用一次

4.export default命令本质:是输出一个叫做default的变量或方法,所以它后面不能跟变量声明语句。将后面的值,赋给default变量.
eg:  var a = 1;
     export default a;

export和import的复合写法

	如果在`一个模块之中`,先输入后输出同一个模块,import语句可以与export语句写在一起。export命令输出的接口没有直接导到当前模块,而是将模块对外转发export命令的接口,然后再利用import命令导入当前模块里面
export { foo, bar}from 'my_module';
// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar }; 

import()

	由于export和import都是放在模块的顶层,js引擎处理在执行代码时对export和import会优先处理再作编译,这样会导致报错
// 报错
if (x === 2) {
  import MyModual from './myModual';
}
es6提供了一个解决方案 import()函数,可以支持动态加载模块,import()函数返回一个promise对象
import('模块js')
    .then(module => {
    module.loadPageInto(main);
  })
    .catch(err => {
    main.textContent = err.message;
  });

Module的加载实现

1.在浏览器和Node.js中加载ES6模块
2.实现加载中实际开发问题:循环加载

浏览器加载

1.传统方法
	通过script标签加载模块脚本
    (1)页面内嵌脚本
    	<script> 
        //module code
        </script>
    (2)外部脚本
    <script src='./xxx.js'>
    </script>
 注意:默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。下载和执行的时间就会很长,因此造成浏览器堵。

2.解决浏览器同步方案:
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>

3.defer和async属性区别
	defer要等到整个页面在内存中正常渲染结束,才会执行。----渲染完再执行
    async是一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染---下载完就执行

加载规则

1.浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。
eg:<script type="module" src="./foo.js"></script>
	浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
posted @ 2022-03-24 21:19  残星落影  阅读(33)  评论(0)    收藏  举报