Javascript高级—深浅拷贝
浅拷贝
浅拷贝是拷贝第一层的拷贝
使用Object.assign
解决这个问题。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
通过展开运算符 ...
来实现浅拷贝
let a = {
age: 1
}
let b = {...a};
a.age = 2;
console.log(b.age) // 1
深拷贝
简单的做法:JSON.parse(JSON.stringfy(obj))
但是该方法也是有局限性的:
- 会忽略
undefined
- 会忽略
symbol
- 会忽略函数
- 不能解决循环引用的对象 (会抱错)
如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel
自封装深拷贝
思路:
- 使用for-in遍历对象
- 因为for-in会遍历原型链上的属性,所以需要判断属性是否在原型链上,不是原型链才拷贝
- 判断属性值类型是原始类型和引用类型
- 原始类型直接赋值(注意null)
- 引用类型判断是对象还是数组,创建对应的空对象或空数组,递归调用函数,将值赋值进去
/**
* 深度克隆
* @param origin 被拷贝的原对象
* @param target 拷贝出来的对象
* @return 拷贝出来的对象
*/
function deepClone(origin, target) {
target = target || {};
for(let prop in origin) { //使用 for-in
if(origin.hasOwnProperty(prop)) { //是原型链上的
if(typeof(origin[prop]) === 'object' && origin[prop] ) { //是对象
// 先判断是不是数组
if(origin[prop] instanceof Array) {
target[prop] = [];
deepClone(origin[prop], target[prop]);
}
target[prop] = {};
deepClone(origin[prop], target[prop]);
}
else {
target[prop] = origin[prop];
}
}
}
return target;
}
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
jQuery的extend方法实现
// 实现jQuery的extend函数
// v1:
function extend() {
let i = 1;
let len = arguments.length;
let target = arguments[0];
let options, copy;
// 遍历第一个以后的所有参数
for (; i < len; i++) {
options = arguments[i];
if (options != null) {
for (let name in options) {
copy = options[name];
if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
}
// TODO: 当前的这个版本不能深拷贝,只能覆盖(如果是对象属性的话)
// v2: 实现一个深度拷贝
function extend(){
// 1. 默认是不进行深拷贝的
let deep = false;
let len = arguments.length;
let i = 1;
// 第一个参数如果不是一个bool的话,target默认就是第一个参数
let target = arguments[0] || {};
let options, src, copy;
if (typeof target === 'boolean') {
deep = target;
target = arguments[i] || {};
i++;
}
// 如果target不是对象的话,无法复制,设置为{}
if (typeof target !== 'object') {
target = {};
}
// 开始深拷贝
for (; i < len; i++) {
options = arguments[i];
if (options != null) {
for (let name in options) {
// 开始准备源数据和目标数据
src = target[name];
copy = options[name];
// 如果是对象的话,就深拷贝
if (deep && copy && typeof copy === 'object') {
target[name] = extend(deep, src, copy);
}
else if (copy !== undefined) {
// 普通数据类型的话
target[name] = copy;
}
}
}
}
return target;
}
// isPlainObject 函数来自于 [JavaScript专题之类型判断(下) ](https://github.com/mqyqingfeng/Blog/issues/30)
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
function isPlainObject(obj) {
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") {
return false;
}
proto = Object.getPrototypeOf(obj);
if (!proto) {
return true;
}
Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object);
}
function extend() {
// 默认不进行深拷贝
var deep = false;
var name, options, src, copy, clone, copyIsArray;
var length = arguments.length;
// 记录要复制的对象的下标
var i = 1;
// 第一个参数不传布尔值的情况下,target 默认是第一个参数
var target = arguments[0] || {};
// 如果第一个参数是布尔值,第二个参数是 target
if (typeof target == 'boolean') {
deep = target;
target = arguments[i] || {};
i++;
}
// 如果target不是对象,我们是无法进行复制的,所以设为 {}
if (typeof target !== "object" && !isFunction(target)) {
target = {};
}
// 循环遍历要复制的对象们
for (; i < length; i++) {
// 获取当前对象
options = arguments[i];
// 要求不能为空 避免 extend(a,,b) 这种情况
if (options != null) {
for (name in options) {
// 目标属性值
src = target[name];
// 要复制的对象的属性值
copy = options[name];
// 解决循环引用
if (target === copy) {
continue;
}
// 要递归的对象必须是 plainObject 或者数组
if (deep && copy && (isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
// 要复制的对象属性值类型需要与目标属性值相同
if (copyIsArray) {
copyIsArray = false;
clone = src && Array.isArray(src) ? src : [];
} else {
clone = src && isPlainObject(src) ? src : {};
}
target[name] = extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
};
let obj1 = {
a: 1,
b: { b1: 1, b2: 2 }
};
let obj2 = {
b: { b1: 3, b3: 4 , b4: 5},
c: 3
};
let obj3 = {
d: 4
}
console.log(extend(obj1, obj2, obj3));
<<<<<<< HEAD
console.log('---------------------');
var a = extend(true, [4, 5, 6, 7, 8, 9], [1, 2, 3]);
console.log(a) // ???
console.log('---------------------');
var obj11 = {
value: {
3: 1
}
}
var obj22 = {
value: [5, 6, 7],
}
var b = extend(true, obj11, obj22) // ???
var c = extend(true, obj22, obj11) // ???
console.log(b, c)
// 补充:深度克隆
function deepClone(obj) {
let res = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
res[key] = deepClone(obj[key]);
}
else {
res[key] = obj[key];
}
}
}
return res;
}