关于深浅拷贝的那些事儿(一)

个人学习笔记,很多不足

一、赋值、浅拷贝、深拷贝的区别

      首先JS中主要分为两种数据类型,一种是基本数据类型,另一种是复杂数据类型,深浅拷贝针对的主要是复杂数据类型,而赋值是两种数据类型都有的。

1.1赋值

      基本数据类型的数据直接存储在栈中,所以基本数据类型的赋值就是直接在栈中开辟一块内存,将数据原封不动的传递给另一个变量。

      复杂数据类型的数据在栈中存储的是地址值(指针)指向堆区中存储的真正数据,所以复杂数据类型的赋值就是把栈中存储的地址值原封不动的传递给另一个变量,简单地说,赋值不涉及堆区数据的改动

1.2 浅拷贝

    浅拷贝会在堆区重新开辟一块内存,将原对象的属性进行拷贝,但它不涉及递归拷贝,对于属性值依然是复杂数据类型的情况,他不会深入拷贝,只是复制他们的地址,因此新对象的改变会影响旧对象,看下例。

var arr = [ { age: 23 }, [1,2,3,4] ];
var newArr = arr.concat();

arr[0].age = 18;
arr[1][0] = 100;

console.log(arr) // [ {age: 18}, [100,2,3,4] ]
console.log(newArr) // [ {age: 18}, [100,2,3,4] ]


由此可见数组的concat方法只是浅拷贝,只复制了属性的地址值

1.3 深拷贝

        深拷贝也会在堆区开辟一块新内存,但与浅拷贝不同的是它会将属性值递归拷贝,复制出一个全新的对象,新对象的改变不会影响旧对象。

 

二、哪些方法可以实现深浅拷贝

准备知识

1、属性可不可枚举指的是什么

     枚举指的就是对象的属性能不能被遍历出来

2、那有什么方法可以判断属性是不是可枚举的呢

     a. propertylsEnumerable()

     用法:obj.propertylsEnumerable('property')

     小栗子:

person.propertyIsEnumerable('sex');//false
person.propertyIsEnumerable('age');//true

     propertyIsEnumerable方法只对对象自身的属性(对象自身添加的、构造函数实例化的)有效,对原型上的、继承来的属性都无效

     b. Object.defineProperty()

     用法:Object.defineProperty(obj, prop, descriptor)
           第一个:目标对象
           第二个:目标属性,字符串
           第三个:对目标属性的行为,放在对象里
     小栗子:

var person = {
    name:'xiao',
    age: '18',
    sex: 'boy'
}
 
Object.defineProperty(person,'age',{
    enumerable:true,//可以被枚举
});
Object.defineProperty(person,'sex',{
    enumerable:false,//不可以被枚举
})
 
for(var k in person){
    console.log(person[k])//a,可以被枚举
}
//18
//xiao

注意:用户自定义的person对象,里面的属性都是可枚举的

  但是内置的对象Math和基本包装类型里的属性是不可枚举的,如Object, Array, Number等

 

2.1 浅拷贝

a. for...in...

      首先for in遍历出来的是对象中的属性,而不是索引值,它会把对象自身以及继承过来的可枚举属性都进行遍历

b. object.keys(obj)

     返回对象自身的可枚举属性

c. object.assign(目标对象,源对象1,源对象2....)

    源对象只将自身的可枚举属性浅拷贝到目标对象上,若目标对象不是对象类型会自动转化为对象 ,object.assign只会复制属性值

// 源对象
const source = {
   //属性是取值函数
   get foo(){return 1}
};
//目标对象
const target = {};
Object.assign(target,source);
//{foo ; 1}  此时foo的值是get函数的求值结果

用途:

     为对象添加属性

class Point{
   constructor(x,y){
      Object.assign(this,{x,y});        //point类的实例都会有x.y属性
   }
}

     为对象添加方法

// 方法也是对象
// 将两个方法添加到类的原型对象上
// 类的实例会有这两个方法
Object.assign(SomeClass.prototype,{
    someMethod(arg1,arg2){...},
    anotherMethod(){...}
});

2.2 深拷贝

2.2.1  JSON

JSON.stringify: 将JS对象转化为JSON字符串

JSON.parse: 将JSON字符串转化为JS对象

     通过这两种方法,绕过一般的浅拷贝,先将对象转化为字符串在将字符串转化为对象,就能完成深拷贝,生成一个全新的对象。

var arr = ['str', 1, true, [1, 2], {age: 23}]

var newArr = JSON.parse( JSON.stringify(arr) );
newArr[3][0] = 100;
console.log(arr); // ['str', 1, true, [1, 2], {age: 23}]
console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}]

   但这种方法有缺陷,有一些对象在转化过程中会出错,比如任意的函数、undefined以及symbol值在序列化的过程中会忽略(针对非数组的属性值),或者转化为null(针对数组)

   函数、undefined 被单独转换时,会返回 undefined

   对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误

   NaN 和 Infinity 格式的数值及 null 都会被当做 null

2.2.2 现有的API

var arr = [1,2,3];
var newArr = arr.toString().split(',').map(item => Number(item))
newArr[0] = 100;
console.log(arr); // [1,2,3]
console.log(newArr); // [100,2,3]

  上面的方法也存在缺陷,只能处理纯数字的数组

  1. toString无法正确的处理对象和函数
  2. Number无法处理 false、undefined等数据类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值