Symbol
Symbol 类型数据的声明
{
let a1 = Symbol ();
let a2 = Symbol ();
console.log(a1 === a2)
// Symbol.for ,检查key值,不存在就新建一个Symbol,存在就返回已经创建的那个Symbol
let a3 = Symbol.for('a3')
let a4 = Symbol.for('a3')
console.log(a3 === a4)
}
Symbol 类型作为对象的key
{
let a1 = Symbol('abc')
let obj = {
[a1]: 123,
'abc': 345,
'c': 456
}
console.log(obj)
// 无法遍历到Symbol类型的key
for (let key in obj) {
console.log(key, obj[key])
}
for (let [key, value] of Object.entries(obj)) {
console.log(key, value)
}
// 取一个对象中所有的Symbol类型的对象
console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(abc) ]
// 获取所有key,(包括Symbol)
console.log(Reflect.ownKeys(obj)) // [ 'abc', 'c', Symbol(abc) ]
}
Set 类型
Set 类型的声明
{
let list = new Set ()
list.add(5)
list.add(7)
// 重复添加不会生效
list.add(5)
console.log(list) // Set { 5, 7 }
console.log(list.size) // 2
}
Set 可用于去重
{
// 可用于去重
let arr = [1, 2, 3, 4, 5, 6, 6,'6']
let list = new Set (arr)
console.log(list) // Set { 1, 2, 3, 4, 5, 6, '6' }
}
Set 的方法:add、delete、clear、has
{
let arr = ['add', 'delete', 'clear', 'has']
let list = new Set (arr)
console.log('has', list.has('add'))
console.log('delete', list.delete('add'))
console.log(list)
console.log('add', list.add('add'))
console.log(list)
console.log('clear', list.clear())
console.log(list)
}
WeakSet
- WeakSet 和 Set 支持的数据类型不一样, weakSet 只支持对象
- 无clear 方法,无size属性
- 不能遍历
{
let weakList = new WeakSet ()
weakList.add({})
console.log(weakList)
}
Map 类型
Map 类型的声明
{
let map = new Map ()
let arr = [1, 2, 3]
map.set(arr, 456)
map.set('a', 1)
console.log(map)
console.log(map.get(arr))
console.log(map.get('a'))
}
{
let map = new Map ([['a', 123], ['b', 456]])
let obj = {a: 1}
map.set(obj, 'ok')
console.log(map)
console.log(map.get(obj))
console.log('map.szie', map.size)
console.log(map.delete('a'), map)
console.log(map.clear(), map)
}
map 对象没有.add方法,新增方法位.set
WeakMap
- 无size属性,无法clear
- 无法遍历
- key必须是对象
{
let weakmap = new WeakMap ()
let obj = {}
weakmap.set(obj, 123)
console.log(weakmap)
console.log(weakmap.get(obj))
}
各数据结构之间的对比
- 优先使用map
- 对数据唯一性要求较高,使用set
- 放弃使用数组和对象
Map 和 Array 的对比
{
let map = new Map ()
let arr = []
// 增
map.set('t', 1)
arr.push({t: 1})
console.info(map, arr)
// 查
let map_exists = map.has('t')
// let arr_exists = arr.find(function (item) {
// return item.t
// })
let arr_exists = arr.find(item => item.t)
console.log(map_exists, arr_exists)
// 改
map.set('t', 2)
arr.forEach(item => item.t ? item.t=2 : '')
console.info(map, arr)
// 删
map.delete('t')
arr.splice(arr.findIndex(item => item.t), 1)
console.info(map, arr)
}
Set 和 Array 的对比
{
let set = new Set()
let arr = []
let obj = {t: 1}
// 增
set.add(obj)
arr.push(obj)
console.info(set, arr)
// 查
let set_exists = set.has(obj)
let arr_exists = arr.find(item => item.t)
// let arr_exists = arr.includes(obj)
console.log(set_exists, arr_exists)
// 改
set.forEach(item => item.t ? item.t = 2 : '')
arr.forEach(item => item.t ? item.t = 2 : '')
console.info(set, arr)
// 删
// set.delete(obj)
set.forEach(item => item.t ? set.delete(item) : '')
arr.splice(arr.findIndex(item => item.t), 1)
console.info(set, arr)
}
Map、Set 和 Object的互相对比
{
let set = new Set()
let arr = []
let obj = {t: 1}
// 增
set.add(obj)
arr.push(obj)
console.info(set, arr)
// 查
let set_exists = set.has(obj)
let arr_exists = arr.find(item => item.t)
// let arr_exists = arr.includes(obj)
console.log(set_exists, arr_exists)
// 改
set.forEach(item => item.t ? item.t = 2 : '')
arr.forEach(item => item.t ? item.t = 2 : '')
console.info(set, arr)
// 删
// set.delete(obj)
set.forEach(item => item.t ? set.delete(item) : '')
arr.splice(arr.findIndex(item => item.t), 1)
console.info(set, arr)
}
{
let item = {t: 1}
let map = new Map ()
let set = new Set ()
let obj = new Object ()
// 增
map.set('t', 1)
set.add(item)
obj['t'] = 1
console.info(obj, map, set)
// 查
console.info({
map_exists: map.has('t'),
set_exists: set.has(item),
obj_exist: 't' in obj
})
// 改
map.set('t', 2)
item.t = 2
obj['t'] = 2
console.info(obj, map, set)
// 删
map.delete('t')
set.delete(item)
delete obj['t']
console.info(obj, map, set)
}
Proxy 和 Reflect
Proxy (代理)
可以通过Proxy 为一个对象创建代理,从而设置外界对改对象访问的拦截
{
// 一个类似于供应商的原始数据对象 obj
let t = Symbol.for('t')
let obj = {
time: '2020-04-04',
name: 'net',
_r: 123,
[t]: 't'
}
// 通过Proxy 新生成一个对象,映射obj
let monitor = new Proxy(obj, {
// 拦截对象属性的读取
// get (target, key) {
// return target[key].replace('2020', '2000')
// },
get: (target, key) => {
if(key === 'time') return target[key].replace('2020', '2000')
return target[key]
},
// 拦截对象设置属性
set (target, key, value) {
if (key === 'name') {
return target[key] = value
} else {
return target[key]
}
},
// 拦截 key in object 操作
has (target, key) {
if (key === 'name') {
return target[key]
} else {
return false
}
},
// 拦截删除操作
deleteProperty (target, key) {
if (key.startsWith('_')) {
delete target[key]
return true
} else {
return target[key]
}
},
// 拦截Object.keys、Object.getOwnPropertySymbols、Object.getOwnPropertyNames
// 注意Object.keys 不会取到Symbol类型的key,所以下面的结果需要.concat拼接上Symbol类型的key的列表
ownKeys (target) {
return Object.keys(target).filter(item => item!='time')
.concat(Object.getOwnPropertySymbols(target))
}
})
console.log(monitor.time)
monitor.time = 2018
console.log(monitor)
monitor.name = 'test'
console.log(monitor)
console.log('name' in monitor)
console.log('time' in monitor)
// delete monitor.time
// console.log(monitor)
// delete monitor._r
// console.log(monitor)
console.log(Object.keys(monitor))
console.log(Object.values(monitor))
console.log(Object.getOwnPropertyNames(monitor))
console.log(Object.getOwnPropertySymbols(monitor))
}
Reflect(映射)
可以通过Reflect对某个对象进行操作
{
let t = Symbol.for('t')
let obj = {
time: '2020-04-04',
name: 'net',
_r: 123,
[t]: 't'
}
console.log(Reflect.get(obj, 'time'))
console.log(Reflect.get(obj, t))
Reflect.set(obj, 'name', 'test')
console.log(obj)
console.log(Reflect.has(obj, 'name'))
}
Proxy 的应用
以下代码实现:
- 函数validator 返回一个代理对象,并设置一些set验证,能避免外界错误地修改对象
- personValidators 对象中定义了各属性的验证规则
- Person类的构造函数,返回的实际上不是真正的实例对象,而是通过validator 函数得到一个实例对象的代理对象,从而将实例对象保护起来,这样就能避免误操作实例对象
{
function validator (target, validator) {
return new Proxy (target, {
_validator: validator,
set (target, key, value, proxy) {
if (target.hasOwnProperty(key)) {
let va = this._validator[key](value)
// console.log('va', va)
if (!! va) {
Reflect.set(target, key, value, proxy)
} else {
throw Error(`${key} 不满足条件,无法设置`)
}
} else {
throw Error(`${key} 不存在`)
}
}
})
}
const personValidators = {
name (val) {
return typeof val === 'string'
},
age (val) {
return typeof val === 'number' && val >= 18
}
}
class Person {
constructor (name, age) {
this.name = name
this.age = age
// 返回的是一个Proxy对象,代理了this,即代理了这个Person对象
// 在外面操作的是代理对象,不是peoson对象
return validator(this, personValidators)
}
}
let per = new Person('Tom', 25)
console.info(per)
//per.age = 8 // 报错,不满足条件,无法设置
}
类和对象
类的基本定义和生成实例
{
class Parent {
constructor (name = 'test') {
this.name = name
}
}
let parent1 = new Parent('v')
console.info(parent1)
}
继承
{
class Parent {
constructor (name = 'test') {
this.name = name
}
}
class Child extends Parent {
}
console.log(new Child())
}
继承 传递参数
{
class Parent {
constructor (name = 'test') {
this.name = name
}
}
class Child extends Parent {
constructor (name = 'child', age = 18) {
// 一定要把 super 放第一行
super(name)
this.type = 'child'
this.age = age
}
}
console.log(new Child('Jack', 20))
}
getter setter
{
class Parent {
constructor (name = 'test') {
this.name = name
}
get longName () {
return this.name
}
set longName (value) {
this.name = value
}
// 注意区分 getter 和 自定义的get属性
// get () {
// console.log('hhh')
// }
}
let parent = new Parent ()
console.log(parent.longName)
console.log(parent)
parent.longName = 'Tom'
console.log(parent)
}
静态方法 和 静态属性
- 静态方法: static 关键字
- 静态属性,创建类后,直接.出来
{
class Parent {
constructor (name) {
this.name = name
}
static say () {
console.log('hello!')
}
}
Parent.type = 'test'
Parent.say()
console.log(Parent.type)
}
Promise
为了解决回调地狱嵌套,EcmaScript 6 中新增了一个API,promise
。
基本用法, 以nodejs中读文件为例
var fs = require('fs')
// 返回一个Promise对象
var pReadFile = function (filePath) {
return new Promise (function (resolve, reject) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
pReadFile('./a.txt')
.then(function (data) {
console.log(data)
return pReadFile('./b.txt')
})
.then(function (data) {
console.log(data)
return pReadFile('./c.txt')
})
.then(function (data) {
console.log(data)
})
基本用法,以setTimeout为例
{
let test = (num) => {
return new Promise ((resolve, reject) => {
setTimeout(() => {
if (num > 5) {
resolve(num)
} else {
reject(`${num} <= 5`)
}
},500)
})
}
// 可以用.catch方法捕获错误
test(3).then((num)=>{
console.log(num)
}).catch((err)=>{
console.log('catch', err)
})
// 也可以给.then方法传入第二个callback参数
test(3).then((num)=>{
console.log(num)
},(err)=>{
console.log('then arg2', err)
})
test(6).then((num)=>{
console.log(num)
}).catch((err)=>{
console.log('catch', err)
})
}
Promise.all 方法
Promise.all()传入一个数组,成员为Promise实例对象,则返回一个新的Promise实例对象。
.then()则传入一个数组,表示所有的结果
所有的都处理完了,再一起处理
参考以下代码:
{
let loadImg = (src) => {
return new Promise((resolve, reject) => {
let img = document.createElement('img')
img.src = src
img.onload = () => {
resolve(img)
}
img.onerror = (err) => {
reject(err)
}
})
}
let showImg = (img) => {
document.body.appendChild(img)
}
let showImgs = (imgs) => {
imgs.forEach((img) => {
document.body.appendChild(img)
})
}
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4224877969,2952280193&fm=26&gp=0.jpg').then((img) => {
showImg(img)
})
// 把所有图片都加载完,再一起显示出来
Promise.all([
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4224877969,2952280193&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4224877969,2952280193&fm=26&gp=0.jpg'),
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4224877969,2952280193&fm=26&gp=0.jpg')
]).then((imgs) => {
showImgs(imgs)
})
}
Promise.race 方法
与Promise.all 类似, 传入的Promise实例中,有一个处理完了,就开始后续处理
{
let loadImg = (src) => {
return new Promise((resolve, reject) => {
let img = document.createElement('img')
img.src = src
img.onload = () => {
resolve(img)
}
img.onerror = (err) => {
reject(err)
}
})
}
let showImg = (img) => {
document.body.appendChild(img)
}
let showImgs = (imgs) => {
imgs.forEach((img) => {
document.body.appendChild(img)
})
}
Promise.race([
loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=4224877969,2952280193&fm=26&gp=0.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586016141886&di=731198488c2a55235d4805d94005943d&imgtype=0&src=http%3A%2F%2Fp4.music.126.net%2F1tsSSkZKQqqahfKlfnnoFA%3D%3D%2F18648816720560140.jpg'),
loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586016141887&di=1461c894cf4a8486b2d7097eaa728510&imgtype=0&src=http%3A%2F%2Fimg3.doubanio.com%2Flpic%2Fs21354179.jpg')
]).then((img) => {
showImg(img)
})
}
Promise 实现长轮询
{
let ajax = function () {
return new Promise ((resolve, reject) => {
// 把setTimeout改写成具体的ajax请求
setTimeout(()=>{
resolve({err_code: 0})
}, 200)
})
}
let pull = () => {
ajax().then((data)=>{
if (data.err_code === 0) {
setTimeout(() => {
console.log('wait')
pull()
}, 20)
} else {
console.log(data)
}
})
}
pull()
}
jquery支持promise方式的ajax
除了常用的callback的方式,jquery还支持promise方式的ajax
$.get('/data1')
.then(function (data) {
console.log(data)
return $.get('/data2')
})
.then(function (data) {
console.log(data)
})
// jquery
let pull = () => {
$.get('/data1')
.then(function (data) {
if (data.err_code === 0) {
setTimeout(()=>{
console.log('wait')
pull()
}, 50)
} else {
console.log(data)
}
})
}
pull()
封装原生ajax get函数(promise版本)
function get (url, callback) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.onload = function () {
// console.log(xhr.responseText)
callback && callback(xhr.responseText)
resolve(xhr.responseText)
}
xhr.onerror = function (error) {
// console.log(error)
reject(error)
}
xhr.send()
})
}
// callback 调用方式
get('/data1', function (data) {
console.log(data)
})
// promise 调用方式
get('/data1')
.then(function (data) {
console.log(data)
})
mongoose 数据库查询,promise方式
// callback 方式
User.findOne({
username: {$regex: '^a'}
}, function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
// promise 方式
User.findOne({
username: {$regex: '^a'}
})
.then(function (ret) {
console.log(ret)
}, function (err) {
console.log(err)
})
// 先找到一个,再更新
User.findOne({
username: {$regex: '^a'}
})
.then(function (ret) {
console.log(ret)
return User.findOneAndUpdate({
username: {$regex: /^a/}
},{
password: '0101001'
})
})
.then(function (ret) {
console.log('更新成功')
console.log(ret)
})
// 先查询,没查询到就新增
User.findOne({
username: 'Jason'
})
.then(function (ret) {
if (ret) {
console.log('存在该用户')
} else {
console.log('不存在该用户,新增一个!')
return new User({
username: 'Jason',
password: '123456',
email: 'admin@admin.com'
}).save()
}
})
.then(function (ret) {
if(ret) console.log('新增成功!')
console.log(ret)
}, function (err) {
console.log('新增失败')
})
lterator 和 for of
数组创建可迭代对象
数组可以使用for of,是因为 Array.prototype[Symbol.iterator]
{
let arr = ['hello', 'world']
let iter = arr[Symbol.iterator]()
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
}
为对象编写自定义 Symbol.iterator 属性
对象没有 Symbol.iterator 属性,若编写 Symbol.iterator 方法,则可以对对象使用 for of 遍历
{
let obj = {
start: [1, 3, 2],
end: [7, 9, 8],
// Symbol.iterator 属性是一个函数,实际上就是一个generator函数
[Symbol.iterator] () {
// let self = this
let index = 0
// let arr = self.start.concat(self.end)
let arr = this.start.concat(this.end)
let len = arr.length
// 这个函数要返回一个对象,返回的对象中要有next方法
return {
// next方法应该返回一个对象,有 value 和 done 属性
next () {
if (index < len) {
return {
value: arr[index++],
done: false
}
} else {
return {
value: arr[index++],
done: true
}
}
}
}
}
}
let iter = obj[Symbol.iterator]()
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
for (let value of obj) {
console.log(value)
}
}
Generator
解决异步编程: 回调、Promise、Generator
Generator 的基本定义
{
let tell = function* () {
yield 'a'
yield 'b'
return 'c'
}
let k = tell()
console.log(k.next())
console.log(k.next())
console.log(k.next())
console.log(k.next())
console.log(k.next())
console.log(k.next())
console.log(k.next())
}
设置对象的 Symbol.iterator 属性为一个Generator函数
{
let obj = {}
obj[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
var iter = obj[Symbol.iterator]()
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
for (let value of obj) {
console.log(value)
}
}
应用 状态机
// 状态机
{
let state = function* () {
while(1) {
yield 'A'
yield 'B'
yield 'C'
}
}
let status = state()
console.log(status.next())
console.log(status.next())
console.log(status.next())
console.log(status.next())
console.log(status.next())
}
抽奖次数
通过Generator将抽奖逻辑和抽奖次数控制逻辑分开
{
let draw = function (count) {
// 抽奖逻辑
console.log(`剩余${count}次`)
}
let residue = function* (count) {
while (count > 0){
count --
yield draw(count)
// draw(count)
}
}
var star = residue(5)
star.next()
star.next()
star.next()
star.next()
star.next()
star.next()
star.next()
star.next()
star.next()
}
Decorators
安装babel 以及 transform-decorators-legacy 插件
npm i babel-plugin-transform-decorators-legacy --save-dev
// 修改.babelrc文件, 加入插件
{
"presets": ["es2015"],
"plugins": ["transform-decorators-legacy"]
}
npx babel-node es6.decorators.js // 运行js脚本
Decorators修饰器 修改对象属性为只读
{
// 定义一个修饰器
let readonly = function (target, name, descriptor) {
descriptor.writable = false
return descriptor
}
class Test {
@readonly // 让time方法为只读
time () {
return '2020-04-05'
}
}
let test = new Test()
console.log(test.time())
// test.time = function () { // TypeError: Cannot assign to read only property 'time' of object '#<Test>'
// console.log('reset time')
// }
let obj = {
@readonly // 让a属性为只读
a: 1,
b: 2
}
// obj.a = 3 // TypeError: Cannot assign to read only property 'a' of object '#<Object>'
}
为类增加一个静态属性
{
let typename = function (target, name, descriptor) {
target.myname = 'hello'
}
// 为类增加一个静态属性
@typename
class Test {
}
console.log(Test)
}
有一个第三方修饰器的js库,core-decorators
,可以不用自己手动写修饰器函数
日志埋点从业务中抽离出来,提高代码复用性
{
// 把埋点系统从业务逻辑中抽离出来,提高复用性
let log = (type) => {
return (target, name, descriptor) => {
// descriptor.value就是被修饰的对象的方法, 把 descriptor.value 拷贝一份,方便后面修改和调用
let src_method = descriptor.value
// 在执行之前输出 begin
console.log(type, 'begin')
// 修改被修饰的方法,在执行完毕后输出 end
descriptor.value = (...arg) => {
// src_method()
// target 为实例对象,arg为方法调用时的参数列表
src_method.apply(target, arg)
console.log(type, 'end')
}
}
}
// 业务逻辑
class AD {
@log('show')
show () {
console.log('ad is show')
}
@log('click')
click () {
console.log('ad is click')
}
}
let ad = new AD ()
ad.show()
ad.click()
}
模块化
node中仍不支持es6的模块化,需要 npx babel-node xxx.js
导出方
export let A = 123
export function test () {
console.log('test')
}
export class Hello {
test() {
console.log('class')
}
}
let A = 123
let test = () => {
console.log('test')
}
class Hello {
test () {
console.log('class')
}
}
export default {
A,
test,
Hello
}
引入方
// import {A, test, Hello} from './es6.17.js'
import * as obj from './es6.17.js'
console.log(obj.A)
console.log(obj.test)
console.log(obj.Hello)
let hello = new obj.Hello()
hello.test()
import obj from './es6.17.js'
console.log(obj.A)
console.log(obj.test)
console.log(obj.Hello)
let hello = new obj.Hello()
hello.test()