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
修饰器
只能用于类和类的方法。
作用
编译期间对类和方法的行为进行改变
修饰器对类的行为的改变,是在代码编译时发生的,而不是运行时。
也就是,修饰器能在编译时,运行代码,即,修饰器是编译时执行的函数。
不同类间共享方法
类修饰器
类修饰器是一个对类进行处理的函数。
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
}
}