这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战”
- JavaScript中的对象是可变的,因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象会影响到原始对象。为了解决这个问题,一般就是使用浅拷贝或者深拷贝来避免被修改,但是这样会造成cpu和内存的浪费
传统处理复杂对象
浅拷贝
简单的复制拷贝,其实地址不变
深拷贝
JSON.parse
利用jaon.pase将对象转换为其JSON字符串形式,然后将其解析回对象
const deepClone(obj)=>JSON.parse(JSON.stringify(obj))
⚠️注意:不能处理循环对象,子父节点互相引用的情况,无法处理对象中有function,正则等情况
messageChannel
- messageChannel接口是信道通信api的一个接口,允许我们创建一个新的信道并通过信道的两个MessageProt属性传递数据\
- 类似的还有Histor API,Notification API.都是用了结构化克隆算法实现传输值的
function structuralClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel();
port2.onmessage = function (e) {
resolve(e.data)
console.log("MessageEvent输出:", e)
}
port1.postMessage(obj);
});
}
const originObj = { name: "一缕清风" }
structuralClone(originObj).then((data) => {
console.log('clone', data) //clone { name: '一缕清风' }
data.age = 18
})
console.log(originObj) //{ name: '一缕清风' }
originObj.name = "Kenguba"
console.log('change origin data-->', originObj) //change origin data--> { name: 'Kenguba' }
/*
{ name: '一缕清风' }
change origin data--> { name: 'Kenguba' }
MessageEvent输出: MessageEvent {
type: 'message',
defaultPrevented: false,
cancelable: false,
timeStamp: 46.602142999880016
}
clone { name: '一缕清风' }
*/
什么是 immutable Data
- immutable(不可改变的)
-
immutable是一种持久化数据。一旦创建,就不能更改的数据,对immutable对象的任何修改或删除添加都会返回一个新的immutable对象,但是原数据不会改变
- 实现原理就是持久化数据结构,在使用旧数据创建新数据的时候,会保证旧数据同时可用且不变,同时为了避免深度复制复制所有节点的带来的性能损耗,immutable使用了结构共享,即如果对象树种的一个节点发生变化,只修改这个节点和受他影响的父节点,其他节点则共享。
为什么要使用 immutable
-
如果需要频繁的操作一个复杂对象,每次完全拷贝一次的效率太低了。大部分场景下都只是更新了对象的一两个字段,其他字段都不变,对于这些不变的字段的拷贝都是多于的
-
核心思路 创建持久化的数据结构,在操作对象的时候值clone变化的节点和其祖先节点,其他的不变,实现结构贡献
总结 immutable的不可变性让纯函数更强大,每次都返回新的immutable的特性让程序员可以对其进行链式操作,用起来更方便
类似 immutable 的库
- Immutable.js
- Immer.js
- 自己写的全部深度克隆代码
function type(obj) {
var type = Object.prototype.toString.call(obj)
var map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
if (obj instanceof Element) {
return 'element'
}
return map[type]
}
function deepClone(item) {
if (!item) return item
var types = ['number', 'string', 'boolean'], result
if (types.includes(type(item))) {
result = item
}
if (result === undefined) {
if (type(item) === 'array') {
console.log('arr')
result = []
item.forEach((child, index, array) => {
result[index] = deepClone(child)
})
} else if (type(item) === 'object') {
if (item.nodeType && typeof item.cloneNode == 'function') {
retult = item.cloneNode(true)
//真实的复制函数并没有走到这里
} else if (!item.prototype) {
// 检查是否是可循环的
if (item instanceof Date) {
result = new Date(item)
} else {
result = {}
for (var i in item) {
result[i] = deepClone(item[i])
}
}
} else {
console.log('发现了其他类型')
}
} else {
result = item
}
} else {
console.log('123', result)
}
return result
}
let arr = [1, 2, 3]
var arr2 = deepClone(arr)
console.log(arr2, 'arr2')
arr.push(1)
console.log(arr2, 'arr2-after')