javascript
深拷贝的缺点_JavaScript 深拷贝
一、JSON 序列化和反序列化
對于一般(狹義)的對象,最簡單快速的方法就是使用 JSON 的序列化和反序列化:
const o1 = {name: 'xxx',age: 18,child: {name: 'zzz'} } const o2 = JSON.parse(JSON.stringify(o1))o1 === o2 // false o1.name === o2.name // true o1.child === o2.child // false o1.child.name === o2.child.name // true缺點:僅支持 JSON 所支持的數據類型和結構。
例如:
對于更復雜的對象,就無法使用上面的辦法,需要使用遞歸來進行深拷貝。
二、搭建測試環境
安裝 mocha、chai 和 sinon-chai:
yarn init -y yarn add mocha chai sinon-chai --devpackage.json 添加運行測試命令:
"scripts": {"test": "mocha test/**/*.js" }導出 deepClone:
// src/index.jsfunction deepClone() {}module.exports = deepClone編寫測試用例:
// test/index.jsconst chai = require('chai') const sinonChai = require('sinon-chai') const deepClone = require('../src/index')chai.use(sinonChai) const assert = chai.assertdescribe('測試深拷貝', () => {it('成功引入深拷貝', () => {assert.isFunction(deepClone)}) })運行測試:
yarn test三、遞歸深拷貝
1、拷貝基本類型
添加測試用例:
it('拷貝基本類型', () => {const s1 = 'xxx'const n1 = 123const u1 = undefinedconst b1 = trueconst empty1 = nullconst s2 = deepClone(s1)const n2 = deepClone(n1)const u2 = deepClone(u1)const b2 = deepClone(b1)const empty2 = deepClone(empty1)assert(s2 === s1)assert(n2 === n1)assert(u2 === u1)assert(b2 === b1)assert(empty2 === empty1) })因為基本類型不會存在深淺拷貝的問題,所以直接返回它的值就可以了。
function deepClone(resource) {return resource }2、拷貝一般(狹義)對象
添加測試用例:
it('拷貝一般(狹義)對象', () => {const o1 = { name: 'xxx', child: { name: 'zzz' } }const o2 = deepClone(o1)assert(o1 !== o2)assert(o1.name === o2.name)assert(o1.child !== o2.child)assert(o1.child.name === o2.child.name) })使用遞歸來進行深拷貝:
function deepClone(resource) {if (resource instanceof Object) {const result = new Object()for(let key in resource) {result[key] = deepClone(resource[key])}return result}return resource }3、拷貝數組
添加測試用例:
it('拷貝數組', () => {const a1 = [[12, 23], [34, 45], [56, 67]]const a2 = deepClone(a1)assert(a1 !== a2)assert(a1[0] !== a2[0])assert(a1[1] !== a2[1])assert(a1[2] !== a2[2])assert.deepEqual(a1, a2) })還是使用遞歸來進行深拷貝,不過在最初生成的時候需要 new Array(),而不是 new Object()。
function deepClone(resource) {if (resource instanceof Object) {if (resource instanceof Array) {const result = new Array()for (let key in resource) {result[key] = deepClone(resource[key])}return result} else {const result = new Object()for (let key in resource) {result[key] = deepClone(resource[key])}return result}}return resource }4、拷貝函數
添加測試用例:
it('拷貝函數', () => {const f1 = (a, b) => {return a + b}f1.xxx = { yyy: { zzz: 'aaa' } }const f2 = deepClone(f1)assert(f1 !== f2)assert(f1(1, 2) === f2(1, 2))assert(f1.xxx !== f2.xxx)assert(f1.xxx.yyy !== f2.xxx.yyy)assert(f1.xxx.yyy.zzz === f2.xxx.yyy.zzz) })通過直接調用源函數的方法,來達到類似深拷貝的目的。
...else if (resource instanceof Function) {const result = function () {return resource.apply(this, arguments)}for (let key in resource) {result[key] = deepClone(resource[key])}return result }...5、拷貝正則表達式
添加測試用例:
it('拷貝正則表達式', () => {const reg1 = /hid+/gireg1.xxx = { yyy: { zzz: 'aaa' } }const reg2 = deepClone(reg1)assert(reg1 !== reg2)assert(reg1.source === reg2.source)assert(reg1.flags === reg2.flags)assert(reg1.xxx !== reg2.xxx)assert(reg1.xxx.yyy !== reg2.xxx.yyy)assert(reg1.xxx.yyy.zzz === reg2.xxx.yyy.zzz) })正則表達式有兩個非常重要的屬性:
- source 返回主體內容
- flags 返回標記
可以根據這兩個屬性,new RegExp() 一個新的正則表達式。
...else if (resource instanceof RegExp) {const result = new RegExp(resource.source, resource.flags)for (let key in resource) {result[key] = deepClone(resource[key])}return result }...6、拷貝日期
添加測試用例:
it('拷貝日期', () => {const d1 = new Date()d1.xxx = { yyy: { zzz: 'aaa' } }const d2 = deepClone(d1)assert(d1 !== d2)assert(d1.getTime() === d2.getTime())assert(d1.xxx !== d2.xxx)assert(d1.xxx.yyy !== d2.xxx.yyy)assert(d1.xxx.yyy.zzz === d2.xxx.yyy.zzz) })可以使用 new Date() 和源日期對象來初始化一個新的日期對象。
...else if (resource instanceof Date) {const result = new Date(resource)for (let key in resource) {result[key] = deepClone(resource[key])}return result }...四、跳過原型屬性
使用 for ... in 來遍歷對象的 key 的時候,會默認遍歷原型上的屬性:
const obj = Object.create({ name: 'xxx' }) // name 會在 obj 的 __proto__ 中 obj.age = 18 for(let key in obj) {console.log(key) }// 'age' // 'name'一般來說,不拷貝原型上的屬性。
添加測試用例:
it('跳過原型屬性', () => {const o1 = Object.create({ name: 'xxx' })o1.xxx = { yyy: { zzz: 'aaa' } }const o2 = deepClone(o1)assert(o1 !== o2)assert.isTrue('name' in o1)assert.isFalse('name' in o2)assert(o1.xxx !== o2.xxx)assert(o1.xxx.yyy !== o2.xxx.yyy)assert(o1.xxx.yyy.zzz === o2.xxx.yyy.zzz) })所以在遞歸的時候,需要判斷 key 是不是它自身的屬性。
for (let key in resource) {if(resource.hasOwnProperty(key)) {result[key] = deepClone(resource[key])} }五、代碼優化
整合上面的代碼,進行優化:
// src/index.jsfunction deepClone(resource) {if (resource instanceof Object) {let resultif (resource instanceof Array) {result = new Array()} else if (resource instanceof Function) {result = function () {return resource.apply(this, arguments)}} else if (resource instanceof RegExp) {result = new RegExp(resource.source, resource.flags)} else if (resource instanceof Date) {result = new Date(resource)} else {result = new Object()}for (let key in resource) {if(resource.hasOwnProperty(key)) {result[key] = deepClone(resource[key])}}return result}return resource }module.exports = deepClone可以直觀的看出,對于不同的復雜對象,只需要對其進行不同的特殊處理即可。
六、環處理
遞歸拷貝還有一個問題,就是對于環的處理。
例如 window.self 就是一個環,它指向 window 自己。當遞歸拷貝遇到環,就會陷入無限的循環。
編寫測試用例:
it('環拷貝', () => {const o1 = { name: 'xxx', child: { name: 'zzz' } }o1.self = o1const o2 = deepClone(o1)assert(o1 !== o2)assert(o1.name === o2.name)assert(o1.child !== o2.child)assert(o1.child.name === o2.child.name)assert(o1.self !== o2.self) })處理的思路:對已處理過的對象進行緩存,在遞歸的同時,檢查是否有緩存,如果發現有緩存,則代表有環,就直接返回該對象,達到環拷貝的目的。
// src/index.jsconst cacheStack = []function deepClone(resource) {if (resource instanceof Object) {const cache = findCache(resource)if (cache) {return cache} else {let resultif (resource instanceof Array) {result = new Array()} else if (resource instanceof Function) {result = function () {return resource.apply(this, arguments)}} else if (resource instanceof RegExp) {result = new RegExp(resource.source, resource.flags)} else if (resource instanceof Date) {result = new Date(resource)} else {result = new Object()}cacheStack.push([resource, result])for (let key in resource) {if (resource.hasOwnProperty(key)) {result[key] = deepClone(resource[key])}}return result}}return resource }function findCache(resource) {for (let i = 0; i < cacheStack.length; i++) {if (cacheStack[i][0] === resource) {return cacheStack[i][1]}}return null }module.exports = deepClone最終完整代碼:
JinChengJoker/deep-clone?github.com總結
以上是生活随笔為你收集整理的深拷贝的缺点_JavaScript 深拷贝的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python系统字体_Python ma
- 下一篇: redis一般缓存什么样数据_Redis