拷贝
赋值(Copy)
赋值是将某一数值或者对象赋给某个变量的过程,分为2部分
- 基本数据类型:赋值,复制之后两个变量互不影响。
- 引用数据类型:赋址,两个变量之间具有相同的引用,指向同一个对象。相互之间有影响。
对于基本类型进行赋值操作,两个变量互不影响。
var a='name';
var b=a;
a = 'change'
// a change ; b name;
对于引用类型进行赋址操作,两个变量指向同一个对象,改变a对象之后,会影响b对象。
通常在开发中并不希望改变变量a之后会影响到变量b。所以需要进行深拷贝和浅拷贝。
浅拷贝(Shallow Copy)
定义
浅拷贝,创建一个新的对象,这个对象有着原始对象属性值的的一份精确拷贝。如果属性值是基本类型,拷贝的就是基本类型结果。如果属性是引用类型,拷贝的就是内存地址。
简单的说:浅拷贝只解决了第一层的问题。拷贝第一层的基本类型值,以及第一层的引用类型地址。
场景
- Object.assign()
方法用于将所有可枚举属性值从一个或多个源对象复制到目标对象,并返回目标对象
- 展开语法Spread
- Array.prototype.slice()
方法返回一个新的数组对象,这一个对象室友begin和and(不包括end),决定原来数组的浅拷贝,原始数组不会改变。
深拷贝
定义
深拷贝会拷贝所有属性,并拷贝属性指向的动态分配额内存,当对象和他所引用的对象一起拷贝是即发生深拷贝。深拷贝相对于浅拷贝速度慢,开销大。拷贝前后两个对象互不影响。
使用场景
JSON.parse(JSON.stringfy(obj))
使用此方法的几个问题。
- 会忽略undefined,symbol,函数
- 不能解决循环引用的对象,会报错。
- 不能正确处理new Date()
- 不能处理正则。
let obj = {
a: 1,
b: {
c: 2,
d: 3
}
}
obj.a = obj.b;
obj.b.c = obj.a;
let b = JSON.parse(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSON
new Date();
// Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)
JSON.stringify(new Date());
// ""2018-12-24T02:59:25.776Z""
JSON.parse(JSON.stringify(new Date()));
// "2018-12-24T02:59:41.523Z"
let obj = {
name: "muyiy",
a: /'123'/
}
console.log(obj);
// {name: "muyiy", a: /'123'/}
let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "muyiy", a: {}}
常用的有jQuery.exend() lodash.cloneDeep()
lodash实现
位掩码
// 木易杨
// 主线代码
const CLONE_DEEP_FLAG = 1 // 1 即 0001,深拷贝标志位
const CLONE_FLAT_FLAG = 2 // 2 即 0010,拷贝原型链标志位,
const CLONE_SYMBOLS_FLAG = 4 // 4 即 0100,拷贝 Symbols 标志位
位掩码用于处理同时存在多个布尔选项的情况,其中掩码中的每个选项的值都等于 2 的幂。相比直接使用变量来说,优点是可以节省内存(1/32)
// 木易杨
// 主线代码
// cloneDeep.js 添加标志位,1 | 4 即 0001 | 0100 即 0101 即 5
CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG
// baseClone.js 取出标志位
let result // 初始化返回结果,后续代码需要,和位掩码无关
const isDeep = bitmask & CLONE_DEEP_FLAG // 5 & 1 即 1 即 true
const isFlat = bitmask & CLONE_FLAT_FLAG // 5 & 2 即 0 即 false
const isFull = bitmask & CLONE_SYMBOLS_FLAG // 5 & 4 即 4 即 true
常用的基本操作如下
a|b
:添加标志位a和b- mask & a取出标志位a
- mask & ~a清楚标志位a
- mask ^ a:取出与a的不同部分
// 木易杨
var FLAG_A = 1; // 0001
var FLAG_B = 4; // 0100
// 添加标志位 a 和 b => a | b
var mask = FLAG_A | FLAG_B => 0101 => 5
// 取出标志位 a => mask & a
mask & FLAG_A => 0001 => 1
mask & FLAG_B => 0100 => 4
// 清除标记位 a => mask & ~a
mask & ~FLAG_A => 0100 => 4
// 取出与 a 的不同部分 => mask ^ a
mask ^ FLAG_A => 0100 => 4
mask ^ FLAG_B => 0001 => 1
FLAG_A ^ FLAG_B => 0101 => 5