1.重温ES6新语法特性 (上)

269 阅读35分钟

虽然ES6用了很久了,但是从没有记录过相关内容,学习新增API的同时,当做温习,查漏补缺(阮一峰ES6)。

1.ECMAScript 简介

  • 通常作为 JavaScript 的标准化规范
  • JavaScript 是 ECMAScript 的拓展语言,ECMAScript 只是提供了最基本的语法,是一种规格,而 JavaScript 则是具体的实现。
    image.png

1.1 ECMAScript 标准是谁定制?

TC39 - 即 ECMA 组织的 39 号技术委员会,是一个推动 JavaScript 发展的委员会,由总舵互联网公司和各大主流浏览器厂商的代表共同组建,是一个开放性的指导委员会。当前 ECMSAScript 标准都是由 TC39 委员会制定的。

1.2 ES6 与 ECMAScript 2015 的关系

2011 年,ECMAScript 5.1 版发布后就开始制定 6.0 版。因此 ES6 这个词的原意指 JavaScript 语言的下一个版本。正常的版本迭代应该是 6.0, 6.1, 6.2 等等, 但是因为新版本的语法功能太多,且制定过程中还有其他人和组织不断地提交新功能,因此无法再一个版本中引入所有的功能。最后制定者将标准流程升级:

image-1.png
最后制定流程

阶段描述
Stage 0Strawman(展示阶段)
Stage 1Proposal(征求意见阶段)
Stage 2Draft(草案阶段)
Stage 3Candidate(候选人阶段)
Stage 4Finished(定案阶段)

ES6 的第一个版本于 2015 年 6 发布,正式命名为 ES2015(ECMAScript 2015),之后都以年份命名 ES2015,ES2016,ES2017...ES2024,在这之后 ES6 成为一个历史名词,表示 Javascript 的下一代标准,涵盖了 ES2015,ES2017...

1.3 Babel

Babel 是 ES6 转码器,将 ES6 转为 ES5,从而在老版本浏览器运行。

name作用
@babel/core是 Babel 的核心包,将 JavaScript 代码解析成抽象语法树(‌AST),使得其他插件能够分析和转换这些代码 ‌,例如处理箭头函数等新的语法特性
.bablercbabel 配置文件,设置转码规则和插件{ "presets": ["@babel/preset-env","@babel/preset-react"],"plugins": [] }
@babel/preset-env是预设是一系列插件的集合,包含了常用 es2015,es2016, es2017 等最新的语法转化插件,允许我们使用最新的 js 语法,比如 let,const,箭头函数等等,但不包含低于 Stage 3 的 JavaScript 语法提案
@babel/preset-reactBabel 的一个插件预设,它用于将 React JSX 语法转换为 React.createElement 调用。它提供了处理 JSX 语法和 React 组件的能力
@babel/clibabel 自带的命令行集成工具,可在 cmd 运行 babel 解析过程。并可以设置包括输出路径等等信息(npx babel example.js --out-file compiled.js)
@babel/node是一个与 Node.js CLI 完全相同的 CLI, 除此之外,还具有在运行之前使用 Babel 预设和插件进行编译的额外优势。提供一个支持 ES6 的 REPL 环境。它支持 Node 的 REPL 环境的所有功能,而且可以直接运行 ES6 代码
@babel/register改写 require 命令,为它加上一个钩子。此后,每当使用 require 加载.js、.jsx、.es 和.es6 后缀名的文件,就会先用 Babel 进行转码,但它只会对 require 命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用
polyfillBabel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API。如 Iterator,Generator,Set, Map 等全局对象,都不会转码,如果要执行这些方法,则需要为当前环境暗转垫片(core-js, regenerator-runtime)

2. ES6新语法

name编写注意点
let代码块内有效1. let为局部变量,var为全局变量,当var执行时,每一次循环,i的值都将变更:
- let a = []; for (let i = 0; i < 10; i++) {a[i] = () => {console.log(i)}}; a[1](); => 1
-let a = []; for (var i = 0; i < 10; i++) {a[i] = () => {console.log(i)}}; a[1](); => 10

2. 无变量提升:
-typeof x; let x; => 将报错
- (x = y, y = 3) => x + y => 将报错

3. 同一作用于不能重复
const只读常量,一旦申明,无法变更1. 作用域与let相同,
2. 不存在变量提升
3. 需要先定义后使用
4. const实际是只想的内存地址的数据不能变更:const a = {x: 1}; a.x = 10; , 它的属性是可以修改的
析构按照一定模式,从数组和对象中提取值,对变量进行赋值
右边需是具备Iterator接口的数据
1. 数组析构:
-let [x, y, z] = [{a: 1}, 10, []]
-let [a, [b], [[c]], x] = ['avalue', ['bvalue'], [['cvalue']]] => x等于undefined
let [x, y, z] = new Set(['a', 'b', 'c']);
-let [x = 1, y = x] = [1, 2];

2. 对象析构:
let { first: f,second, last = 10} ={ first: 'hello'}; => f 等于hello, second默认值undefined, last 等于默认值10, first是模式,变量名为f
和数组一样,可深层嵌套,let { p, p: [x, { y }] } = {p: ['Hello',{ y: 'World' }]} => 不仅p可以被赋值,x,y还能析构value

3. 析构特别用法:
- 交换变量:let x = 10; let y = 20; [x, y] = [y, x]
遍历Map析构key: const map = new Map();map.set('first', 'hello');for (let [key, value] of map) {}
- var {x:y = 3} = {x: 5}; => x模式取值,value为5,赋值给y, y=> 5
- const arr = [1,2,3];const {0:first, [arr.length-1]: last} = arr; => first: 1, last: 3
字符串拓展新的支持1. 对 Unicode 的支持:
-限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示:"\uD842\uDFB7" => "𠮷"
-在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript 会理解成\u20BB+7,ES6使用大括号,能正确解读:'hell\u{6F}' => hello, "\u{41}\u{42}\u{43}" => abc , '\u{1F680}' === '\uD83D\uDE80' => true

2. 添加遍历器接口,使得字符串可通过for...of遍历(可遍历Unicode ):
for(let x of 'abc') {console.log(x); // 输入a b c}

3. 使用``增强模板字符串来标识,模板字符串的大括号内部,就是执行 JavaScript 代码:
`hello name1+1={name},1 + 1 = {1+1}`

4. 可以换行表示多行字符串
5.标签模板:
functionName`hello` => 等价于functionName(['hello'])
模板字符里面有变量,会将模板字符串先处理成多个参数,再调用函数,变量作为后面的参数: let a = 10 ; let b = 5; functionName`hello a+b,world,{a + b}, world, {a}` => 等价于functionName(['hello', 'world'], 15, 5)
String Method新增方法1. String.fromCodePoint(): ES5的String.fromCharCode()只能识别小于0xFFFFUnicode 码点, String.fromCodePoint()范围更大,String.fromCodePoint方法有多个参数,则它们会被合并成一个字符串返回。
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'

2.String.raw():返回一个斜杠都被转义的字符串
String.raw`Hi\n${2+3}` => 'Hi\n5'

3.codePointAt(): 如果是4个字节的字符,ES5的charCodeAt只能分别返回前两个字节和后两个字节的值,而codePointAt可以正确处理4 个字节储存的字符。
测试字符是由4个字节组成:const is32Bit = (c)=> c.codePointAt(0) > 0xFFFF;
正确读取字节内容,使用for of或者拓展运算法:
- let s = '𠮷a'; for(let c of s) {console.log(c.codePointAt(0).toString(16));}
- let c = [...s]; c.forEach(d => console.log(d.codePointAt(0).toString(16)))

4. normalize(): 有些语言由语调符号和重音符号,Unicode提供2种方式,直接提供重音符号例如Ǒ(\u01D1),还有一种是合成符号,由O(\u004F)ˇ(\u030C)组成Ǒ,语义等价,但js无法识别,可通过normalize处理
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
不能识别三个或三个以上字符的合成。这种情况下,还是只能使用正则表达式

5.include(),startWith(), endWith(), repeat():
include(): 是否包含参数字符串
startWith():是否以参数字符串作为头部
endWith(): 是否以参数作为尾部
repeat(n):将字符串重复n次返回, 参数如果是小数,会取整数位参数,为负或Infinity数则报错,0 到-1 之间的小数,则等同于 0,NAN等同于0,是字符串则先转为数字, 'x'.repeat(5) => xxxxx

6. matchAll():返回一个正则表达式在当前字符串的所有匹配
replaceAll():与replace()类型,但是匹配多次:'aabbcc'.replaceAll('b', '_') 等价 'aabbcc'.replace(/b/g, '_')
替换文本可是特殊字符:
'abc'.replaceAll('b', '$&') => $&匹配字符本身,即 字符b,返回abc
'abbbc'.replaceAll('b', '\`') =\> `:匹配结果之前的字符串, 第一个b前面是a, 第二个b前面是ab, 所以第一个b替换为a,第二个b替换为ab,第三个b替换为abb => 'aaababbc'
'abbbc'.replaceAll('b', `$'`) => $': 匹配结果后面的文本,'abbcbccc'
'abbc'.replaceAll(/(ab)(bc)/g, '221') => 2bc,2是bc, 1是ab, 交换两个位置, 结果是bcab
'abc'.replaceAll('b', '$$') => $$就是符号,输出符号,输出

7.at():一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)
'hello'.at(-1) => 输出o
RegExp 正则es5: RegExp('xyz', 'i') or RegExp(/xyz/i)
es6: RegExp(/xyz/ig, 'i') => i会替换ig修饰符
1.es5字符串的match()/replace()/search()/split(), ES6都实现到了RegExp身上

2.加上u修饰符号,支持资格字节的UTF-16编码:
- /^\uD83D/u.test('\uD83D\uDC2A') => false
- (.): 表示处换行符外的任意单个字符,使用u修饰符,正确校验长度:var s = '𠮷';/^.$/u.test(s) => true(es5会认为这个长度为2,2个字符)
-使用大括号表示Unicode字符
/\u{62}/u.test('b')
-使用u修饰符,结合量词使用,识别码点大于0xFFFF的 Unicode 字符,/𠮷{2}/u.test('𠮷𠮷')
- 预定义模式:'𠮷𠮷'.match(/[\s\S]/gu).length => 2

3:RegExp.prototype.unicode 属性:判断正则是否设置了u修饰符
const r2 = /hello/u; => r2.unicode === true

3.y修饰符:同g相似,全局匹配,区别:多次执行匹配,y修饰符确保匹配必须从剩余的第一个位置开始,g只要剩余字符中存在匹配就可以。
: var s='aa_a'; var r1 = /a+/g;var r2 = /a+/y; r1.exec(s); => aa r1.exec(s); => a, r2.exec(s) => aa, r2.exec(s) => null, 第二次剩余字符为_a, 不是以a开头的,所以匹配失败
RegExp.prototype.sticky 属性: r2.sticky => true, 表示是否设置了y 修饰符

4. es5: /abc/ig.source => 返回 正则 abc
ES6: /abc/ig.flags => 返回表达式的修饰符 gi

数值新增内容1. 二进制前缀(ob, oB)与八进制(0o,0O)表示 => Number('0b111') => 7; 0b111 == 7 => true

2.Number.isFinite()用来检查一个数值是否为有限的,只对数字有效,其他类型均false

3.Number.isNaN()用来检查一个值是否为NaN,只对数字有效,其他类型均false

3.Number.parseInt(), Number.parseFloat():移植到Number对象上面,行为完全保持不变,使用方式Number.parseInt('12.34')

4.Number.isInteger():判断一个数值是否为整数

5.Number.isInteger():用来判断一个数值是否为整数

4.Number.EPSILON:它表示 1 与大于 1 的最小浮点数之间的差,实际是JS能够表示的最小精度,目的在于浮点数计算不精准,设置一个误差返回,如果误差小于这个值,则表示不存在误差
- 0.1 + 0.2 - 0.3 < Number.EPSILON: 0.1+0.2 不等于0.3,但是误差小于Number.EPSILON,则认为浮点数相等

5.安全整数和 Number.isSafeInteger(): Number.MAX_SAFE_INTEGER (最大安全整数),和Number.MIN_SAFE_INTEGER(最小安全整数)

6.Math 对象的扩展:
- Math.trunc:用于去除一个数的小数部分,返回整数部分。非Number会先转Number => Math.trunc(4.5) => 4; Math.trunc(true) => 1; Math.trunc(undefined) => NaN
-Math.sign:判断一个数到底是正数(+1)、负数(-1)、正零(0), 负零(-0),其他返回NaN,非数值先将其转换为数值。Math.sign(-5) => -1
- Math.cbrt(): 用于计算一个数的立方根,非数字先转换为数字。 => Math.cbrt(8) => 2
- Math.clz32():将参数转为 32 位无符号整数的形式,前面将填补多少个0。 Math.clz32(1)=> 31;将在前面填补31个0
-Math.imul:返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数
-Math.fround:返回一个数的32位单精度浮点数形式。将64位双精度浮点数转为32位单精度浮点数。如果小数的精度超过24个二进制位,返回值就会不同于原值,否则返回值不变(即与64位双精度值一致)。
Math.fround(2 ** 24 - 1) => 16777215
Math.fround(0.3) => 丢失精度(0.30000001192092896)
Math.fround(1.125)=>未丢失进度(1.25)
- Math.hypot(): 返回所有参数的平方和的平方根,Math.hypot(3, 4, 5); => 7.0710678118654755
- Math.expm1(): Math.expm1(x)返回 ex - 1,即Math.exp(x) - 1
-Math.log1p():Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)
-Math.log10():Math.log10(x)返回以 10 为底的x的对数。如果x小于 0
Math.log2(): Math.log2(x)返回以 2 为底的x的对数
- 双曲函数方法:Math.sinh(x),Math.cosh(x) ,Math.tanh(x) ,Math.asinh(x),Math.acosh(x),Math.atanh(x)
函数新增内容1.函数参数默认值,析构获取参数.通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数:function foo(x, { a, b},y = 1){}

2.函数的length属性:返回没有指定默认值的参数个数,设置了默认值的参数不是尾参数,则后面的参数将不计入length。function foo(x, { a, b},y = 1, z, d){}; foo.length => 2。

3.参数的默认值,函数进行声明初始化时参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失
-var x = 1;var c = 3;function f(x, y = x, z= c, b=function() { c = 10}) {c = 4; b(); console.log(y, z, c);}f('x'); => x 3 10, 函数参数有生成了单独作用域,x 等于传入的‘x’, y = 'x', z等于外部定义的c,函数内部的c=4调用b()之前, b函数执行后,将外部c修改为10。如果函数内的c=4 改为 var c=4, 则b()无法影响函数内容定义的c

4. rest参数:获取函数多余参数,是一个数组。function(a, ...b) {} => b获取剩余参数,是数组

5:严格模式变更:es5函数内部可设置为严格模式,es6规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错

5.函数的name属性, 返回函数name,匿名函数返回空字符串

6.箭头函数:允许使用“箭头”。
- 箭头函数没有自己的this对象,所以也就不能用call()、apply()、bind()这些方法去改变this的指向
- 不可以当作构造函数
-不可以使用arguments对象,可以用 rest 参数代替。
-不可以使用yield命令,因此箭头函数不能用作 Generator 函数

6.不适合使用场景:
- 定义在对象的方法,该方法内部不适合使用this =>var a = 10; var obj = { a : 20, b:() => { console.log(this.a)}}; obj.b()
- 需要动态this的时候,也不应使用箭头函数。var b = document.getElementById('test'); b.addEventListener('click', () => { console.log(this)}) => this is window
数组新增1. 扩展运算符...: br - console.log(1, ...[2, 3, 4], 5) =>1 2 3 4 5
- 复制数组:var a = [1,2]; var b = [..a];
-合并数组:var a=[1,2]; var b= [2,3]; var c =[...a, ...b];
- 与解构赋值结合: var a =[1,2, 3,4]; var [first, ...rest] = a;
- 字符串变数组:var a = 'hello'; var arr = [...a];
- 扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构、获取页面的dom的nodelist

2.Array.from:将类数组可遍历的对象(例如Set和Map)转为真正的数组
-类数组转数组: var a = {'0': 1, '1': 2, length: 2}; const b = Array.from(a);
- 可遍历对象(NodeList 对象也可以):var a =new Set([1,2]); var c = Array.from(a);

3.Array.of: 将一组值,转换为数组
- Array.of(3, 11, 8) => [3,11,8]

4.Array.prototype.copyWithin(target, start = 0, end = this.length):在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。start,end:如果为负数,从末尾开始计算
- [1, 2, 3, 4, 5, 6, 7, 8].copyWithin(1, -4, -1)=>// -2相当于5号位,-1相当于8号位 => 1,5,6,7,5,6,7,8,如果要复制最后一位,则不传递end参数就行

5.find(),findIndex(),findLast(),findLastIndex() :
- [1, 4, -5, -10].find((currentValue, index,arr) => currentValue < 0), 返回n小于0第一个, 不存在返回undefined
- [1, 4, -5, -10].findIndex((currentValue, index,arr) => currentValue < 0), 返回n小于0第一个, 不存在返回-1
- find, findIndex接收第二个参数,作为绑定为调函数的this对象。let person = {name: 'zyh', age: 20};[10, 12, 26, 15].find(function(n) {return n>this.age}, person); => 函数不能是箭头,箭头函数this是window

6.fill(filldata, startPosition, endPosition): 使用给定的值填充一个数组。
- ['a', 'b', 'c'].fill(7) => [7,7,7]
[1,2,3,4,5].fill(7,2, 4) => [1,2,7,7,5]

7.entries():对键值对的遍历,keys():对键名的遍历, 和 values():对键值的遍历: 可用for ...of进行遍历

8.includes():某个数组是否包含给定的值,第二个参数表示搜索的起始位置,负数就从end开始数。[NaN].includes(NaN) => true

9.flat(num):将嵌套的数组“拉平”变成一维数组,num为拉平几维数组,默认1为,设置为3,则可将嵌套三维拉成一维数组,[1, 2, [3, [4, 5]]].flat(2)=>[1,2,3,4,5]
flatMap():flatMap()方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。

10.数值空位:var a = Array(3); // 输出 [,,],执行0 in a;返回false, o in [undefined, undefined]返回true
Object对象拓展1.属性简洁表示:const a ='x'; const b ={a}; // a简洁表示

2.表达式作为属性名称:const a = 'keyname'; const obj = {[a]: 'value'}

3.方法的 name 属性:函数有name属性,方法也有。const k ={method: () =>{console.log(z.method.name)}}; // 打印method
-通过getter、setter取值的方法name属性不在该方法上,而是在该方法的属性的描述对象的getset属性上,返回值会加上getsetconst z = {get foo() {}, set foo(x){}}; const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo'); console.log(descriptor.get.name); // 打印get foo
- bind方法创造的函数,name属性返回bound加上原函数的名字:var test=function(){};test.bind().name // 返回 bind test
- Function构造函数创造的函数,name属性返回anonymous: (new Function()).name // 返回anonymous - 对象方法是个Symbol,name返回Symbol值的描述: const k = Symbol('description'); const obj={[k]: () => {}}; console.log(obj[k].name); // 但因[description]

4.属性的可枚举和遍历:
-对象的enumerable可枚举性,如果为false则某些操作会忽略该属性。for...in,Object.keys(), JSON.Stringfy(), Object.assign(),他们处理Object自身可枚举的属性,for...in还会继承的可枚举的属性。Object.assign()是ES6新增的
- 属性的遍历:
for..in,自身和继承的可枚举属性,不含Symbol属性
Object.keys(obj):返回自身可枚举属性的数组,不含继承,不含Symbol属性
Object.getOwnPropertyNames(obj),返回自身所有属性集合,包含继承,包含不可枚举属性,不含Symbol属性
Object.getOwnPropertySymbols(obj):返回自身所有属性集合,包含继承,包含不可枚举属性,包含Symbol属性
Reflect.ownKeys(obj):返回自身所有属性,含Symbol,含不可枚举,但不含继承

super关键字,指向当前对象的原型对象。var parent={pkey: 'parent'}; var child={pkey: 'child', cmethod(){console.log(super.pkey);}, cfunc:function(){ console.log('super error, because this is function');}}; Object.setPrototypeOf(child, parent); child.cmethod(); // 打印parent, cfunc不能调用super,因为他是函数,不是方法

6.对象的扩展运算符:
- 解构赋值的拷贝是浅拷贝:let obj = { a: { b: 1 } };let obj2={...obj};console.log(obj2.a === obj.a) // true
- 只拷贝对象的属性,不会复制继承原型对象的属性:let parent={a: 1}; let child={b: 1}; chid.__proto__= parent; let {...c}= child;console.log(c); // 没有a属性,只有b属性
Object新增方法1.Object.is(): 同值相等。var a =[x]; var b = a; Object.is(a, b); // true

2.Object.assign(target, source1,source2):将源对象(source)的所有可枚举属性,复制到目标对象(target)。
- 是浅拷贝,只拷贝自身属性,不拷贝继承属性和不可枚举的属性。-
同名属性的替换,名称重复,后面的替换前面的-
数组处理:Object.assign([6, 7, 3], [4, 5]) // 输出[4,5,3],将数组作为对象处理,[4,5] => {0:4, 1: 5},替换了前者
- 复制getter函数赋值的对象,只取值,不影响赋值:const source = {x get foo() { return 1 }};const target = {};Object.assign(target, source); target.foo = 20

3.Object.setPrototypeOf(),Object.getPrototypeOf():设置/获取一个对象的原型对象(prototype)- const proto = {parentkey: 'xxx'}; const obj = Object.setPrototypeOf(obj, proto);
- let proto2 = Object.getPrototypeOf(obj)
运算符拓展新增1.指数运算符**:3**4 // 3*3*3*3等于81
ES6引入类型Symbol新增的原生数据类型1.类型Symbol表示独一无二的值:var a = Symbol('a'); var b = Symbol('a'); // a 不等于b
- Symbol不能用于计算(字符串、number都不行),可调用String转为字符串
- Symbol的参数就是它的描述

2. Symbol不会出现在遍历对象的时候(for...in, for...of, Object.keys()等),
- 它也不算私有属性,可以通过Object.getOwnPropertySymbols()获取对象的所有Symbol属性名
-Reflect.ownKeys()可以通过该方法返回对象的所有键名,包含Symbol类型

3.Symbol.for() 与Symbol.keyFor():
- Symbol.for():description一样,使用同一个Symbol值。将会被登记在全局环境通过key进行搜索,如果存在,直接返回,不存在new一个。const a = Symbol.for('same'); const b = Symbol.for('same'); a===b; // true
- Symbol.keyFor():返回一个已登记的 Symbol 类型值的key: let s1 = Symbol.for("foo");Symbol.keyFor(s1) //foo

4.内置的Symbol值:
- Symbol.hasInstance: 当对象调用instanceof运算符时foo instance Foo,实际调用的是Foo[Symbol.hasInstance](foo)class MyClass{ [Symbol.hasInstance](foo) {return foo instanceof Array}}; [1, 2, 3] instanceof new MyClass() // 内部实际调用的是hasInstance
- Symbol.isConcatSpreadable: 表示在concat数组时,是否可以展开。数组默认为true, 类数组默认为false。var a = [1,2];var b=[3,4]; b[Symbol.isConcatSpreadable]=false;a.concat(b, 'c') // 输出[1,2,[3,4], 'c']
-Symbol.species:指向构造函数,创建衍生对象(基于new的实例获取的值)时,会使用该属性。class MyArray extends Array {static get [Symbol.species]() {return Array; }}const a = new MyArray(); const b = a.map(x => x); // b类型是Array,不是MyArray
- Symbol.match:指向一个函数,当执行str.match(newObj)是调用。class MyMatcher {[Symbol.match](string) {return 'hello world'.indexOf(string);}} 'e'.match(new MyMatcher()) // 匹配出index为1
- Symbol.replace: Symbol.replace方法会收到两个参数,第一个参数是replace方法正在作用的对象。const x = {};x[Symbol.replace] = (a,b) => console.log(a,b);var a = 'Hello'.replace(xxx, 'World') // 打印出 Hello world
-Symbol.search: 指向一个方法。const x = {};x[Symbol.search] = (a) => console.log('worldHelloWord'.includes(a), a);var a = 'Hello'.search(x)
- Symbol.split :重新定义了字符串对象的split方法的行为。const x = {};x[Symbol.split] = (a) => console.log(a);var a = 'Hello'.split(x)
- Symbol.iterator: 指向该对象的默认遍历器方法
- Symbol.toPrimitive: 指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。et ab = {[Symbol.toPrimitive](hint) {switch (hint) {case 'number': return 123; default:throw new Error();}}}; 3 * ab ; // 等于 3 * 123 = 369
-Symbol.toStringTag: 用来设定一个字符串。设定的字符串会出现在toString()方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]或[object Array]中object后面的那个大写字符串。
var s = {}; s.toString();//输出'[object Object]'
var s = ({[Symbol.toStringTag]: 'Foo'}.toString()); s.toString(); //'[object Foo]' 不是Object是Foo
-Symbol.unscopables: 指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。with是已过期的语法,这里不探讨
Set与Map新增1.Set:类似于数组,但是成员是唯一的。可接受具有iterable接口的其他数据结构作为参数。
- 可以用于数组去重。[...new Set([1,2,3,2,2])]; // 输出 1,2,3
- 增、删、查、改、长度方法。const set =new Set([1,2,3,4,5]); set.size; set.add(10).add(100);set.has(10); set.clear();
- 遍历操作:
keys(),values(),entries(): 因为Set没有键名,所以keys()values()结果一样,entries()返回的[key,value]结构也相等
Set.prototype[Symbol.iterator] === Set.prototype.values:因为Set结构默认实例可比案例,且遍历生成器函数就是它的values方法,因此可以直接沈略values进行遍历。const set=new Set([1,2]); for(let v of set){console.log(v)}
forEach():const set=new Set([1,2]); set.forEach((value, key) => console.log(value))
交集(new Set([...a].filter(x => b.has(x))))、并集( new Set([...a, ...b]))、差集(new Set([...a].filter(x => !b.has(x))))

2.WeakSet: 结构与Set类似,但是值只能是Symbol或者对象。WeakSet是弱引用,它存放的对象,如果其他地方没有引用,则垃圾回收机制将回收它,不会考虑WeakSet的引用。因为垃圾回收机制何时运行不可预测,因此WeakSet不可比遍历 -
WeakSet的方法:add(),delete(),has(),没有遍历方法

3.Map:键值对集合。传统Object只能是字符串作为Key,而Map可以是各种类型的值作为Key
- 增删改查方法 set(),get(), size,has(), delete(),clear()const map=new Map(); map.set('a', 'b'); const obj={}; map.set(obj, 'c'); map.get(obj);map.has('a');map.delete(obj); map.size;map.clear();
-不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。const map=new Map([['key1', 'value1'], ['key2', 'value2']])
- key唯一,后面的值将覆盖前面的
- 遍历方法,Map的遍历顺序就是插入顺序:keys(), value(), entries(), forEach()for (let [key, value] of map.entries()) {console.log(key, value);}
结合数组的map,filter方法可以实现遍历和过滤:const map = new Map([[1, 'one'],[2, 'two'],[3, 'three']]); map.entries().map(() => {...})
对象转Map: new Map(Object.entries(obj)),Map转对象需要遍历处理

4.WeakMap: 与Map结构类似,但是不接收NULL作为键名。其键名指向的对象,均不计入垃圾回收机制,因此不具备迭代的功能。典型应用场景:在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除
拥有方法:get(),set(),has(),delete()
Proxy新增代理在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

1.创建调用:const target ={a: 1};const proxy = new Proxy(target, { get: (target, proxyKey)=> {return proxyKey + ': test'}}); proxy.x; // 打印'x:test',target的值不受影响。,如果直接修改prxoy.a = 10; 将直接修改target,因为这里没有设置set方法进行拦截

2.拦截器支持的方法:
- 拦截对象属性的读取get():const proxy = new Proxy(target, { get: (target, proxyKey)=> {return target[proxyKey]}});
-set():拦截某个属性的赋值操作。const proxy = new Proxy(target, { set: (target, propKey, value, receiver)=> {target[proxyKey] = value;}});,目标对象自身的某个属性不可写(writable: false),那么set方法将不起作用
- apply():apply方法拦截函数的调用callapply操作。三个参数分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。const proxy = new Proxy((a,b)=>{return a + b}, { apply (target, ctx, args) { console.log(Reflect.apply(...arguments) * 2);;}}); proxy.apply(null, [1,2]);// 输出6
- has():拦截propKey in proxy的操作,两个参数目标对象、需查询的属性名,返回布尔值。const proxy = new Proxy({a: 1, '_d':2}, { has (target, key) {return key.includes('_') ? false : (key in target)}}); '_d' in proxy; // false。原对象不可配置或者禁止扩展,这时has()拦截会报错。
- construct(): 拦截new命令。结果必须返回一个对象。 const p = new Proxy(function () {}, { construct: function(target, args) {return { value: args[0] * 10 };}});(new p(1)).value
- deleteProperty():拦截delete操作。返回false表示不能被删除。const p = new Proxy({_prop: 'test'}, { deleteProperty(target, key) { if (key.includes('_')) { return false; } else { delete target[key]; return true} }}); delete p._prop; // 删除失败
- defineProperty (target, key, descriptor):拦截了Object.defineProperty()操作。
- getOwnPropertyDescriptor(target, propKey): 拦截 Object.getOwnPropertyDescriptor(proxy, propKey)返回属性的描述对象。
- getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),Object.prototype.__proto__,Object.prototype.isPrototypeOf(), Reflect.getPrototypeOf(),instanceof,返回一个对象。
- isExtensible(target): 拦截Object.isExtensible(proxy),返回一个布尔值
- ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性(继承、Symbol、不可遍历的属性)
- preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。只有目标对象不可扩展时(即Object.isExtensible(proxy)false),proxy.preventExtensions才能返回true,否则会报错。var proxy = new Proxy({}, {preventExtensions: function(target) {Object.preventExtensions(target);return true;}}); Object.preventExtensions(proxy);proxy.n = 10; // 设置失败
- setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。

3.Proxy.revocable():返回可取消的Proxy实例。let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123;proxy.foo;// 输出123,再调用 revoke();proxy.foo; // 错误

4.this 问题: 只要Proxy进行代理,那么代理对象target内部this就指向了proxy,const proxy = new Proxy({ m: function(){ console.log('self', this)}}, {}); // this is proxy handler
Reflect新增Reflect1. 目的:
- 现在某些方法在ObjectReflect都存在,未来新方法将只部署在Refkect上。
- 当前Object的方法出现无法定义时会报错,而Reflect会返回false,更友好
- 让Object操作都变成函数行为,例如delete a.x 变成Reflect.deleteProperty(obj, name)
-ReflectProxy对象的方法一一对应,只要在Proxy的方法,在Reflect上就存在

2.Reflect的静态方法:
-Reflect.get(target, name, receiver), 同Proxy一样,getter函数this指向receiver: var a = {x: 1, y:2, get custom() { return this.x + this.y } }; Reflect.get(a, 'x'); Reflect.get(a, 'custom', {x: 10, y: 20}); // 30
- Reflect.set(target, name, value, receiver): 对应setter方法内部this指向receivervar a = {x: 1, y:2, set custom(value) {this.x = value; } }; var reciever = {x: 0}; Reflect.set(a, 'x', 0);Reflect.set(a, 'custom',12, reciever);Reflect.set会触发Proxy.defineProperty拦截
- Reflect.has(obj, name): 对应name in objin运算符。let obj = {a: 1}; Reflect.has(obj, 'a');
- Reflect.deleteProperty(obj, name): 对应delete obj[name]delete属性。 let obj = {a: 1}; Reflect.deleteProperty(obj, 'a')
- Reflect.construct(target, args): 等价与new target('...args')。 具体用法:function A(name){this.name = name;} function A(name){this.name = name;} const instance=Reflect.construct(A, ['ZYH'])
- Reflect.getPrototypeOf(obj): 读取对象的__proto__属性。 const a = {}; console.log(Reflect.getPrototypeOf(a) === a.__proto__);
- Reflect.setPrototypeOf(obj, newProto): 设置目标对象的原型。Reflect.setPrototypeOf(myObj, Array.prototype);
Reflect新增Reflect1. 目的:
- 现在某些方法在ObjectReflect都存在,未来新方法将只部署在Refkect上。
- 当前Object的方法出现无法定义时会报错,而Reflect会返回false,更友好
- 让Object操作都变成函数行为,例如delete a.x 变成Reflect.deleteProperty(obj, name)
-ReflectProxy对象的方法一一对应,只要在Proxy的方法,在Reflect上就存在

2.Reflect的静态方法:
-Reflect.get(target, name, receiver), 同Proxy一样,getter函数this指向receiver: var a = {x: 1, y:2, get custom() { return this.x + this.y } }; Reflect.get(a, 'x'); Reflect.get(a, 'custom', {x: 10, y: 20}); // 30
- Reflect.set(target, name, value, receiver): 对应setter方法内部this指向receivervar a = {x: 1, y:2, set custom(value) {this.x = value; } }; var reciever = {x: 0}; Reflect.set(a, 'x', 0);Reflect.set(a, 'custom',12, reciever);Reflect.set会触发Proxy.defineProperty拦截
- Reflect.has(obj, name): 对应name in objin运算符。let obj = {a: 1}; Reflect.has(obj, 'a');
- Reflect.deleteProperty(obj, name): 对应delete obj[name]delete属性。 let obj = {a: 1}; Reflect.deleteProperty(obj, 'a')
- Reflect.construct(target, args): 等价与new target('...args')。 具体用法:function A(name){this.name = name;} function A(name){this.name = name;} const instance=Reflect.construct(A, ['ZYH'])
- Reflect.getPrototypeOf(obj): 读取对象的__proto__属性。 const a = {}; console.log(Reflect.getPrototypeOf(a) === a.__proto__);
- Reflect.setPrototypeOf(obj, newProto): 设置目标对象的原型。Reflect.setPrototypeOf(myObj, Array.prototype);
Reflect新增Reflect1. 目的:
- 现在某些方法在ObjectReflect都存在,未来新方法将只部署在Refkect上。
- 当前Object的方法出现无法定义时会报错,而Reflect会返回false,更友好
- 让Object操作都变成函数行为,例如delete a.x 变成Reflect.deleteProperty(obj, name)
-ReflectProxy对象的方法一一对应,只要在Proxy的方法,在Reflect上就存在

2.Reflect的静态方法:
-Reflect.get(target, name, receiver), 同Proxy一样,getter函数this指向receiver: var a = {x: 1, y:2, get custom() { return this.x + this.y } }; Reflect.get(a, 'x'); Reflect.get(a, 'custom', {x: 10, y: 20}); // 30
- Reflect.set(target, name, value, receiver): 对应setter方法内部this指向receivervar a = {x: 1, y:2, set custom(value) {this.x = value; } }; var reciever = {x: 0}; Reflect.set(a, 'x', 0);Reflect.set(a, 'custom',12, reciever);Reflect.set会触发Proxy.defineProperty拦截
- Reflect.has(obj, name): 对应name in objin运算符。let obj = {a: 1}; Reflect.has(obj, 'a');
- Reflect.deleteProperty(obj, name): 对应delete obj[name]delete属性。 let obj = {a: 1}; Reflect.deleteProperty(obj, 'a')
- Reflect.construct(target, args): 等价与new target('...args')。 具体用法:function A(name){this.name = name;} function A(name){this.name = name;} const instance=Reflect.construct(A, ['ZYH'])
- Reflect.getPrototypeOf(obj): 读取对象的__proto__属性。 const a = {}; console.log(Reflect.getPrototypeOf(a) === a.__proto__);
- Reflect.setPrototypeOf(obj, newProto): 设置目标对象的原型。let myObj ={};Reflect.setPrototypeOf(myObj, Array.prototype);
- Reflect.apply(func, thisArg, args): 等价与Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。const a = Reflect.apply(Math.min, Math, [10,20]);
-Reflect.defineProperty(target, propertyKey, attributes): 等同于Object.definePropertyfunction Ojb(){};Reflect.defineProperty(Ojb, 'now', {value: () => Date.now()});
- Reflect.getOwnPropertyDescriptor(target, propertyKey): 基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者
- Reflect.isExtensible (target):对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展
- Reflect.preventExtensions(target): 对应Object.preventExtensions方法,用于让一个对象变为不可扩展。
- Reflect.ownKeys (target): 返回对象的所有属性,基本等同于Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和
Promise写进语言标准异步编程解决方案。有三种状态(pending进行中,fulfilled已成功,rejected已失败),一旦建成就会立即支持,无法取消。调用resolve()后如果后面有代码会继续执行,const promise=new Promise((resolve, reject)=>{console.log('1'); if (true) {resolve('true value');} else {reject(err)}}); promise.then(() => {console.log('2');}, (err) => {}).catch(() =>{}); console.log('3');,Promise新建后立即执行,打印1 3 2

1.let p = Promise.all([p1,p2,p3]): 接受一个Promise数组作为参数,当p1,p2,p3都是fullfilled,p就是fullfilled,只要其中一个rejected, p的状态就是rejected。实例:const p1 = new Promise((resolve, reject) => {});const p2 = new Promise((resolve, reject) => {throw new Error('错误')}); Promise.all([p1, p2]).catch(e => console.log(e))
- Promise.race(): 将多个 Promise 实例,包装成一个新的 Promise 实例。const p1 = new Promise((resolve, reject) => {});const p2 = new Promise((resolve, reject) => {}); Promise.race([p1, p2]).catch(e => console.log(e))
- Promise.resolve():将现有对象转为 Promise 对象。const jsPromise = Promise.resolve($.ajax('/url')) 等价于new Promise(resolve => resolve($.ajax('/url'))
- Promise.reject():返回一个新的 Promise 实例,该实例的状态为rejected

2. 使用Promise加载图片。const loadImg = (path) => {return new Promise((resolve, reject)=> { const img = new Image(); img.onload = resolve; img.onerror= reject; img.src = path;})}
Iterator迭代器与for...of循环新增什么是迭代器:一种接口,为不同数据结构提供统一的访问访问机制,使数据成员按照某种顺序排列,ES6中增加新的遍历for...of,Iterator接口主要供for...of消费。它首先创建一个指针,指向数据结构起始位置,第一次调用指针对象的next方法,指针指向数据结构第一个成员,第二次调用指针对象的next()方法,指针就指向数据结构第二个成员,.....,直到指向结束位置

1. 默认Iterator接口:ES6规定,只要一个数据结构具有Symbol.iterator属性,就认为是可遍历的。例如ES6的Array, Map, Set,String, TypedArray,函数arguments对象,NodeList对象,因为对象不确定哪个属性先遍历,哪个后遍历,所以没有部署默认迭代器接口。
- let arr= [1,2]; let iter = arr[Symbol.iterator]();iter.next();iter.next();iter.next();
- 为对象添加迭代接口,调用同数组迭代一样。let obj = { data: [1,2], [Symbol.iterator]() { const self = this; let index = 0; return { next() { return index < self.data.length ? {value: self.data[index++], done: false}: {value: null, done: true}}}}}

2.默认会调用Iterator接口的场景:解构赋值, 扩展运算符,yield*,for...of, Array.from(),Promise().all(),Promise.race(),Map(), Set()...

3.字符串的 Iterator 接口:var str="1,233"; var iter=str[Symbol.iterator]();iter.next();

3. for...of: 可与break,continue,return配合使用。for(var n of x){if (n >10) break; console.log(n);}
Generator新增的异步变成解决方案1.Generator特性:
- function关键字与函数名之间有一个星号。且函数内部使用yield表达式,定义不同的内部状态。调用时,使用括号执行函数,返回一个指向内部状态的指针对象,执行next()移动下一个状态。function* generator() {yield 'hello'; yield 'world'}; var a = generator(); a.next();a.next();
-yield表达式:暂停标志,暂停后将yield后的表达式值返回,在执行下一个next时,返回下一个yield后表达式的值。没有则返回undefined.
-yield放在表达式中,需要使用圆括号。function* demo(){console.log('hello' + (yield 123));}

2.与Iterator的关系:Generator函数就是遍历器生成函数,将Gnerator赋值给对象的Symbol.iterator属性,使其具有Iterator接口。var obj={}; obj[Symbol.iterator]=function*(){yield 1; yield2;};

3.next()方法参数:next()的参数是表示上一个yield表达式的返回值。function * generator(x){var first = (yield (x + 1)) * 2; var second=yield(x + first); var three=yield second+10;} var iter=generator(1); iter.next(); iter.next(6);iter.next(9);next方法的参数表示上一个yield表达式的返回值,因此第一个next参数无效。
- 第一次调用next(),x1,返回yield(x + 1)的值2
- 第二次next(6):x还是为1,将上次yield的值设置为6,计算first= 6 * 2second= 1 + 12,,输出13
- 第三次next(9): 虽然上次输出是13,但是next(9)的参数是9,则three= 9 + 10,输出19

4.for...of, 拓展运算符..., 结构赋值,Array.from可以将Generator函数返回的iterator对象作为参数。function * number(){yield 1; yield 2}; const a = number(); Array.from(a)

5.Generator.prototype.throw():Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。const g = function*(){try {yield 1;}catch(e){console.log('内部catch');}}; try{let iter=g(); iter.next(); iter.throw('g catch');iter.throw('g2 catch')}catch(e){console.log('外部');},因为generator只执行一次,因此捕获第一个throw,第二个throw会被外部捕获。内部捕获不会影响下一次遍历。

6.Generator.prototype.return(): 返回的遍历器对象还有一个return()方法,可以返回给定的值,并且终结遍历 Generator 函数。var gener=g(); gener.next();gener.return('custom value'); // 终止遍历并返回value,不提供参数返回undefined

7: 总结:next()、throw()、return()都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。 假如 yield表达是为function *gener(){let result = yield x + y;return result;} , 调用第二个next(10),相当于let result=10;, 调用iter.throw(new Error('错误')),相当于let result = throw(new Error('错误')),调用iter.return(30),相当于let result=30; return result;

8.yield* 表达式: 在一个 Generator 函数里面执行另一个 Generator 函数。function* f1(){yield 1; yield 2} function * f2() {yield 3; yield*f1()}; var iter=f2(); [...iter] // [3,2,1]

9.可作为对象的属性,var obj={gener: function*(){}}。 可通过call方法绑定Generator函数内部的thisvar obj={};function *Gener(){ console.log(this); yield this.a=10;}; var g = Gener.call(obj);// Gener实例内部this绑定obj,调用 g.next(); obj.a; // 10

10.同步流程:function * task(value){ var value1=yield step1(value); var value2=yield step2(value1)};,调用第一步:var iter=task(1); var taskObj=iter.next();,只要没有done,则继续调用if(!taskObj.done){iter.next(taskObj.value)}
Generator异步编程新增1.异步任务封装:function * gen(url){var result=yield fetch(url);console.log(result);} var g = gen('xxx');var result = g.next(); result.value.then(() => { return data.json}).then((data) =>{g.next(data);})

2.Thunk :将多参数函数替换成一个只接受回调函数作为参数的单参数函数。const Thunk=function(fn){return function(...args) {return function(callback){ return fn.call(this, ...args, callback);}}}, 单参数函数调用var readFileThunk = Thunk(fs.readFile);readFileThunk(fileA)(callback);
class新增1.class只是一个语法糖,构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面,调用类实例方法,实际就是调用原型上的方法,
- class Person{constructor(){} toValue(){}} 等价于Person.prototype={constructor(){}, toValue(){}}
- prototypeconstructor指向类本身: Person.prototype.constructor === Person

2.constructor():类的默认方法,new生成实例时自动调用

3.类实例:new关键字生成,const person1 = new Person(); const person2=new Person(),同es5一样,实例共享原型对象person1.__proto__ === person2.__proto__,它们的原型都是Person.prototype
- gettersetterconst propsName="custom"; class P{ _p = ''; get prop(){return this._p; }; set prop(value) { this._p = value + 'hello'} [propsName](){}}
-Class表达式:const MyClass= class Me{}; const Myclss= class {}

3.静态方法:static关键字,该方法不会被实例继承,而是直接通过类来调用,静态方法中的this指向,不是实例。静态方法会被继承。class Person{ static _name = 'yezi'; static getName(){console.log(this._name);}} Person.getName();// yezi

4.静态属性: Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。class P { static classProps1 = 10;} P.classProps2=20;

5.注意点:
- 不存在变量提升,name属性表示class关键字后面的类名。
- 如果某个方法之前加上星号(*),
-就表示该方法是一个 Generator 函数,方法内部如果含有this,它默认指向类的实例。可用构造方法中bind,解决单独使用方法报错问题.class P{constructor(){this.method1= this.method1.bind(this);} method1() {this.method2()} method2() {console.log('method2')} method3() {this.method2()}},调用时 const {method1,method3} =new P();method3(); // 内部报错,this执行错误,不存在method2

6.new.target属性:构造函数如果是通过newReglect.construct()new.target返回classname,否则返回undefinedfunction P(name) {if (new.target) {this.name=name} else {throw new Error('必须使用new命令')}},调用P('hello')// 抛出异常
class的继承新增1.extends实现继承。因为子类基于父类,因此需先调用super()才能使用this关键字。class P{constructor(x,y) {this.x=x;this.y=y;}} class C extend P{ constructor(x,y) {super(x, y);}}

2.私有属性与私有方法不能被继承。class P {#myname='zyh'; #myMehtod() {}} class C extends P { constructor(){super(); this.#myMethod(); // 抛出异常}}

3.静态属性与静态方法被继承

4.可通过Object.getPrototypeOf()获取父类。class P{} class C extends P{} Object.getPrototypeOf(C) === P // true

5.super关键字:
- 当做函数调,代表父类构造函数在子类中调用,但要注意,子类中调用superthis指向了子类的实例,相当于P.prototype.constructor.call(this)new.target执行当前正在执行的函数。class P{ constructor(){ console.log(this, new.target)}} class C extends P{constructor(){super()}},new C() // this执行C实例,new.target指向C
- super作为对象时,在普通方法中,指向父类的原型对象this指向子类实例;在静态方法中,指向父类,this指向子类(不是子类实例)。class P {cName=20; pMethod(){console.log(this.cName);} static PsMehtod(){console.log(20);}} class C extends P { cName=10;constructor(){ super(); super.pMethod(); // 普通方法指向父类原型对象,对然调用父类方法,但实例内部this指向实例,因此打印10} static Cmethod() {super.PsMehtod(); // 指向父类,打印20}}

6.子类__proto__表示构造函数的继承,总是指向父类,子类prototype__proto__属性表示方法的继承,指向父类的prototype属性
- class A{} class B extends A{}, B.__proto__ === A // trueB.prototype.__proto__ === A.prototype // true, 其实就是等价于Object.setPrototypeOf(B.prototype, A.prototype);Object.setPrototypeOf(A, B);, 而setPrototypeOf方法内部实现:function(B, A) {B.__proto__ = A;}
- 不存在继承时:class A{}, A.__proto__ === Function.prototype; A.prototype.__proto__=Object.prototype,
moudle 模块话新增1.ES6模块加载。import { stat,exists } from 'fs';,在只会加载这连个方法,不会全部载入,因此可在编译时加载,使静态分析成为可能。而CommonJS模块let { stat, exists } = require('fs');,需要进行全量加载,将所有方法加载再读取,因为只有运行时才能得到这个对象,导致无法再编译时做静态优化

2.exportimport:希望外部能够读取模块内部的某个变量,使用export导出,希望引入外部文件的变量,使用import导入。A文件a.js导出:cons a= 'xx'; export { a }; B文件导入import { a } from 'a.js';
- 在静态分析阶段,不能使用表达式、变量、if等结构。import { a +b} from 'xx'; if(x==1) {import a from 'a.js'}...都会出错。

3.使用*加载整个文件导出的内容。 import * as All from 'a.js';

4.使用export default命令导出默认变量或函数等。在引入时就不用{}了。 const a = 'hello'; export default a;,导入:import A from 'a.js';

4.模块的继承和改名:
- 继承extendsMoudle中所有方法并导出。export * from 'extendsModule';
- 改名:export { a as changeName } from 'extendMoudle';
5.跨模块共享值: A模块:export const a=10;, 在X模块引入:import * as A from 'a.js'; console.log(A.a);, 在Y模块引入:import * as A from 'a.js'; console.log(A.a); ,可达到共享。
Moudle 加载实现新增1. JS对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。加载规则:type="module",js就知道是es6的模块加载。<script type="module" src="./foo.js"></script>默认是defer属性,可以自己设置为async属性,脚本在后台加载完就立即运行。 <script type="module" src="./foo.js" async></script>。如果在模块作用域中,模块内部变量对外不可见,默认严格模式,模块中顶层thisundefined,不是window。可用this判断是否在e6模块中。const isInES6Moudle= this === undefined

2.ES6模块与CommonJS模块差别:
- CommonJS输出是值的拷贝,外部引入后修改不会影响值的变化。 ES6模块是值的引用
- CommonJs是运行时加载,ES6模块式编译时输出层接口
- CommonJS模块的require()设计同步加载模块,ES6模块的import是异步加载,有一个独立的模块依赖的解析阶段

3.Node.js的模块加载:只要是.mjs命名的文件,Node.js就认为是ES6模块。默认开启严格模式。如果不希望使用.mjs,可在package.json中配置{"type": "module"}.cjs文件总是以 CommonJS 模块加载。或者配置package.json{"type": "commonjs"}
Decorator使用很少,暂时不做详细了解
ArrayBuffer暂时不做详细了解

tips:
:

什么是单精度,什么是双精度

单精度浮点数占4字节(32位)内存空间,其数值范围为-3.4E38~3.4E+38,;双精度型占8 个字节(64位)内存空间,其数值范围为-1.79769313486232E308 到1.79769313486232E308。 双精度浮点型类型数值可转换到其他类型的整数或浮点数,反之亦然。

因为掘金文字长度限制,分为上下两篇,下篇:juejin.cn/post/741103…