导航
[[深刻01] 执行上下文](https://juejin.im/post/684490...
[[深刻02] 原型链](https://juejin.im/post/684490...
[[深刻03] 继承](https://juejin.im/post/684490...
[[深刻04] 事件循环](https://juejin.im/post/684490...
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深刻08] 前端平安](https://juejin.im/post/684490...
[[深刻09] 深浅拷贝](https://juejin.im/post/684490...
[[深刻10] Debounce Throttle](https://juejin.im/post/684490...
[[深刻11] 前端路由](https://juejin.im/post/684490...
[[深刻12] 前端模块化](https://juejin.im/post/684490...
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深刻14] canvas](https://juejin.im/post/684490...
[[深刻15] webSocket](https://juejin.im/post/684490...
[[深刻16] webpack](https://juejin.im/post/684490...
[[深刻17] http 和 https](https://juejin.im/post/684490...
[[深刻18] CSS-interview](https://juejin.im/post/684490...
[[深刻19] 手写Promise](https://juejin.im/post/684490...
[[深刻20] 手写函数](https://juejin.im/post/684490...
[[react] Hooks](https://juejin.im/post/684490...
[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...
[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
前置常识
堆栈
stack栈
- <font color=red>栈区</font> 蕴含了:<font color=red>变量的标识符</font> 和 <font color=red>变量的值</font>
栈区:
- 指的是内存中的栈内存
- 根本类型的数据(值类型数据)保留在栈中
比拟
- <font color=red>根本类型数据的比拟是(值)得比拟</font>
- <font color=red>根本类型的数据(不可变),不能增加属性和办法</font>
heap堆
堆区
- 援用类型的数据保留在栈和堆中
- 栈区保留:<font color=red>变量标识符</font> 和 指向堆内存中对象的 <font color=red>指针</font>
- 堆区保留:具体的对象数据
比拟
- <font color=red>援用类型数据的比拟是(援用)的比拟</font>
- <font color=red>援用类型的数据(可变),能够增加属性和办法</font>
数据类型
- 根本数据类型(值类型):number,string,boolean,null,undefined,symbol
- 援用类型的数据:object,array,function等
区别:
- <font color=red>根本类型没有属性和办法,大小固定,保留在栈区</font>
- <font color=red>援用类型有属性和办法,大小不固定,保留在栈区和堆区,栈中保留指向堆中数据的地址</font>
数据类型的案例
援用类型和原始类型的案例var a = 1 // 根本类型的数据var b = {name: 'woow_wu7'} // 援用类型的数据var aa = a // a 和 aa 是不同的数据var bb = b // b 和 bb 指向堆中的同一份数据,批改堆中数据,b和bb的指向没变,则援用的值也会跟着扭转a = 2b.name = 'wang'console.log(a, aa, 'a和aa是不同的数据') // 扭转后不等console.log(b.name, bb.name, 'b和bb两个变量中的指针 => 都同时指向了同一个堆内存中的数据') // 扭转后相等console.log(b === bb) // true,阐明两个变量指向了同一个堆内存
Map数据结构
Object对象的key只能是字符串
字符串-值
对应
Map数据结构的key能够是任意类型
值-值
对应
- <font color=red>Map相似于对象,也是key,value的键值对的汇合</font>
- Map是一种更欠缺的hash构造实现,如果你须要键值对的数据结构,Map比Object更适合
Map.prototype.set(key, value) // key能够是任意类型
Map.prototype.get(key)
Map.prototype.has(key) // 返回布尔值
Map.prototype.delete(key) // 删除某个键,但返回布尔值,示意是否删除胜利
Map.prototype.clear() // 革除所有成员,没有返回值
Map.prototype.keys() values() entries() forEach()
- <font color=red>Map构造函数能够承受数组为参数,成员必须是一个个示意键值对的数组</font>
<font color=red>Map能保障对象key的唯一性</font>
const mapArrKey = [1,2];const mapKeyAddress = ['chongqign']const mapInstance = new Map([['name', 'woow_wu'],[[1,2], 20],[mapArrKey, 20],[{age: 20}, {age: 20}],])console.log(mapInstance, 'mapInstance')console.log(mapInstance.size, 'size') // 4console.log(mapInstance.get(mapArrKey), 'Map.prototype.get(key) => key是一个数组')console.log(mapInstance.get([1,2]), 'Map.prototype.get(key)') // undefined 必须是同一个数组mapInstance.set(mapKeyAddress, '地址')console.log(mapInstance.get(mapKeyAddress))console.log(mapInstance.has(mapKeyAddress), 'Map.prototype.has(key) => key是否存在,布尔值') // trueconsole.log(mapInstance.delete(mapKeyAddress), 'Map.prototype.delete(key) => 删除键,返回布尔值,示意是否删除胜利') // trueconsole.log(mapInstance.get(mapKeyAddress)) // undefinedconsole.log(mapInstance.clear(), 'Map.prototype.clear() => 删除所有键,没有返回值')console.log(mapInstance)
Reflect
- 操作对象的api
- reflect:反映,反射的意思
Reflect.get(target, name, receiver)
- 获取target对象的name属性,如果没有该属性返回undefined
- 如果name属性部署了getter函数,getter函数中的this指向receiver参数对象
- 如果target参数不是对象,Reflect.get()会报错
Reflect.set(target, name, value, receiver)
- 设置target对象的name属性为value
- 如果name属性部署了settter函数,setter函数中的this指向receiver参数对象
Reflect.deleteProperty(obj, name)
- 删除obj的name属性
- 相当于: delete obj.name
Reflect.constructor(target, args)
- 执行构造函数target,传入target构造函数的参数是args
- 如果target不是函数,就会报错
Reflect.getPrototypeOf(obj)
- 相等于:Object.getPrototypeOf(obj)
Reflect.setProrotypeOf(obj, prototypeObj)
Reflect.apply(func, thisArg, args)
- 等于:Function.prototype.apply.call(func, thisArg, arges)
<font color=red>Reflect.ownKeys(target)</font>
返回对象参数的所有属性,留神:包含Symbol类型的属性
等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和
- 包含symbol类型的属性!!!
运算符的联合性
- <font color=red>一元运算符,三元运算符,赋值运算符是右联合,其余的都是左联合</font>
三元运算符是右联合:即从右向左算,先算左边的三元表达式,再算右边的三元表达式
三元运算符右联合let name = null
=== true ? name = 'wang' : 1 < 0 ? name = 'zhang' : name = 'woow_wu7';
//相当于:1 === true ? name = 'wang' : (1 < 0 ? name = 'zhang' : name = 'woow_wu7')console.log(name, 'name')
// 'woow_wu7'
// 1 === true // false,类型不一样都是false
typeof返回值
- typeof能够用来判断根本数据类型,返回值是字符串
- typeof不能辨别对象类型的数据:如对象还是数组
<font color=red>typeof 的返回值一共有7种类型</font>
- 返回值有(7种):number,string,boolean,undefined,symbol,function,object
根底数据类型(6种):number,string,boolean,undefined,symbol,null
typeof NaN ---------------------- 'number'
typeof Symbol() ----------------- 'symbol'
typeof function(){} ------------- 'function'
typeof [] ---------------------- 'object'
typeof {} ---------------------- 'object'
typeof null --------------------- 'object'
浅拷贝和深拷贝的区别
- <font color=red>浅拷贝是只进行一层拷贝,深拷贝是拷贝所有层级,直到属性对应的值是原始数据为止</font>
浅拷贝
- 创立一个新对象(创始一个新的堆空间),该新对象有着原始对象属性值的准确拷贝
- 属性值是根本类型,拷贝的就是根本类型的值(
即拷贝的是值,互不烦扰
) - 属性值是援用类型,拷贝的就是指向堆内存的指针(
即拷贝的是指针,互相烦扰
)
深拷贝
- 在堆内存中创立一个新空间,把对象实现的拷贝到新空间中,互相独立,互不烦扰
浅拷贝和赋值的区别
之所以不易辨别浅拷贝和赋值,是因为拷贝后个别都随同者赋值
- 赋值:两个变量对象的指针,指向同一个堆内存中的对象数据,不会开翻新的堆空间
- 浅拷贝:<font color=red>创始一个新的堆内存空间(即创立一个新对象),新对象是对原始对象的一个准确拷贝,属性是根本类型的值拷贝的就是根本类型的值,如果属性是援用类型的值,拷贝的就是堆内存的指针</font>
一句话总结: (1)赋值不会开翻新的堆内存空间,而浅拷贝会开翻新的堆内存空间; (2)赋值:扭转对象属性相互影响; (3)浅拷贝:扭转属性值是原始类型时,互不烦扰。扭转的属性值是援用类型时,相互影响 - </table></tr></td>
赋值和浅拷贝的区别实例
赋值和浅拷贝的区别var a = { name: 'woow_wu', score: { ch: 90, en: 80 }};var b = avar c = {...a}console.log(a===b, '赋值 => 不会开翻新的堆空间,批改相互影响') // true,阐明是同一份堆数据console.log(a===c, '浅拷贝 => 会开翻新的堆空间,批改原始值属性互不烦扰,批改援用值属性,相互影响') // false,不同堆数据a.name = 'wang'console.log(b, 'b') // 相互影响console.log(c, 'c => 浅拷贝,批改属性值为根本类型 => 互不烦扰') // 互不烦扰a.score.en = 100console.log(c, 'c => 浅拷贝,批改属性值为援用类型 => 相互影响') // 相互影响
浅拷贝
对象的浅拷贝
- Object.assign()
- {...} 开展运算符
数组浅拷贝
- Array.prototype.slice()
// 不传参
- Array.prototype.concat()
// 不传参
[...] 开展运算符
const arr = [1, 2, 3]---
- slice
slice(start, end)
- 截取指标数组的一部分,----------------------------------- ( 返回一个新数组,不扭转原数组 )
- start起始地位,从0开始,能够取到
- end终止地位,留神取不到end // arr.slice(0, 2) // [1, 2]
上面三种写法等价
- arr.slice()
- arr.slice(0)
- arr.slice(0, 3) 三者的后果一样
- concat
concat
- 用于多个数组的合并,它将新数组的成员,增加在原属数组的尾部,---- ( 返回一个新数组,不扭转原数组 )
参数
- concat的参数除了是 ( 数组 ) 还能够是 ( 其余任意类型的值 )
- 数组的浅拷贝
- arr.concat() // 不加参数
- arr.slice() // 不加参数
[...arr]
深拷贝
办法一 JSON.parse(JSON.stringify())
- 毛病:
只能深拷贝对象和数组,但不能拷贝函数,循环援用,原型链上的属性和办法(Date, RegExp, Error等)
const objComplex = {name: 'woow_wu7',address: { city: 'chongqing', district: 'yubei', town: 'jiazhou', detail: ['chongqing', 'yubei', 'jiazhou']},arr: [1,2, {l:20, r: 30}],fn: function(){}, // Functiondate: new Date(), // Dateerr: new Error(), // Errorreg: new RegExp(), // RegExpnumber: 1,string: '',boolean: true,null: null,undefined: undefined,symbol: Symbol('symbol'), // Symbol}const copy = JSON.parse(JSON.stringify(objComplex))console.log(objComplex, 'objComplex')console.log(copy, 'copy')如下图:JSON.parse(JSON.stringify()) 不能拷贝function,Date,Error,RegExp,等对象
办法二
根底版 - for...in循环递归(1)
- <font color=red>要求:能够拷贝对象和数组</font>
未解决:
- 循环援用
- Symbol()类型key的属性对应值的拷贝
其余对象的复制如 Date,Error,Regexp,Symbol数据类型等
const objComplex = {name: 'woow_wu7',address: {city: 'chongqing',district: 'yubei',town: 'jiazhou',detail: ['chongqing', 'yubei', 'jiazhou']},score: [100, 200]}function deepClone(parameter) {const parameterType = Object.prototype.toString.call(parameter).slice(8, -1)// 获取类型字符串// Array.prototype.slice(8, -1) 从下标为8的字符开始截取,直到倒数第2个值// 因为第一个参数地位能够取到,第二个参数地位取不到const cloneObj = null// 参数是数组,赋值[]// 参数是对象,赋值{}// 其余类型:间接返回if (parameterType === 'Array') {cloneObj = []}else if (parameterType === 'Object') {cloneObj = {}}else {return parameter}for(let key in parameter) {// for...in // 1. 循环用来遍历对象 ( 所有可遍历的属性 ),会 ( 跳过不可遍历的属性 )// 2. 不仅能够遍历 ( 本身属性 ),还能够遍历 ( 继承的属性 )// 3. 所以个别状况下,( for...in都要联合hasOwnProperty来遍历本身的属性 )if (parameter.hasOwnProperty(key)) { // 是否是本身属性 if (typeof parameter[key] === 'object') { // 对象或数组,持续判断 // 这里应用typeof没有去区是分数组或对象,因为会在deepClone中去做判断 cloneObj[key] = deepClone(parameter[key]) } else { // typeof不是objet // 则有可能是:number string boolean undefined symbol function // 这里没有思考 function 的拷贝 cloneObj[key] = parameter[key] }}}return cloneObj}const res = deepClone(objComplex)console.log(res, 'res')
----------更精简的写法const obj = { name: 'woow_wu7', address: { city: 'chongqing', districe: 'yubei', town: 'jiazhou', detail: ['chongqign', 'yubei', 'jiazhou'] }, arr: [1,2]}function deepClone(parameter) { if (typeof parameter === 'object') { // 这里只思考 对象和数组 // typeof返回值是字符串,有7种 // number string boolean undefined symbol function object const objClone = Array.isArray(parameter) ? [] : {} for (let key in parameter) { if (parameter.hasOwnProperty(key)) { objClone[key] = deepClone(parameter[key]) // 不论是对象类型还是根本数据类型,都去调用deepClone(parameter[key]) // 在deepClone()函数中会去判断对象类型是数组还是对象,根本数据类型间接返回并赋值给objClone[key] } } return objClone } else { // 不是数组和对象间接返回形参 // 留神形参是新申明的变量 return parameter }}const res = deepClone(obj)console.log(obj)console.log(res)
Map 解决循环援用 - for...in循环递归(2)
<font color=red>要求: 能够拷贝对象和数组,并解决循环援用问题</font>
(1) 什么是循环援用?const obj = {name: 'wang'}obj.circle = obj// obj新增circle属性,值是obj对象自身// 这样的状况,像下面的代码例子中,for..in循环中deepClone(parameter[key])会一直反复执行// 最终造成内存溢出----------
- 查看map实例中是否有克隆过的对象
- 如果存在,间接返回
如果不存在,就赋值键值对,key是传入的对象,value是克隆的对象
var objComplex = {
address: {
city: 'chongqing',
town: 'jiazhou',
},
score: [100, 200],
}
objComplex.circular = objComplexfunction deepClone(objComplex, mapx = new Map()) { // 默认值,是一个空的map实例
if (typeof objComplex !== 'object') {
// 不是对象和数组间接返回
return objComplex
}
const objClone = Array.isArray(objComplex) ? [] : {}if (mapx.get(objComplex)) {
// 存在被克隆的对象,间接返回
return mapx.get(objComplex)
}
// 不存在,就增加键值对,将被克隆对象作为key,克隆的对象作为value
mapx.set(objComplex, objClone)for(let key in objComplex) {
objClone[key] = deepClone(objComplex[key], mapx)
// 留神:mapx要传入做判断
// 不论objComplex[key]是什么类型,都调用deepClone(),因为在deepClone()中会判断
}
return objClone
}
const res = deepClone(objComplex) // 这样就不会内存溢出了
console.log(res, 'res')
Reflect 解决Symbol数据类型复制 - Reflect.ownKeys()循环递归(3)
- <font color=red>要求: 能够拷贝对象和数组,并解决循环援用问题,并解决Symbol数据类型</font>
- 解决 Symbol 数据类型
- Symbol不能用 new 去调用,参数能够是数组
Reflect.ownKeys(obj)返回参数对象的所有本身属性,包含symbol数据类型的属性
- 参数如果不是obj,则会抛出typeError
毛病:Reflect不能取到原型链上的属性和办法
`
用 Reflect 解决 Symbol类型数据的复制var objComplex = {address: {city: 'chongqing',town: 'jiazhou',},score: [100, 200],}objComplex.circular = objComplexobjComplex[Symbol()] = 'symbol'function deepClone(objComplex, mapx = new Map()) {if (typeof objComplex !== 'object') {return objComplex}const objClone = Array.isArray(objComplex) ? [] : {}if (mapx.get(objComplex)) {return mapx.get(objComplex)}mapx.set(objComplex, objClone)// for(let key in objComplex) {// objClone[key] = deepClone(objComplex[key], mapx)// }Reflect.ownKeys(Array.isArray(objComplex) ? [...objComplex] : { ...objComplex }).forEach(key => {// Reflect.ownKeys(obj)返回对象参数的所有属性,包含symbol类型的属性objClone[key] = deepClone(objComplex[key], mapx)})return objClone}const res = deepClone(objComplex)console.log(res, 'res')`
2021/06/20 优化
下面的ownKeys参数中是不须要判断是不是数组的,因为ownKeys能够遍历数组和对象
<script> // deep clone const obj = { name: "woow_wu7", address: { city: "chongqing", districe: "yubei", town: "jiazhou", detail: ["chongqign", "yubei", "jiazhou"], }, arr: [1, 2], [Symbol("unique")]: "unique", }; obj.circle = obj; const deepClone = (param, map = new Map()) => { let typeParams = Object.prototype.toString.call(param); if (typeof param !== "object" && typeof param !== "function") { return param; } if (map.get(param)) { return map.get(param); } let resObj = Array.isArray(param) ? [] : {}; map.set(param, resObj); // for (let key in param) { // resObj[key] = deepClone(param[key], map); // } Reflect.ownKeys(param).forEach((key) => { // ownKeys参数中,不必做判断是不是数组了 resObj[key] = deepClone(param[key], map); }); return resObj; }; const newObj = deepClone(obj); newObj.name = "222"; console.log(`newObj`, newObj); console.log(`obj`, obj); </script>
结构化克隆算法解决其余对象的拷贝 (4)
- <font color=red>要求: 能够拷贝对象和数组,并解决循环援用问题(Map缓存),并解决Symbol数据类型(Reflect.ownKeys()),解决其余对象的拷贝(结构化克隆),构造函数生成实例的原型对象的拷贝</font>
- 比方:Date,Regexp,构造函数生成的实例的原型对象的属性拷贝
毛病:不能解决Error,不能解决Function,不能解决DOM节点 `
function Message() {this.sex = 'man'}Message.prototype.age = 1000var objComplex = {address: { city: 'chongqing', town: 'jiazhou',},score: [100, 200],[Symbol()]: 'symbol',date: new Date(),reg: new RegExp(),fn: function () { },err: new Error(),message: new Message()}objComplex.circle = objComplexfunction deepClone(objComplex, mapx = new Map()) {if (typeof objComplex !== 'object') { return objComplex}let objClone = Array.isArray(objComplex) ? [] : {}if (mapx.get(objComplex)) { return mapx.get(objComplex)}mapx.set(objComplex, objClone)// for(let key in objComplex) {// objClone[key] = deepClone(objComplex[key], mapx)// }// Reflect.ownKeys(Array.isArray(parameter) ? [...parameter] : { ...parameter }).forEach(key => {// objClone[key] = deepClone(parameter[key], mapx)// })switch (objComplex.constructor) { // 传入的参数对象的构造函数 case Date: case RegExp: case Message: // 自定义的构造函数 objClone = new objComplex.constructor(objComplex) break // 如果是Date,RegExp,Message构造函数的申请 // 就别离用这些构造函数生成实例,而后再赋值给拷贝对象 default: Reflect.ownKeys(Array.isArray(objComplex) ? [...objComplex] : {...objComplex}).forEach(key => { objClone[key] = deepClone(objComplex[key], mapx) })}return objClone}const res = deepClone(objComplex)console.log(res, 'res')console.log(res.message.age, '克隆的对象')console.log(objComplex.message.age, '原对象')`
2021/07/17温习
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <script> // deepClone // 1. Map 解决循环援用 => 缓存 // 2. Reflect 解决Symbol()数据类型 => Reflect.ownKeys() // 3. 结构化克隆解决非凡对象的拷贝 => Date RegExp // - 尽管结构化克隆解决了 ( Date ) ( RegExp ) 对象的拷贝问题 // - 然而结构化克隆依然没有解决 ( Error ) ( function ) ( DOM节点 ) 的拷贝问题 const obj = { num: 1, str: "str", boo: true, und: undefined, [Symbol()]: Symbol(), arr: [1, 2, 3], obj: { name: "woow_wu7" }, date: new Date(), regexp: new RegExp(), }; obj.circle = obj; const deepClone = (params, map = new Map()) => { if (typeof params !== "object") { return params; } let resObj = Array.isArray(params) ? [] : {}; if (map.get(params)) { return map.get(params); } map.set(params, resObj); // for (let key in params) { // if (params.hasOwnProperty(key)) { // resObj[key] = deepClone(params[key], map); // } // } // Reflect.ownKeys(params).forEach(key => { // resObj[key] = deepClone(params[key], map) // }) switch (params.constructor) { case Date: case RegExp: resObj = new params.constructor(params); break; default: Reflect.ownKeys(params).forEach((key) => { resObj[key] = deepClone(params[key], map); }); break; } return resObj; }; const res = deepClone(obj); console.log(`res`, res); console.log(`obj`, obj); </script> </body></html>
根本:https://juejin.im/post/684490...
深刻:https://juejin.im/post/684490...
知乎(lodash deepClone源码剖析):https://zhuanlan.zhihu.com/p/...
stack,heap:https://segmentfault.com/a/11...
https://juejin.im/post/684490...
我的简书 https://www.jianshu.com/p/a23...