深浅拷贝的理解
数据类型
- 基本数据类型 - 直接储存在==栈(stack)==中的数据
- String
- Number
- Boolean
- Null
- Undefined
- Symbol(new in ES6)
- 引用数据类型 - 在栈中存储指针,真实数据放在堆内存中,指针指向堆中数据的其实地址
统称为Object类型(细分为: Object、Array、Date、RegExp、Function等等)
赋值
赋的是该对象在栈中的地址,而不是堆中的数据
浅拷贝
-当数据结构只有一层时是深拷贝—仅改变数据第一层可以把浅拷贝当成深拷贝使用
两者的栈中地址不同,但指针指向同一个堆内存
浅拷贝只能解决第一层结构的问题,深层次还是会享有相同的引用
0. 引用赋值
1. Object.assign
2. 扩展运算符
3.concat和slice等 — 仅适用于一位数组
4.filter和map等等
let arr = [1,2,3]
let a1 = [...arr]//1层可以看做是深复制
let arr = [{name:'aa'}]
let a2 = [...arr]//修改name会影响//第二层及以后没有达到深复制
深拷贝
在堆内存中重新开辟一块空间进行存放
1. JSON.parse(JSON.stringify())
- 会忽略undefined(丢失这个结构)
- 不能序列化函数(对函数无法转化)
- 不能解决循环引用的对象
let obj = {//循环引用
a: 1,
b: {
c: 2,
d: 3,
},
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
2.lodash深拷贝函数
上述先转字符串在转回来的方法是内置函数中处理深拷贝性能最开的,当然有缺陷,因此缺陷的3中情况可使用lodash
//该函数库也有提供_.cloneDeep用来做 Deep Copy
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
3.递归
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝
//定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result, targetType = checkedType(target)
if (targetType === 'object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else { //获取到value值是基本的数据类型或者是函数。
result[i] = value;
}
}
return result
}
或者
function deepClone(obj){
//判断参数是不是一个对象
let objClone = obj instanceof Object?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
var a ={
x:1,
y:2
};
b=deepClone(a);
a.x=3
console.log(a);
console.log(b);
4.JQuery库的extend方法-原理是递归
- 浅拷贝(只复制一份原始对象的引用)
var newObject = $.extend({}, oldObject);
- 深拷贝(对原始对象属性所引用的对象进行进行递归拷贝)
var newObject = $.extend(true, {}, oldObject);
5.immutable - ES6的新数据结构
mobx笔记后面
博客
https://www.cnblogs.com/dengyao-blogs/p/11466598.html
https://blog.csdn.net/weixin_41082623/article/details/88084831
二、immutable(只要基于node环境就可以用)
immutable(facebook公司)-和redux联合使用提高性能-解决深拷贝的性能问题
redux中的深复制特别耗费性能。而immutable只改变变化部分,其他结构保留(下面动图)
保持数据不变,如果数据改变,自动生成一个新对象
回调原理
//回调
function fun(callback){
setTimeout(()=>{
//直接return是undefined,是不等待异步的
callback(555)
},3000);
}
//异步结束后执行回调-参数就是callback(555)传过来的555
fun((data)=>{
console.log(data)
})
比较(3种)
import {Map,is,List} from 'immutable'
var m1 =Map({"a":2});
var m2 = m1.set("a",666);
var m3 = m2.set("a",2)
console.log(m1==m3) //false(比较的是地址)
console.log(m1.equals(m3)); //true(比较内容是否相等)-原生方法
console.log(is(m1,m3))//true(比较内容是否相等)-immutable的方法
Map - 对象
import {Map} from 'immutable'
const m1=Map('a',111)//不会被改变
let m2=m1.set('a',222)
console.log(m1.get('a'))//打印111
console.log(m2.get('a'))//打印222
//说明m1是不可修改,一旦修改了,就会产生一个新对象
//原理是深拷贝,但性能高,使用了结构共享技术
set get 单层
setIn getIn 多层嵌套
update 回调
// var obj1 =Map({"a":{"b":33}});
// var obj2 = obj1.set("a",{"b":{"c":99}});
// var obj3 =obj2.setIn(["a","b","c"],100);
// console.log(obj3.get("a").b.c);
// console.log(obj3.getIn(["a","b","c"]))
//上一个的值加1
var m3 = m2.set("a",m2.get("a")+1)
// update的回调写法
var m4 = m3.update("a",(value)=>value+1)
console.log(m4.get("a"));
List - 数组
import {Map,is,List} from 'immutable'
const list1=List([1,2,3,4,5])
var list2 = list1.set(0,222);
var list3 = list2.update(0,(value)=>666)
console.log(list3.get("0"))
**immutable和reducer两者联合使用
**主要是在reducer中深拷贝做副本时使用
**在组件中取数据也要使用immutable的方法(get、getIn)
1.安装
cnpm i immutable redux-immutable -S
2. combineReducers的引入变化了
import {combineReducers} from ‘redux-immutable’
存
set setIn
取
get getIn
import {Map} from 'immutable'
import {combineReducers} from 'redux-immutable'
const initialState = Map({
n:1
})
var reducer =(state = initialState, action) => {
switch (action.type) {
case 'INC':
return state.set("n",state.get("n")+1)
//如果是复杂对象,这个地方性能比较好
default:
return state
}
}
export default combineReducers({
num:reducer
})
//在组件中取数据也要使用immutable的方法
import { connect } from 'react-redux'
const mapStateToProps = (state, ownProps) => {
return {
searchList: state.getIn(['blog', 'searchList']).filter(item => item.isPub)
}
}
const mapDispatchToProps = {
// promise中间件,没有dispatch
getAllArticle: actionCreator.getAllArticle,
setSearch: (value) => {
return {
type: SEARCHBLOG,
payload: value
}
},
setBreadList: actionCreator.setBreadList
}
// 使用直接从props中使用*************
/* this.props.getAllArticle() */
@connect(mapStateToProps, mapDispatchToProps)
相关链接
https://blog.csdn.net/canduecho/article/details/79390207
https://cn.mobx.js.org/intro/concepts.html
https://blog.csdn.net/sinat_17775997/article/details/73603797
基本数据类型 如 number string 都是 不可变对象
引用数据类型 如 Array Object 都是 可变对象
引用类型的数据,优点在于频繁的操作数据都是在原对象的基础上修改,不会创建新对象,从而可以有效的利用内存,不会浪费内存,这种特性称为mutable(可变)
但恰恰它的优点也是它的缺点,太过于灵活多变在复杂数据的场景下也造成了它的不可控性,假设一个对象在多处用到,在某一处不小心修改了数据,
其他地方很难预见到数据是如何改变的,针对这种问题的解决方法,一般就像刚才的例子,会想复制一个新对象,再在新对象上做修改,
这无疑会造成更多的性能问题以及内存浪费
为了解决这种问题,出现了immutable对象,每次修改immutable对象都会创建一个新的不可变对象,而老的对象不会改变
immutable.js (想帮助你像操作不可变数据一样操作可变数据(对象数组))
安装下载
cnpm i immutable redux-immutable -S
常用api介绍
immutable.js提供了十余种不可变的类型(List,Map,Set,Seq,Collection,Range等)
//Map() 原生object转Map对象 (只会转换第一层,注意和fromJS区别)
immutable.Map({name:’danny’, age:18,a:{b:1000}})
//List() 原生array转List对象 (只会转换第一层,注意和fromJS区别)
immutable.List([1,2,3,4,5])
//fromJS() 原生js转immutable对象 (深度转换,会将内部嵌套的对象和数组全部转成immutable)
immutable.fromJS([1,2,3,4,5]) //将原生array —> List
immutable.fromJS({name:’danny’, age:18}) //将原生object —> Map
//toJS() immutable对象转原生js (深度转换,会将内部嵌套的Map和List全部转换成原生js)
immutableData.toJS();
//查看List或者map大小
immutableData.size 或者 immutableData.count()
// is() 判断两个immutable对象是否相等
immutable.is(imA, imB);
//merge() 对象合并
var imA = immutable.fromJS({a:1,b:2});
var imA = immutable.fromJS({c:3});
var imC = imA.merge(imB);
console.log(imC.toJS()) //{a:1,b:2,c:3}
//增删改查(所有操作都会返回新的值,不会修改原来值)
var immutableData = immutable.fromJS({
a:1,
b:2,
c:{
d:3,
f:{
g:100
}
}
});
var data1 = immutableData.get(‘a’) // data1 = 1
var data2 = immutableData.getIn([‘c’, ‘d’]) // data2 = 3 getIn用于深层结构访问
var data3 = immutableData.set(‘a’ , 2); // data3中的 a = 2
var data4 = immutableData.setIn([‘c’, ‘d’], 4); //data4中的 d = 4
var data5 = immutableData.update(‘a’,function(x){return x+4}) //data5中的 a = 5
var data6 = immutableData.updateIn([‘c’, ‘d’],function(x){return x+4}) //data6中的 d = 7
var data7 = immutableData.delete(‘a’) //data7中的 a 不存在
var data8 = immutableData.deleteIn([‘c’, ‘d’]) //data8中的 d 不存在
优点
降低mutable带来的复杂度 (数组和对象)
节省内存
拥抱函数式编程
缺点
需要重新学习api
- react
就是数据驱动的框架
jQuery 真真切切的操作DOM
虚拟DOM object 用来描述 存储在 内存里面记录DOM标签的对象树 (key 对比 )
操作数据 ——- 操作虚拟DOM —— 真实DOM
配套的数据管理方案和框架 (实现组件的数据交换和数据缓存)
vuex (state action mutations component ==> store)
redux (state action reducers component ==> store)
a. 组件 components 取到 state 的值
b. 发送 action store.dispatch
c. 进入 reducers 根据 type 修改对应的数据 state
d. store 会订阅 state, 当state 改变就会 重新刷新组件视图
单向数据流 (管理数据)
state ——> reducers ——> store ——-> components
a. 把初始化的 state 存储在 reducers
b. 把reducers 挂载到 store (store.getState() )
c. 组件 components 获取到 store 里面的 state 数据
d. 修改state 组件发送 store.dispatch 发送 action
flux
mobx
react-redux (完全遵循 redux 优化 将redux 里面的组件 拆分为 容器组件和 UI 组件 )
1.redux
2.mobx