ES6+
var、let、const
var
// 1
function ff() {
console;log(a) // undefined
var a = 1; // a: ff
}
ff()
// 2 自动变量类型提升成全局变量
var b = 1 // b = 1; window.b = 1;
// 3 多个(自动提升成一个唯一的i => 012345) window.i === 5
var lis = document.querySelectorAll('li');
for(var i = 0; i < lis.length; i++){
lis[i].onclick = function() {
alert(i) // i === 5
}
}
// 解决方式
// 1、自执行函数(闭包)
for(var i = 0; i < lis.length; i++){
(function(n) {
lis[n].onclick = function() {
alert(n) // n 分别为0、1、2、3、4
}
})(i)
}
// 2、给元素绑定值
for(var i = 0; i < lis.length; i++){
lis[i].id = i;
lis[i].onclick = function() {
alert(this.id) // 分别为0、1、2、3、4
}
}
// 3、var => let
for(let i = 0; i < lis.length; i++){
(function(n) {
lis[n].onclick = function() {
alert(n) // n 分别为0、1、2、3、4
}
})(i)
}
let
顶部声明不会提升为全局,块级作用域(唯一)
语法上不允许多次声明同名变量
值可以改变,且可以二次赋值
let b = 2;
// window.b // undefined 不会全局
let n = 99;
if(true) {
let n = 3;
console.log(n) // 3 就近原则
}
let n2 = 1;
if(true) {
// let 声明的变量属于块钱作用域
// 编辑期将其解析 不在向上查找
// 在初始化之前,不能访问该变量: 临时性死区
console.log(n2) // 报错
let n2 = 100;
}
const
值不能二次改变,并且必须一次性赋值
const PI = '3.1415926'
const obj = {}; // 存储的地址
obj.a = 1;
obj.b = 2;
箭头函数
箭头函数不能作为构造函数
this是根据上线文固定的,而不是动态的this对象决定
不能被call bind apply 改变指向
// 多个参数
let ff1 = (a,b) => {
console.log('xxx');
return 1;
}
// 一个参数
let ff2 = a => {
console.log('xxx');
return 2;
}
// 一行代码就是返回值
let ff3 = a => a;
// 返回值是一个对象
let ff2 = a => ({});
// 复杂写法
// 看箭头+左边参数 右侧是返回值或者函数体
let aa = n => n => (n, m) => (n, m) => n // aa 是一个函数
解构赋值
左边解构:解析解构
右边赋值
对象(按名称)
let {a,xxx} = {a:1, b:2}
console.log(a,xxx) // 1 undefined
// :(+别名)取别名(a不存在只是一个标识)
let {a: asA} = {a:1, b:2}
console.log(asA) // 1
// :(+结构)
let {obj: {b}} = {obj: {a:1, b:2}}
console.log(b) // 2
数组(按顺序)
let [a,b,c] = [1,2,3]
console.log(a,b,c) // 1 2 3
// 不可取别名
// 跳着获取元素
let [,b1,c1] = [1,2,3]
console.log(b1,c1) // 2 3
函数参数解构
function parseProp({client}) {
return client
}
函数默认值
优先已传递进来的为准,没有传递填充默认值
// 参数默认值
function add(a=0,b=0) {
return a + b
}
组合运用
const fun = ({x=1, y} = {y:2}) => {
// 隐性存在
// 没有传递参数: Object.assign(形参, 默认值对象)
// 传递参数: 以形参为主
console.log(x,y)
}
fun() // 1 2 // 二合一 以默认值为主
fun({x: 1}) // 1 undefined // 以形参为主
展开运算符
便于代码复用
属于浅拷贝 大规模使用时需要注意引用数据类型的深拷贝
let arr = [1,2,3]
let arr2 = [0, ...arr]
let obj = {name: '张三’, age: 19}
let obj2 = {...obj, address: ''}
// 注意: 属性覆盖 先展开后覆盖
let obj3 = {
...obj,
name: '李四'
}
// ...args:是一个数组 可以接受到剩余参数
function add(...args) {
return args.reduce((sum, n) => {
return sum + n
}, 0)
}
arr(1,2,3,4,5,6,7,8,9)
无限平铺
let arr = [1,[2,[3,[6,[7]]]], 4,5]
arr.flat() // 返回一个新数组 [1, 2, [3,[6,[7]]], 4, 5] 只展开一层
arr.flat(Infinity) // 返回一个新数组 [1, 2, 3, 6, 7, 4, 5] 无限展开
// 利用...平铺
function myFlat(arr) {
// 判断元素是否时数组 不是结束循环
// while(arr.some((ele) => {
// return Array.isArray(ele)
// })) {
// arr= [].concat(...arr)
// }
// 简化
while(arr.some(Array.isArray)) {
arr= [].concat(...arr)
}
return arr;
}
const newArr2 = myFlat(arr2);
可选链
// 对象数组可选链
let id = res?.data?.info?.[4]?obj?.id
// 方法可选链
function error(fn) {
console.log('错误信息')
fn?.('回调信息')
}
error()
error((info) => console.log('回调执行'))
空值合并(??)
默认数据的填充,区分隐式数据类型数据转换
主要针对于null undefined
console.log(null ?? '后') // 后
console.log(undefined ?? '后') // 后
console.log(0 ?? '后') // 0
console.log('' ?? '后') // ''
console.log(null || '后') // 后
console.log(undefined || '后') // 后
console.log(0 || '后') // 后
console.log('' || '后') // 后
Map Set WeakMap WeakSet
具备优化性能的算法存储数据 同时自带去重功能
Set
单列的(key === value)
let arr = [1,2,3,2,2,3,1,213,43214,1,1,3,2]
// 1.直接通过数组构造(数组去重,但不会排序)
let set = new Set(arr)
// 2.添加数据(可以为任意数据类型 且自带去重)
set.add(8);
set.add({}); // 引用数据类型 堆 对应一个地址
let obj = {}
set.add(obj)
// 3.转换成数组 (元素不会存在深拷贝)
let arr2 = Array.from(set)
// 4.长度(size)
console.log(set.size)
// 5.删除(delete)
set.delete(null)
set.delete({}) // 无法删除 生成新的 堆 及 新地址
set.delete(obj) // 删除obj 删除需要先声明在删除
// 6.是否存在(has)
console.log(set.has(1))
// 7.(keys) 返回 Iterator 迭代器
set.keys()
// 8.(values) 返回 Iterator 迭代器
set.values()
// 9.(entries) // Map = keys + values 返回 Iterator 迭代器
set.entries()
// 10.(forEach)
set.forEach((val,key) => {
console.log(val, key)
})
// 11.清空所有(clear)
set.clear()
数组去重排序
const killDup = (arr) => {
return Array.from(new Set(arr))
}
// 数组去重
let arr = killDup([5,4,4,3,2,1,7,8,9,6,5,3,2,4])
// 数组排序
arr.sort(); // 只能针对于10以内的数据排序
// 如果 compareFn(a,b) return值 >0 b在a之前
// 如果 compareFn(a,b) return值 <0 a在b之前
// 如果 compareFn(a,b) return值 =0 a、b相对位置不变
arr.sort((a,b) => {
// 数组正序 判断a是否大于b => a > b => b,a
// return a - b;
// 数组逆序 判断b是否大于a => b > a => b,a
return b - a;
})
Map
// 1.创建
let obj = {name: 'zs'}
let arr = [['a', 1], [obj, 2]]
let map = new Map(arr)
// 2.添加(set)
map.set('b', 3)
map.set('c', 4)
// 3.判断(has)
console.log(map.has(obj))
// 4.长度(size)
console.log(map.size())
// 5.删除(delete)
// 6、转换
let arr2 = Array.from(map)
// 7.(keys) 返回 Iterator 迭代器
map.keys()
// 8.(values) 返回 Iterator 迭代器
set.values()
// 9.(entries) // Map = keys + values 返回 Iterator 迭代器
map.entries()
// 10.(forEach)
map.forEach((val,key) => {
console.log(val, key)
})
// 11.清空所有(clear)
map.clear()
补 数组:计算机基础中 数组是一块联系的空间(查找快),计算机开辟连续空间【new Array(空间大小) 】增加删除后改变空间大小【再次开辟新空间,拷贝数据】
Map底层实现
cosnt mapSize = 8;
function MyHashMap() {
this.initStore();
}
MyHashMap.prototype.initStore = function() {
this.store = new Nrray(mapSize);
for(let i = 0; i < mapSize; i++) {
this.store[i] = {
next: null
}
}
}
MyHashMap.prototype.hash = function(n) {
return n % mapSize; // 取余
}
MyHashMap.prototype.has = function(key) {
return !!this.get(key)
}
MyHashMap.prototype.get = function(key) {
// 根据key计算房间号
let index = this.hash(key);
// queue 对象
let queue = this.store[index];
while(queue.next) {
if(queue.key === key) {
return queue.val;
} else {
// 指针下移
queue = queue.next
}
}
return undefined;
}
// 存储数据
MyHashMap.prototype.set = function(key, val) {
// 根据key计算房间号
let index = this.hash(key);
// queue 对象
let queue = this.store[index];
// 找后面next是否有数据,对比key,如果满足则覆盖
// 链表
while(queue.next) {
if(queue.next.key === key) {
queue.next.val = val; // 覆盖
return;
} else {
// 再看下一个
queue = queue.next
}
}
// 赋值
queue.next = {
next: null,
key,
val
}
}
var map = new MyHashMap()
map.set(1, 'a')
map.set(2, 'b')
map.set(3, 'c')
map.set(9, 'd')
console.log(map)
console.log(map.get(9))
console.log(map.has(2))
WeakMap
相对于Map无遍历;key只能使用引用数据类型(对象)
let map = new WeakMap();
//增
let obj = {}
map.set(obj, 1)
// 取
console.log(map.get(obj))
// 判
console.log(map.has(obj))
// 删
map.delete(obj)
// 无遍历
WeakSet
key只能使用引用数据类型 (对象)
let set = new WeakSet();
// 增
let obj = {}
set.add(obj)
// 不可以单个获取值,可转成数组
// 判
console.log(set.has(obj))
// 删
set.delete(obj)
// 无遍历
强引用
Map、Set
functin Green() {}
let arr = new Array(20000).fill(null),map(e => new Green())
let map = new WeakMap();
// WeakMap的key只能使用引用数据类型
let obj = {}; //引用计数1
map.set(obj,arr); // 不会引用计数+1
obj = null // 引用计数-1
// 释放arr
arr = null; // 引用计数-1
functin Green() {}
let arr = new Array(20000).fill(null),map(e => new Green())
// Map的key可以使用引用数据类型
let map = new Map();
let obj = {}; //引用计数1
map.set(obj,arr);
// 附加处理map内的强引用
map.delete(obj);
obj = null // 引用计数-1
// 释放arr
arr = null; // 引用计数-1
弱引用
WeakMap、WeakSet
functin Green() {}
let arr = new Array(20000).fill(null),map(e => new Green())
let map = new WeakMap();
// WeakMap的key只能使用引用数据类型
let obj = {}; //引用计数1
map.set(obj,arr); // 不会引用计数+1
obj = null // 引用计数-1
// 释放arr
arr = null; // 引用计数-1
WeakRef(了解)
能不使用尽量不要使用
functin Green() {}
let arr = new Array(20000).fill(null),map(e => new Green())
// 强引用
// let arr2 = arr; // 引用计数+1
// arr2 = null;
// 弱引用
let ref = new WeakRef(arr); // 引用计数不存在+1
// GC垃圾回收一旦执行获取的值deref就是undefined
console.log(ref.deref().length)
arr = null
手写深拷贝优化
let obj1 = {
arr: [1,2,3],
arrOfObjs: [{c: 5}, {d: 6}],
obj: {val: 4},
fn: function() {
return 5;
},
set: new Set(),
map: new Map(),
nul: null,
undef: undefined,
};
// 环形对象
var obj2 = {
to: obj1
}
obj1.to = obj2
// 处理环形对象(全局性对象统一缓存)
var cache = new WeakMap()
var cloned = deepClone(obj1);
function deepClone(obj) {
if(typeof obj !== 'object' || !obj){
// 处理基本的数据类型 undefined null function
retutn obj;
}
if(cache.has(obj)){
return cache.get(obj)
}
let tmp;
if (obj instanceof Map) {
tmp = new WeakMap();
cache.set(obj,tmp);
obj.forEach((val, key) => {
tmp.set(deepClone(key), deepClone(val))
})
} else if (obj instanceof Set) {
tmp = new Set();
cache.set(obj,tmp);
obj.forEach((val) => {
tmp.add(deepClone(val))
})
} else if (obj instanceof RegExp || obj instanceof Date) {
tmp = new obj.constructor(obj);
cache.set(obj,tmp);
} else {
// 处理引用数据类型 数组 || 对象 及 其他
tmp = new obj.constructor();
cache.set(obj,tmp);
for(var key in obj){
tmp[key] = deepClone(obj[key]);
}
}
return tmp;
}
Symbol常量
toStringTag
底层类型标注
function Person(){}
let obj = new Person()
console.log(Object.prototype.toStrig.call(obj)) // [object Object]
console.log(Object.prororype.toString.call({})) // [objcet Object]
functin Person1(){}
// 底层类型标注
Person1.prototype[Symbol.toStringTag] = Person.name
let obj1 = new Person();
console.log(Object.prototype.toStrig.call(obj1)) // [object Person]
toPrimitive
引用数据类型在做基本数据类型转换时的底层行为 例如 + - * %
let obj = {name: 'zhangsan'};
console.log(obj - 1) // NaN
let obj1 = {name: 'lisi', age: 36};
obj1.__protp__[Symbol.toPrimitive] = function(){
// return 99;
return this.age
}
// consloe.log(obj - 2) // 97
// consloe.log(obj - 2) // 34
iterator
可迭代
let map = new Map([['a': 1], ['b': 2]])
let obj = Object.fromEntries(map); // Map转Object
console.log(obj);
obj.c = 3;
// let map2 = new Map(obj); // error: obj is not iterable
// for(let val of obj) {
// console.log(val)
//} // error: obj is not iterable
// 解决方式:
obj.__proto__[Symbol.iterator] = function() {
let entries = Object.entries(this) // Object转Map this为{a: 1, b: 2, c: 3}
let len = entries.length
let index = 0
return {
next: () => {
let done = index >= entries.length
let value = entries[index];
index++;
return {
value,
done
}
}
}
}
for(let val of obj) {
console.log(val)
}
generator(了解)
迭代器(过渡)
// 生成器的声明
function* gen() {
console.log('abc')
// 礼让 => 异步
let res = yield 'eee'
console.log('1111', res)
}
// 获取生成器对象
let generator = gen()
console.log(generator)
let res = generator.next() // 代码执行 礼让 abc
generator.next(res.value) // 将礼让时的值传入 再次执行 1111 eee
Promise
回调函数
function req(fn) {
setTime(() => {
// 数据回来
let data = 123;
fn(data) // 回调
}, 1000)
}
req((data) => {
console.log(data)
});
回调函数(嵌套)地狱
function req(fn) {
setTime(() => {
// 数据回来
let data = 123
fn(data) // 回调
}, 1000)
}
req((data) => {
console.log(data)
req((data1) => {
console.log(data1)
req((data2) => {
console.log(data2)
});
});
});
解决回调函数地狱
通过异步容器管理异步执行顺序
使代码更为简洁
function req() {
// resolve 解决
// reject 拒绝
return new Promise((resolve, reject)) => {
setTime(() => {
// 数据回来
let data = Math.random()
resolve(data)
}, 1000)
}
}
// 通过promise实例.then((res) => {})
req() // 请求1
.then(res => {
console.log(res) // 响应1
return req(); // 请求2
})
.then(res2 => {
console.log(res2) // 响应2
return req(); // 请求2
})
.then(res3 => {
console.log(res3) // 响应3
})
状态及后续
// pending(待发) <= 初始态
let p1 = new Promise((resolve, reject) => {})
// fulfilled(已满足) <= resolve
let p2 = new Promise((resolve, reject) => {
resolve('123')
})
.then(res => {
console.log(res)
}) // 处理完成后 仍然处于fulfilled状态
.finally(() => {
console.log('无论如何最终将会执行')
})
// rejected(已拒绝) <= reject
let p3 = new Promise((resolve, reject) => {
reject('err')
})
.catch(err => {
console.log(err)
}) // 处理完成后 变成fulfilled状态
// 实现中可以多组then 对应一个catch
// then接在fulfilled状态后
new Promise((resove, reject) => {
resove(123)
})
.then(res123 => {
console.log(res123);
return new Promise((resove, reject) => {
resove(456)
})
})
.then(res456 => {
console.log(res456)
})
PromiseApi
function req() {
return new Promise((resolve, reject)) => {
setTime(() => {
let data = Math.random()
if(data < 0.5){
reject('req失败了')
} else {
resolve(data)
}
}, 1000)
}
}
function req1() {
return new Promise((resolve, reject)) => {
setTime(() => {
let data = Math.random()
if(data > 0.5){
reject('req1失败了')
} else {
resolve(data)
}
resolve(data)
}, 1000)
}
}
// 1、Promise.all()
// 如果多个请求,任意一个失败则失败
Promise.all([
req(),
req1(),
]).then(([res, res1]) => { // 返回的是一个数组 进行解构
// 数据响应
console.log(res, res1)
}).catch((err) => {
console(err)
})
// 2、Promise.race()
// 获取的是第一个响应的结果
Promise.race([
req(),
req1(),
]).then((res) => {
// 数据响应
console.log(res)
}).catch((err) => {
console(err)
})
// 3、Promise.resove()
// 直接创造 fulfilled 状态
const fulfilledPromise = Promise.resove('aaa')
fulfilledPromise.then(res => {console.log(res)})
// 4、Promise.reject()
// 直接创造 rejected 状态
const rejectedPromise = Promise.reject('err')
rejectedPromise.catch(err => {console.log(err)})
async await
// 输出结果 1 3 5 2 4
async function asyncFn() {
console.log(3)
await new Promise(res => {
console.log(5)
res()
});
// 如果Promise不调用res 则不会执行console.log(4)
// 等同于.then()里面的代码
console.log(4)
}
console.log(1)
asyncFn()
console.log(2)
优雅处理async await
try catch 处理
function req() {
return new Promise((resolve, reject)) => {
setTime(() => {
let data = Math.random()
if(data < 0.5){
reject('req失败了')
} else {
resolve(data)
}
}, 1000)
}
}
async function run() {
try{
let res = await req()
console.log(res)
}catch(e){
console.log(e)
}finally(){
console.log('最终')
}
}
run()
中间层处理
function req() {
return new Promise((resolve, reject)) => {
setTime(() => {
let data = Math.random()
if(data < 0.5){
reject('req失败了')
} else {
resolve(data)
}
}, 1000)
}
}
function pretty(promise) {
// 参数错误优先
return promise
.then(res => {
return [undefined, res]
})// 处理完成后变成 fulfilled 状态
.catch(err => {
return [err, undefined]
})// 处理完成后变成 fulfilled 状态
}
async function run() {
let [err, data] = await pretty(req())
if(err){
console.log('数据错误了')
return
} else {
console.log(data)
}
}
run()
集中处理
function req() {
return new Promise((resolve, reject)) => {
setTime(() => {
let data = Math.random()
if(data < 0.5){
reject('req失败了')
} else {
resolve(data)
}
}, 1000)
}
}
async function run() {
var res = await req()
console.log(res)
}
run()
window,addEventListener('unhandledrejection', (err) => {
err.preventDefault()
console.log(err.reason)
})
forEach存在的坑
let arr = [1,2,3,4,5, {name: 'zhangsan'}]
function req(n) {
return new Promise((resolve, reject)) => {
setTime(() => {
resolve(n)
}, 1000)
}
}
// 存在调用时间不会每隔1秒执行一次
// arr.forrEach(async ele => {
// await req(ele)
// })
// 解决方式重写forEach
// 实现forEach覆盖
Array.prototype.forEach = async function(fn){
if(typeof fn !== 'function') throw new Errer('不是函数')
if(!Array.isArray(this))throw new Errer('this不是数组')
for(let i = 0, len = this.length; i < len; i++){
// this, ele, index, newArr
await fn.call(this, this[i], i, this)
}
}
arr.forrEach(async (ele, index, newArr) => {
await req(ele)
})