ECMAScript
缩写ES
-
JavaScript是ES的扩展语言
-
ECMAScript只提供了最基本的语法,只停留在语言层面
-
JavaScript语言本身指的就是ECMAScript
ES2015
概述
ES6
asnyc2017标准
准备
yarn init
code 001.js
yarn add nodemon --dev
yarn nodemon 001.js
//监视脚本文件
let与块级作用域
-
全局作用域
-
函数作用域
-
块级作用域 {}
-
let 在所声明代码块外部无法访问
-
循环计数器 嵌套循环 是两层嵌套的独立作用域
var i重复 内部拿到的只是外部的i
for(var i ...) for(let i ...) console.log(i)var 全局作用域 闭包也可以摆脱影响
for(let i = 0;i < 3;i ++){ let i = 'foo' console.log(i) //互不影响 } /=== let i = 0 i++ if(i < 3){ let i = 'foo' console.log(i) }-
原有变量声明的提升,不报错,undefined
let被改正
console.log(foo) var foo = 'zce' -
-
const
恒量常量 只读
只要没有修改指向的内存地址,都是被允许的
特性与let相同
const u = {}
u.hi = '1'//√
u = {}//×
主用const,配合let,不用var
数组的解构
const arr = [1,2,3]
const [foo, bar, baz] = arr
const [foo=3, ...rest] = arr
//提取当前位置往后的所有成员,只能在解构位置最后一个成员上使用
对象的解构
const obj = {name:'zce', age: 18}
const name = 'tom'
const {name: objName='jack'} = obj
console.log(objName)//zce
可以简化代码的编写,体积减小
const { log } = console
log('foo')
log('1')
模板字符串字面量
const str = \`nihao\`nihaonihao`
const mag = `hey, ${name}`
带标签的模板字符串
fuction myTagFunc (string, name, gender){
console.log(string, name, gender)
return '123'
}//按照表达式分割的静态的内容,结果是一个数组
const str = myTagFunc`hey,${name} is a ${gender}`
console.log(str)//123
//tag是函数,作为一个模板标签
const arr = console.log`world`
字符串的扩展方法
- includes()中间包含xx
- startsWith()判断开头是xx
- endsWith()判断结尾是xx
参数默认值
没有在传递参数或者传递undefined时使用的一个值
function foo(enable){
enable = enable === undefined ? true : enable
}
//==
function foo(a, enable = true){
//一定要出现在参数列表的最后
}
foo()
剩余参数
新增了...
取代argement
出现在最后一位,只能出现一次
function foo (...args) {
console.log(args)
}
fo(1,2,3,4)
展开数组
arr = [1,2,3,4]
console.log(...arr)
箭头函数
const omc = n => n + 1
//Fira Code
const arr = [1,2,3,4,5,7]
arr.filter(i => i % 2)
箭头函数与this
-
没有this的机制,不会改变this的指向,它始终指向的都是当前作用域里的this
-
this在外面是什么,在里面就是什么
-
在普通函数中,this始终会指向调用它的对象,箭头函数不然
const person = { name : 'tom', sayHi : () => { console.log(`hi,${this.name}`) //在外面this是undefined }, sayHiAsync:function(){ const _this = this //使用了闭包 setTimeout(function(){ console.log(_this.name) },1000) }, sayHiAsync1:function(){ setTimeout(() => { console.log(this.name) //this就是sayHiAsync1作用域中的this },1000) } } person.sayHi() person.sayHiAsync1()
对象字面量的增强
计算属性名
const obj = {
foo:2,
bar,
[Math.random()]:123
}
Object.assign
-
多个源对象的属性复制到一个目标对象中
const a = {} const b = {} const result = Object.assign(a,b) //a === b const result = Object.assign(source3,source1,source2) //复制的是source2 -
也可以解决两个对象直接相等改变前者指向地址的问题
const funcObj = Obj.assign({},obj)
Object.is(对象扩展方法)
==自动转换数据类型
===严格比较
Object.is(+0,-0)
Object.is(NaN,NaN)
Proxy代理对象
Object.defineProperty捕获到对象读写的过程。Vue3.0前实现数据响应,完成双向数据绑定
const person = {
name: 'zse',
age: 20
}
const p = new Proxy(person, {
get(target, property) {
//目标对象,访问的属性名
return property in target ? target[property] : 'default'
//访问属性值,先判断对象中是否存在该属性名
},//监视属性访问
set(target, property, value) {
//代理目标对象,要写入的属性名称,写入的属性值
console.log(target, property, value)
//加入数据校验
if(property === 'age'){
if(!Number.isInteger(val)){
throw new TypeError(`1111`)
}
}
target[property] = value
}//监视属性设置
})
p.gender = true
console.log(p.name)
Proxy VS. defineProperty
-
defineProperty只能监视属性的读写,Proxy 能够监视到更多对象操作
//Proxy对象 deleteProperty(target,property){ delete target[property] }//监听删除操作handler方法 触发方式 get 读取 set 写入 has in操作符 deleteProperty delete操作符 getPrototypeOf Object.getPrototypeOf() setPrototypeOf Object.setPrototypeOf() apply 调用一个函数 construct 用new调用一个函数 -
Proxy更好的支持数组对象的监视
以往重写数组的操作方法
const list = [] const listProxy = new Proxy(list,{ set(target, property, value){ console.log(target, property, value) //目标对象,数组长度,传值 target[property] = value return true } }) listProxy.push(100) -
Proxy是以非侵入的方式监管了对象的读写
Reflect
-
内部封装了一系列对对象的底层操作(13个)
-
Reflect成员方法就是Proxy处理对象的默认实现
如果在内部没有定义set get等方法,就默认调用了Reflect同名方法
... get(target, property){ return Reflect.get(target, property) } -
意义:提供了统一一套用于操作对象的API
const obj = { name : 'hi', age:1 } // console.log('name' in obj) // console.log(delete obj['age']) // console.log(Object.keys(obj)) //即将废弃 console.log(Reflect.has(obj, 'name')) console.log(Reflect.deleteProperty(obj, 'age')) console.log(Reflect.ownKeys(obj)) -
new Reflect()本身不可以对数据进行拦截
Promise
- 一种更优的异步编程解决方案
- 解决了传统异步编程中回调函数嵌套过深的问题
class 类
- 以前定义函数和定义函数的原型对象prototype去实现的类型
function Person (name) {
this.name = name
}//构造函数
Person.prototype.say = function () {
//对象方法
}
-
通过class
class Person { constructor (name) { this.name = name } say() { console.log(`hi`) } } const p = new Person('tom') p.say()
静态方法(static)
-
实例方法 vs. 静态方法
-
新增了添加静态成员的static关键词
需要注意this
class Person { constructor (name) { this.name = name } say() { console.log(this) console.log(`hi`) } static create (name) { console.log(this) //this = Person { name: 'tom' } return new Person(name) } //静态方法挂载到类型上,所以静态方法内部的this就不会指向某,而是当前的类型 //this = [class Person] } const tom = Person.create('tom') tom.say()
类的继承(extends)
以前用原型
class Student extends Person {
constructor(name ,number){
super(name)//调用了弗雷的构造函数
this.number = number
}
hello(){
super.say()
}
}
- 子类构造器中super关键字前面不能出现this关键字
Set(集合)
-
内部成员不允许重复,重复添加会忽略
const s = new Set() s.add(1).add(2).add(3).add(4) //返回集合本身,链式调用add forEach size(length) have delete clear
-
作用:为数组元素去重
const arr = [1,2,3,4,2,3,1]
//const result = new Set(arr)是一个对象
const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
//都可以得到一个数组
Map
和对象类似,本质上是键值对集合,但键只能是字符串类型,
const obj = {}
obj[123] = '?'
obj[{a:1}] = '!'
Map可以解决这样的问题
const m = new Map()
const tom = {name : 'tom'}
m.set(tom, 90)
console.log(m.get(tom))
//键值是什么都可以
has delete clear forEach
symbol
-
一种全新的原始数据类型
-
避免对象属性名重复产生的问题
Symbol() != Symbol() Symbol('foo') Symbol('baz')//描述文本 ... const obj = {} obj[Symbol()] = '123' obj[Symbol()] = '456' //因为Symbol的值都是独一无二的,不用担心会冲突 -
可以模拟实现对象的私有成员
- 原理:外界无法获取到相同的smybol键值,则无法访问
//a.js const name = Symbol() const person = { [name]:'1', say(){ console.log(this[name]) } } //b.js person.say() //只能通过调用对象方法访问 -
为对象添加独一无二的属性名
-
特性:
-
唯一性 for
Symbol('foo') != Symbol('foo') //与描述文本无关 //for接收一个字符串作为一个参数,相同的字符串一定会返回相同的字符串的值 const s1 = Symbol.for('foo') const s2 = Symbol.for('foo') s1 === s2它维护的是字符串和smybol之间的关系,如果传参不是字符串,会自动转换为字符串
-
提供了很多内置的Symbol常量,作为内部方法的标识。可以去实现js中内置的接口。
const obj = { [Symbol.toStringTag]: 'XObject' //toStringTag内置的Symbol常量 } console.log(obj.toString()) //[object object] 对象的toString标签但还是可以使用Object.getOwnPropertySymbol(obj)获取到obj中Symbol类型的属性名
-
for...of循环
-
遍历所有统一方式
for (const item of arr){ if(...){ break } } //forEach都不会中止遍历伪数组也可
-
但普通对象Object无法被迭代
ES2015提供了Iterable接口,表示可迭代的。能实现Iterable接口就是for...of的前提(在内部已经实现了这个接口)
找原型对象,有Iterable的方法,必须挂载的
总结:需要返回带有next()方法的对象,不断调用next()方法实现对内部对象的遍历
const set = new Set(['foo','bar','baz']) const iterator = set[Symbol.iterator]()//调用set的iterator方法,获得set的迭代器 console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next())
实现可迭代接口(Iterable)
在对象内部实现迭代器
const obj = {
store :['foo', 'bar', 'baz'],
[Symbol.iterator] : function (){
let index = 0
const self = this
return {
next: function () {
const result = {
value: self.store[index],
done : index >= self.store.length
}
index ++
return result
}//向后迭代的逻辑,实现了迭代结果的接口,IterationResult
}//实现了迭代器接口 Iterator
}
//挂载一个iterator方法,在这个方法里返回一个迭代器对象
}
for(const item of obj){
console.log('???',item)
}
迭代器模式
-
意义:对外提供遍历统一接口,外部不用再关心内部的结构。语言层面实现,适用于任何结构。
const todos = { a: [1, 2, 3], b: [4, 5, 6], c: [7, 8, 9], [Symbol.iterator]: function () { const all = [...this.a, ...this.b, ...this.c] let index = 0 return { next: function () { return { value: all[index], done: index++ >= all.length //防止死循环 } }//返回此次迭代的结果 } } } for (const item of todos) { console.log(item) }
生成器(Generator)
-
避免异步编程中回调嵌套过深问题
-
提供更好的解决方案
-
内部也有一个迭代器next方法
function * foo { ... return 100 } const result = foo() console.log(result) //打印出来的结构是一个Generator对象 console.log(result.next()) //直接调用next方法便可以遍历foo对象,foo才开始进行执行 -
配合yield使用
-
总结:
- 生成器函数会自动返回一个生成器对象,调用它的next方法才会让这个函数体开始执行
- 一旦中途遇到yield关键字,便会暂停执行,继续next便会继续执行
生成器应用
- 发号器
function * createIdMaker () {
let id = 1
while(true){
yield id++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
//简单的发号器需求
-
使用Generator函数实现iterator方法
不用手动实现一个迭代器对象,直接使用循环yield去返回一个生成器的对象
const todos = { a: [1, 2, 3], b: [4, 5, 6], c: [7, 8, 9], [Symbol.iterator]: function () { const all = [...this.a, ...this.b, ...this.c] // let index = 0 // return { // next: function () { // return { // value: all[index], // done: index++ >= all.length // //防止死循环 // } // }//返回此次迭代的结果 // } for(const item of all){ yield item } } } for (const item of todos) { console.log(item) }
ES Modules
- 语言层面地模块化规范
ES2016
-
检查数组是否包含指定元素,能去查找NaN
arr.includes()
-
指数运算符
Math.pow(2 ** 10)
ES2017
-
Object.value()
返回对象中所有的值组成的数组
-
Object.entries()
返回对象中所有的键值对
for (const [key, value] of Object.entries(obj)) { console.log(key, value) } -
Object.getOwnProperyDescriptors
完整地获取对象属性中的描述信息,配合ES中的get和set使用
-
String.prototype / String.prototype.padEnd
字符串填充方法。用给定的字符串去填充字符串的开始和结束位置,达到指定长度为止
-
在函数参数中添加逗号
-
Async / Await
本文首发于我的GitHub博客,其它博客同步更新。