【JS02】ES6+ 复习引导(超全)

162 阅读12分钟

ES6+

ECMA2016+新语法复习引导

ES6声明变量

共6种:

  var\let\const\function\class\import

let & const

  • let
    • 不存在变量提升
    • 所以也就存在暂时性死区
    • 不允许重复声明
    • 作用域内有效
  • const
    • 一旦声明,常量的值就不能再改动(实质上是指向的内存地址不可以改动)
    • let的3个特性

解构赋值

解构:

可以按照一定的模式从数组和对象种取值。
被解构的对象要具备Iterator接口,否则报错。 字符串也有Iterator,也可以被解构。

用途:

  • 交换变量的值
  • 从函数返回多个变量
  • 函数参数的定义

字符串

中文码点

"\u{20BB7}" 大于 0XFFFF的码点
"\u{1F680}" === "\uD83D\uDE80"

Iterator

有遍历器接口。

Api

- .at(n)
- .charAt(n)
- .includes('str')
- .startWith('str')
- .endWith('str')
- .repeat(n) //将原字符串重复几次,拼接返回,数值向下取整
- .padStart(len, 'str')
// 补全字符串到len长度,如果str.length >= len, 
// 返回原字符串,没有第二个参数,则用空格补全
- .padEnd(len, str)

正则

.source() ==> a+

修饰符.flags()

-g -i -u -y

-g

-y 后一次匹配必须从前一次匹配成功的下一位开始匹配到,必须粘连,否则返回null

'aaa_aa_a'

/a+/g   第一轮匹配 aaa    第二轮匹配 aa

/a+/y   第一轮匹配 aaa    第二轮匹配 null

Number

Api

- .isFinate(var)
- .isNaN(var)
- .parseFloat(x,x)
- .parseInt(x,x)
- .isInteger(var) // 由于js内部整数和浮点数是同样的储存方法,所以, 3和3.0被视为同一个值
- .EPSILON // 表示1与大于1的浮点数之间的差,是js能表示的最小精度
- .trunc(4.1) 返回整数部分  4 
- .sign(var) 判断是正数 (1)还是负数 (-1)还是0 (+0 -0)
- ** 指数运算符

Integer

后缀n: 112132n       
typeof 121n === 'integer'
Integer与Number不能混合计算

函数

rest参数

function add (...rest) {

}

箭头函数

  • this指向定义时所在对象,而非使用时所在对象
  • 不可当作构造函数
  • 绑定bind()是无效的
  • 不可以使用arguments对象,可用rest参数代替
  • 不可使用yeild,不能做generator

双冒号运算符

foo::bar
foo.bind(bar)

数组

扩展运算符 ...

Iterator

有遍历器接口。

Api

- Array.from('hello', function(v){return v}) // 转换为数组,将类似数组的对象和可遍历Iterable的对象
- Array.of(2,11,12) //将一组值转换为数组
- .copyWithIn(target, start, end) 
// 数组内部,将指定位置的数组成员复制到其他位置,然后返回当前数组, 
// target开始替换,替换从start-end取值
- .find((n) ==> {}) //返回成员
- .findIndex((n) ==> {}) // 类似indexOf(),只是可以识别NaN,因为Object.is()
- .fill(num, start, end) //覆盖
- .entries()
- .keys()
- .values()
- .includes()

对象

Iterator

有遍历器接口。

obj['a' + 'bc'] = 123

属性

- get 
- set
const obj = {
  get foo() {},
  set foo(x) {}
}

属性的可枚举性和遍历

- for...in // 自身和继承可枚举属性, 无symbol
- Object.keys() //自身可枚举属性, 无symbol
- JSON.stringify() // 自身可枚举属性, 无symbol
- Object.assign() //自身可枚举属性
- Object.getOwnPropertyNames() // 自身所有的属性, 无symbol
- Reflect.ownKeys(obj) 
// (自身)先遍历所有数值键,升序,
//  再遍历所有字符串键,按加入时间升序排,
//  再所有symbol键,按加入时间升序排

Api

- Object.is(a, b) //与=== 区别在于NaN
- Object.assign(target, source1, source2...) 
  //source本身(不考虑继承)所有的可枚举属性,复制到target
  // 后面会覆盖前面,如果不是对象,会先转成对象。如果source无法转成对象,就会跳过
  // 浅拷贝
- Object.setPrototypeof(obj, pro)
- super 只能在对象的方法中,表示对象的原型对象
- Object.fromEntries()  Entries ===> Object

null传导运算符

obj?.func?.()

Symbol

ES5的属性名都是字符串,容易重复

let s = Symbol();
typeof s === 'symbol'

symbol不是对象,不能添加属性。

作为对象属性key

作为对象属性时,不能用点运算符。

不会出现在for ... in, for ... of中, 不会被Object.keys, Object.getOwnPropertyNames, Json.stringify返回,也不是私有属性

 Object.getOwnPropertySymbols()
  Reflect.ownKeys()

symbol比较

有无参数,都是不同的symbol实例。

Symbol 值不能够被强制类型转换为数字(显式和隐式都会产生错误),
但可以被强制类型转换为布尔值(显式和隐式结果都是 true )。

索引

const s1 = Symbol.for('foo') & Symbol('foo')
两者都会生成新的symbol,
但区别是,
前者会被登记在全局供搜索,不会每次都创建新的symbol,
而是会先检查给定的key是否已经存在,如果不存在才会创建

Symbol.keyFor(s1) // 返回指定symbol登记的key

Set

是一个构造函数

Iterator

有遍历器接口。

没有重复值,使用Object.is()判定

const set =  new Set();

- set.add(1)
- set.delete(1)
- set.has(1)
- set.clear() // clear all
- set.size

// 支持链式调用
set.add(1).add(2)

遍历顺序就是插入顺序。 for(let v of set)

WeakSet

  • 成员只能是对象。
  • 成员都是弱引用,GC不会考虑其引用。
  • 因此,无size属性,也不能遍历

Map

是一个构造函数

Iterator

有遍历器接口。

undefined和null也是两个不同的键,各种值都可以作为键,包括对象和symbol

原理: Map的键是跟内存地址绑定的,只要内存不一样,就是两个不同的键

const map = new Map();

map.set('k', 'v')
map.get('k')
map.delete('k')
map.has('k')
map.clear()

Map与Object区别

  • Map的key是跟内存地址绑定的,只要内存不一样,就是两个不同的键;
  • Map是按写入的顺序来组织排序索引,Object是按字符编码顺序来组织排序索引的;
  • 双方api不同

WeakMap

  • 只接受对象作为键名(null除外),不接受其他类型作为键名。
  • 不计入GC引用
  • 不能遍历
  • 无法清空

Proxy 自定义对象中操作

与Object.definedProperty对比:

  • 无需一层一层的递归给每个属性添加代理,性能上更好,一次性完成;
  • 原本存在一下数据更新不能监听到,但Prxoy可以完美监听到任何方式的数据改变。
    • 如vue,通过下标方式修改数组数据,或者,给对象新增属性,并不会触发组件的重新渲染,数组的大部分操作都是拦截不到的。
new Proxy(target, handler)
const obj = new Proxy({}, {
  get: function(target, key, receiver){}, //  拦截对象属性的读取
  set: function(target, key, value, receiver){} // 拦截对象属性设置
  has: function(target, key){} // 拦截 key in proxy操作
  deleteProperty: function(target, key){} // 拦截delete key操作
  ownKeys: function(target){}  // Object.getOwnPropertyNames/Object.getOwnPropertySymbols/Object.keys()
  getOwnPropertyDescriptor: function(target, key){} 
  defineProperty: function(target, key, config){} 
  getPrototypeOf: function(target){} 
  setPrototypeOf: function(target, proto){} 
  preventExtensions: function(target){} 
  isExtensible: function(target){} 
  apply: function(target, object, args){} // 拦截Proxy实例作为函数调用的操作,proxy(...args), proxy.call(obj, ...args),.apply() 
  constructor:function(target, args){} // 拦截Proxy实例作为构造函数调用的操作
})

所有操作,必须基于proxy实例。

在Proxy代理的情况下,目标对象内部的this关键词指向Proxy代理

Reflect

将Object上一些明显属于语言内部的方法,放到Reflect上,比如Object.definedProperty

  - get(target, key, receiver)
  - set(target, key, value, receiver)
  - has(target, key)
  - deleteProperty(target, key)
  - ownKeys(target) // Object.getOwnPropertyNames /Object.getOwnPropertySymbols /Object.keys(),
  返回对象自身的所有属性
  - constructor(target, args) //  等同于new target(...args)
  - getPrototypeOf(target)
  - setPrototypeOf(target, proto)
  - apply(target, object, args) // 绑定this对象后执行给定函数
  - defineProperty(target, key, config)
  - getOwnPropertyDescriptor(target, key)
  - preventExtensions(target)
  - isExtensible(target)

Promise

三种状态:resolve, reject, pendding

一旦状态改变就不会再变。

resolve(value),将promise对象的状态从未完成变成成功

reject(error),将promise对象的状态从pending调整为失败

.then(res => {}).catch(err=>{}).finally(() => {}).done(e => { // 兜底报错,抛向全局 })

Promise.all([promise1, promise2, ...]) //参数必须具备Iterator接口,且每个成员必须返回Promise实例。 //全部完成即完成

Promise.race([promise1, promise2, ...]) //参数必须具备Iterator接口,且每个成员必须返回Promise实例。 //有一个完成就完成了

Promise.resolve() // 转为一个promise对象 参数是不具有then方法的对象,或者根本不是对象,则返回一个新的Promise对象,状态resolved

Iterator

for ... of // 使用for of循环遍历时,会自动去寻找Iterator

遍历器是一种接口,为各种不同的数据提供一种统一的访问机制。

ES6规定,默认的Iterator接口部署在数据结构的[Symbol.iterator]属性, 本身是一个函数,就是当前结构的遍历器生成函数,执行就会返回一个遍历器

一个对象,如果要具备可被for...of循环调用的Iterator接口,就必须在Symbol.iterator属性上部署遍历器生成方法,原型链上的对象具备也可。

作用:

  • 为各种数据结构,提供一个统一简便的访问接口;
  • 使得数据结构的成员能够按照某种次序排列;
  • for ... of 循环

next()方法用来移动指针。

每一次调用.next都会返回数据结构当前成员的信息,是一个对象,包含value(当前值)和done(遍历是否结束)

举例

var it = makeIterator(['a', 'b'])
it.next() // {value: 'a', done: false}
it.next() // {value: 'b', done: false}
it.next() // {value: undefined, done: true}

具备Iterator的数据结构有: Array\String\Set\Map\arguments

Generator

Generator 函数有多种理解角度。

  • 从语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
  • 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,但是有两个特征。

  • 一是,function关键字与函数名之间有一个星号;ES6 没有规定,function关键字与函数名之间的星号写在哪个位置
  • 二是,函数体内部使用yield表达式,定义不同的内部状态
  function* helloWorldGenerator() { 
    yield 'hello';   
    yield 'world'; 
    return 'ending';
  }  
  var hw = helloWorldGenerator(); 

状态机

执行分析:

三个状态,return是最后一个状态

Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

  hw.next() 
  // { value: 'hello', done: false } 
  hw.next() 
  // { value: 'world', done: false } 
  hw.next() 
  // { value: 'ending', done: true } 
  hw.next() 
  // { value: undefined, done: true } 

具体执行逻辑:

(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

yield表达式如果用在另一个表达式之中,必须放在圆括号里面。

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

function* foo(x) { 
  var y = 2 * (yield (x + 1));   
  var z = yield (y / 3); 
  return (x + y + z); } 
  var a = foo(5);
  a.next() // Object{value:6, done:false} 
  a.next() // Object{value:NaN, done:false} 
  a.next() // Object{value:NaN, done:true} 
  var b = foo(5); 
  b.next() // { value:6, done:false } 
  b.next(12) // { value:8, done:false }   
  b.next(13) // { value:42, done:true } 

遍历器生成函数

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

var myIterable = {}; 
myIterable[Symbol.iterator] = function* () {   
yield 1;   
yield 2;   
yield 3; 
}; 


[...myIterable] // [1, 2, 3] 

for...of 循环可以自动遍历 Generator 函数时生成的。Iterator对象,且此时不再需要调用next方法。

function *foo() {   
    yield 1;   
    yield 2;   
    yield 3;   
    yield 4;   
    yield 5;   
    return 6; 
}  

for (let v of foo()) {  console.log(v); } 

// 1 2 3 4 5 

.throw()

.return("xxx") //{ value: "xxx", done: true } 可以返回给定的值,并且终结遍历Generator 函数

yield*表达式 用来在一个 Generator 函数里面执行另一个 Generator 函数。

function* bar() {   
  yield 'x'; 
  yield* foo();   
  yield 'y'; 
} 

async

Generator语法糖

async函数就是将Generator函数的 * 换成了 async 标识,将yeild 换成了 await

一旦遇到await就会先返回,执行执行栈(宏任务),等到异步操作完了,加入到执行栈。

调用async函数会返回一个Promise对象,await后面的对象会转换为promise

class 语法糖

class 的本质是函数,不存在变量提升。

class A {
  constructor (x, y) {
    this.x = x; // 类的实例属性
    this.y = y;
  }
  toString() { // 类中的所有方法都是不可枚举的
    return this.x;
  }
  static classMethod() { 
    // 静态方法,不会被实例继承,只能被类调用
    // 静态方法内的this指向类
    return "hello"
  }
}

A instanceOf Function ===> true

new A() 立即执行

继承 extends

class B extends A { 
  //B继承了A的所有属性和方法
  constructor(props){
    super(props) // 调用父类的constructor,用来新建父类的this对象,只能在子类中调用
  }
}

在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错:
因为子类实例的构建,是基于父类实例的加工,只有super才能返回父类的实例。

super 指向 父类的原型对象(A.prototype)

ES5的继承 & ES6的继承

ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上;
ES6的继承,是先创造父类实例对象this(所以必须先调super),再用子类的构造函数修改this

修饰器

只能用于类和类的方法。

作用

  1. 编译期间对类和方法的行为进行改变

修饰器对类的行为的改变,是在代码编译时发生的,而不是运行时。 也就是,修饰器能在编译时,运行代码,即,修饰器是编译时执行的函数。

  1. 不同类间共享方法

类修饰器

类修饰器是一个对类进行处理的函数。

function decorator(target) {
  target.age = 30
}

// 有自定义参数
function setProps(value) {
  // 返回修饰器
  return function(target) {
    target.name = value
  }
}

@setProps('deyi')
@decorator
class A() {

}

等价于
class A {

}
A = setProps("deyi")(decorator(A)) || A

console(A.age)   ====> 30
console(A.name)  ====> deyi

方法的修饰

function noneunumrable(target, name, descriptor) {
  descriptor.enumrable = false;
  return descriptor
}
class A {
  @noneunumrable
  get kidCount() {
    return this.children.length
  }
}