《JS核心原理精讲》笔记(二)深浅拷贝

浅拷贝

自己创建一个新的对象,来接受你要重新复制活音乐的对象值。如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;但如果属性是引用数据类型,复制的就是内存中的地址,如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象。

方法一:object.assign

object.assign是ES6中object的一个方法,该方法可以用于JS对象的合并等多个用途,其中一个用途就是可以进行浅拷贝
在这里插入图片描述

  • 它不会拷贝对象的继承属性
  • 它不会拷贝对象的不可枚举属性
  • 可以拷贝Symbol类型的属性

示例代码:

let target = {};
let source = { a: { b: 1 } };
Object.assign(target, source);

console.log(target);

{a: {b: 1}}

示例代码2:

let target = {};
let source = { a: { b: 2 } };
Object.assign(target, source);

console.log(target);
source.a.b = 10;
console.log(source);
console.log(target);

{a: {b: 2}}
{a: {b: 10}}
{a: {b: 10}}

示例代码3:

let obj1 = { a: { b: 1 }, sym: Symbol(1) };
Object.defineProperty(obj1, 'innumerable', {
    value: '不可枚举属性',
    enumerbale: false
});
let obj2 = {};
Object.assign(obj2, obj1)
obj1.a.b = 2;

console.log('obj1', obj1);
console.log('obj2', obj2);

在这里插入图片描述
从前面的用例代码中可以看到,利用assign可以拷贝Symbol类型的对象,但是如果到了对象的第二层属性 obj1.a.b 的时候,前者值的改变,也会影响后者第二层属性的值,说明其中依旧存在访问共同堆内存的问题,这就是说明,这种方法还不能进一步的复制,而只是完成了浅拷贝的功能。

方法二:扩展运算符方式

利用JS的扩展运算符,在构造对象的同时完成浅拷贝的功能。
在这里插入图片描述
示例代码1:

/* 对象的拷贝 */
let obj = { a: 1, b: { c: 1 } };
let obj2 = { ...obj };
obj.a = 2;
console.log('obj', obj, 'obj2', obj2)
obj.b.c = 2;
console.log('obj', obj, 'obj2', obj2)

在这里插入图片描述
在这里插入图片描述

/* 数组的拷贝 */
let arr = [1,2,3];
let newArr = [...arr]

方法三:concat拷贝数组

数组的concat方法其实也是浅拷贝

let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr);
console.log(newArr);

[1,2,3]
[1,100,3]

let arr = [1, { a: 1 }, 3];
let newArr = arr.concat();
arr[1].a = 2;
console.log(newArr);

[1,{a:2},3]

方法四:slice拷贝数组

slice方法仅仅针对数组类型,会返回一个新的数组对象,由前两个参数来决定截取的开始和结束,是不会影响和改变原始数组的。
在这里插入图片描述

let arr = [1, 2, { val: 4 }];
let newArr = arr.slice();
newArr[2].val = 1000;

console.log(arr);

[1,2,{val:1000}]

手工实现一个浅拷贝

  1. 对基础类型做一个最基本的一个拷贝
  2. 对引用类型开辟一个新的存储,并且拷贝一层对象基础
const shallowClone = (target) => {
    if (typeof target === 'object' && target != null) {
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = target[prop];
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}

hasOwnProperty()方法:检测一个属性是否是对象的自有属性

深拷贝

浅拷贝:只是创建了一个新的对象,复制了原有对象的基本类型的值,而引用数据类型只拷贝了一层属性,再深层的还是无法进行拷贝。

深拷贝:对于复杂引用数据类型,其在堆内存中完全开辟了一块内存地址,并将原有的对象完全复制过来存放。 这两个对象是相互独立,不受影响的,彻底实现了内存上的分离。
在这里插入图片描述

方法一:乞丐版(JSON.stringfy)

JSON.stringfy() 是目前开发过程中最简单的深拷贝方法,其实就是把一个对象序列化成json的字符串,并把对象里面的内容转化成为字符串,最后再用JSON.parse()方法将json字符串生成新的对象。

let obj1 = { a: 1, b: [1, 2, 3] };
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log(obj2);
obj1.a = 2;
obj1.b.push(4);
console.log(obj1);
console.log(obj2);

{a:1,b:[1,2,3]}
{a:2,b:[1,2,3,4]}
{a:1,b:[1,2,3]}

注意

  1. 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过JSON.stringfy 序列化之后的字符串中这个键值对会消失
  2. 拷贝 Date 引用类型会变成字符串
  3. 无法拷贝不可枚举的属性
  4. 无法拷贝对象的原型链
  5. 拷贝正则引用类型会变成空对象
  6. 对象中含有NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null
  7. 无法拷贝对象的循环引用,即对象成环(obj[key]=obj)
function Obj() {
    this.func = function () { alert(1) };
    this.obj = { a: 1 };
    this.arr = [1, 2, 3];
    this.und = undefined;
    this.reg = /123/;
    this.date = new Date(0);
    this.NaN = NaN;
    this.infinity = Infinity;
    this.sym = Symbol(1);
}

let obj1 = new Obj();
Object.defineProperty(obj1, 'innumerbale', {
    enumerable: false,
    value: 'innumerbale'
});

console.log('obj1', obj1);
let str = JSON.stringify(obj1);
let obj2 = JSON.parse(str);
console.log('obj2', obj2);

在这里插入图片描述

方法二:基础版(手写递归实现)

function deepClone(obj) {
    let cloneObj = {};
    for (let key in obj) {     //遍历
        if (typeof obj[key] === 'object') {
            cloneObj[key] = deepClone(obj[key]);  //是对象就再次调用该函数递归
        } else {
            cloneObj[key] = obj[key];  //基本类型的话就直接复制值
        }
    }
    return cloneObj;
}

let obj1 = {
    a: {
        b: 1
    }
}
let obj2 = deepClone(obj1);
obj1.a.b = 2;

console.log(obj2);

{a:{b:1}}

但这种方法还是有问题:

  1. 这个深拷贝函数并不能复制不可枚举的属性以及Symbol属性
  2. 这种方法只是针对普通的引用类型的值做递归复制
  3. 对象的属性里面成环,即循环引用没有解决

我用方法一里的对象用方法二进行深拷贝结果如下:
在这里插入图片描述

方法三:改进版(改进递归实现)

  • 针对能够遍历对象的不可枚举属性以及Symbol 类型,我们可以使用Reflect.ownKeys方法(返回一个由目标对象自身的属性键组成的数组。)
  • 当参数为 Date、RegExp 类型,则直接生成一个新的示例返回
  • 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链
  • 利用 WeakMap 类型作为 Hash 表,因为WeakMap 是弱引用类型,可以有效防止内存泄漏,作为检测循环引用很有帮助,如果存在循环,则引用直接返回WeakMap存储的值
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);
const deepClone = function (obj, hash = new WeakMap()) {
    if (obj.constructor === Date)
        return new Date(obj);   //日期对象直接返回一个新的日期对象
    if (obj.constructor === RegExp)
        return new RegExp(obj);   //正则对象直接返回一个新的正则对象
    //如果循环引用了就用weakMap来解决
    if (hash.has(obj)) return hash.get(obj);
    let allDesc = Object.getOwnPropertyDescriptors(obj)
    //遍历传入参数所有键的特性
    let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
    //继承原型链
    hash.set(obj, cloneObj);
    for (let key of Reflect.ownKeys(obj)) {
        cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ?
            deepClone(obj[key], hash) : obj[key];
    }
    return cloneObj;
}

let obj = {
    num: 0,
    str: "",
    boolean: true,
    unf: undefined,
    nul: null,
    obj: { name: '我是一个对象', id: 1 },
    arr: [0, 1, 2],
    func: function () { console.log('我是一个函数') },
    date: new Date(0),
    reg: new RegExp('/我是一个正则/ig'),
    [Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
    enumerable: false, value: '不可枚举类型'
})

obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj; //设置loop成循环引用的属性

let cloneObj = deepClone(obj);
cloneObj.arr.push(4);
console.log('obj', obj);
console.log('cloneObj', cloneObj);

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ShardingSphere是一个用于分库分表的分布式数据库解决方案。它通过将数据分片存储在不同的数据库中,以提高数据库的性能和扩展性。其核心原理是基于特定的规则将数据按照某种规则进行分片,然后将数据分散存储在不同的数据库中。 在ShardingSphere中,分库分表的核心配置是ShardingRuleConfiguration。这个配置项是用来定义数据分片的规则和策略的。可以通过配置分片规则,指定哪些字段用来进行数据分片,以及分片的方式,比如按照范围、按照哈希等。同时,还可以指定数据库的分片策略,比如按照一致性哈希算法将数据分散存储在不同的数据库中。 除了分库分表,ShardingSphere还提供了其他核心功能,比如读写分离、分布式事务、数据脱敏和编排治理。读写分离功能可以将读操作和写操作分开处理,提高数据库的性能和负载均衡。分布式事务功能可以保证多个数据库之间的事务一致性。数据脱敏功能可以对敏感数据进行脱敏处理,保护用户的隐私。编排治理功能可以对分片进行动态管理和配置。 总之,ShardingSphere是一个功能强大的分库分表解决方案,它通过配置规则和策略,实现了数据的分片存储和管理,提高了数据库的性能和扩展性。同时,它还提供了其他核心功能,包括读写分离、分布式事务、数据脱敏和编排治理,以满足不同的分布式数据库需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [ShardingSphere分库分表核心原理第一节 理论基础和简介](https://blog.csdn.net/fegus/article/details/124942286)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [ShardingSphere分库分表核心原理第二节 应用集成和配置驱动](https://blog.csdn.net/fegus/article/details/124942445)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值