精华内容
下载资源
问答
  • 浅拷贝和深拷贝

    千次阅读 2016-01-05 17:12:31
    浅拷贝和深拷贝的区别
    在解释什么是浅拷贝和什么是深拷贝之前,我们先来看个例子,
        
    public class Thing implements Cloneable{
    //定义一个私有变量
    private ArrayList arrayList = new ArrayList();
    @Override
    public Thing clone(){
    Thing thing=null;
    try {
    thing = (Thing)super.clone();
    } catch (CloneNotSupportedException e) {
    e.printStackTrace();
    }
    return thing;
    }
    //设置HashMap的值
    public void setValue(String value){
    this.arrayList.add(value);
    }
    //取得arrayList的值
    public ArrayList getValue(){
    return this.arrayList;
    }
    }

    在Thing类中增加一个私有变量arrayLis,类型为ArrayList,然后通过setValue和getValue分别进行设置和取值,我们来看场景类是如何拷贝的
       
    public class Client {
    public static void main(String[] args) {
    //产生一个对象
    Thing thing = new Thing();
    //设置一个值
    thing.setValue("张三");
    //拷贝一个对象
    Thing cloneThing = thing.clone();
    cloneThing.setValue("李四");
    System.out.println(thing.getValue());
    }
    }

    猜想一下运行结果应该是什么?是仅一个“张三”吗?运行结果如下所示:
    [张三,李四]
    怎么会这样呢?怎么会有李四呢?是因为Java做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一种非常不安全的方式,在实际项目中使用还是比较少的(当然,
    这也是一种“危机”环境的一种救命方式)。
    你可能会比较奇怪,为什么在Mail那个类中就可以使用String类型,而不会产生由浅拷贝带来的问题呢?
    内部的数组和引用对象才不拷贝,其他的原始类型比如intlongchar等都会被拷贝,但是对于String类型,Java就希望你把它认为是基本类型,它是没有clone方法的,处理机制也比较特殊,通过字符串池(stringpool在需要的时候才在内存中创建新的字符串,读者在使用的时候就把String当做基本类使用即可。

    注意 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。浅拷贝是有风险的,那怎么才能深入地拷贝呢?我们修改一下程序就可以深拷贝,

       
     
    public class Thing implements Cloneable{
    //定义一个私有变量
    private ArrayList arrayList = new ArrayList();
    @Override
    public Thing clone(){
    Thing thing=null;
    try {
    thing = (Thing)super.clone();
    thing.arrayList = (ArrayList)this.arrayList.clone();
    } catch (CloneNotSupportedException e) {
    e.printStackTrace();
    }
    return thing;
    }
    }

    仅仅增加了粗体部分,对私有的类变量进行独立的拷贝。Client类没有任何改变,运行结果如下所示:
    [张三]
    该方法就实现了完全的拷贝,两个对象之间没有任何的瓜葛了,你修改你的,我修改我的,不相互影响,这种拷贝就叫做深拷贝。深拷贝还有一种实现方式就是通过自己写二进制
    流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下。
    注意 深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
    注意 要使用clone方法,类的成员变量上不要增加final关键字。  
    展开全文
  • 主要介绍了C#浅拷贝和深拷贝,是比较重要的概念,需要的朋友可以参考下
  • 主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 本篇文章主要介绍了浅析javaScript中的浅拷贝和深拷贝,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下.
  • 拷贝和深拷贝都是对于JS中的引用类型而言的,浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。只有深拷贝才是真正地对对象的拷贝
  • 主要介绍了JS浅拷贝和深拷贝原理与实现方法,结合实例形式分析了javascript浅拷贝深拷贝相关概念、原理、实现方法与操作注意事项,需要的朋友可以参考下
  • C/C++ 浅拷贝和深拷贝的实例详解 深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。 浅拷贝就是对内存地址...
  • 本文将会深入的探讨一下在拷贝对象中会出现的浅拷贝和深拷贝的情况。 拷贝接口 java中所有的对象都是继承自java.lang.Object。Object对象中提供了一个clone方法,来供我们对java对象进行拷贝。 protected native ...
  • 下面小编就为大家带来一篇深入理解python中的浅拷贝和深拷贝。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了Python基础教程之浅拷贝和深拷贝实例详解的相关资料,需要的朋友可以参考下
  • JavaScript的变量中包含两种类型的值:基本类型值 和 引用类型值,这篇文章主要介绍了JS中实现浅拷贝和深拷贝,需要的朋友可以参考下
  • Js复制对象/克隆对象 Js浅拷贝深拷贝拷贝和深拷贝的实现方法 前言 学习Js克隆一个对象,作为准备工作,需要理解Js中的数据类型和按值传递:Js中的数据类型和按值传递 浅拷贝最后两种方法不理解的话,可以读es5...

    Js复制对象/克隆对象 Js浅拷贝与深拷贝 浅拷贝和深拷贝的实现方法

    前言

    学习Js克隆一个对象,作为准备工作,需要理解Js中的数据类型和按值传递:Js中的数据类型和按值传递

    浅拷贝最后两种方法涉及到了继承和es5新特性中的call方法,可以读es5替换函数中的this的方法
    Js中的prototype、__proto__和constructor

    1. 浅拷贝

    1.1. 赋值和浅拷贝

    概念:

    浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是原始类型,拷贝的就是原始类型的值;如果属性是引用类型,拷贝的就是内存地址 。

    代码示例:

    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj1=student;
    function clone(obj){
        var newObj={};
        for(var key in obj){
            newObj[key]=obj[key]
        }
        return newObj;
    }
    var obj2=clone(student)
    obj1.name="Tom";
    obj2.sex="男";
    obj2.friends[0]="Lilei";
    console.log(student.name) //Tom 
    console.log(student.age) //15
    console.log(student.friends) //["Lilei", "Rose", "Ben"]
    
    

    解析:

    i. 赋值:

    上面一段代码,student变量中保存着所创建对象的地址,obj1由赋值得到,根据按值传递,我们知道,obj1变量中保存的是student所存地址的副本,这两个地址指向同一个实例化对象,无论哪个对象发生改变,都会改变这个实例化对象,两个对象是联动的。

    ii. 浅拷贝:

    a. 重新创建一个新对象,逐个拷贝源对象的属性;

    b. 如果属性值是原始类型数据,拷贝的是原始类型的值的副本,原始类型的属性在新对象和源对象之间互不影响

    c. 如果属性值是引用类型数据,拷贝的是地址,如果其中一个的地址发生改变,就会影响到另外一个对象

    1.2. 浅拷贝的方法

    1.2.1. Object.assign()

    Object.assign()方法可以将源对象自身的可枚举属性(任意多个)拷贝给目标对象,然后返回目标对象

    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj3=Object.assign({},student)
    console.log(obj3.name) //LilY
    
    1.2.2. 展开运算符…(Es6新特性)
    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj4={...student}
    console.log(obj4.name) //LilY
    
    1.2.3. 强行调用数组的concat方法
    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj5=Array.prototype.concat.call({},student)
    console.log(obj5) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}  
    
    1.2.4. 强行调用数组的slice方法
    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj6=Array.prototype.slice.call({},student)
    console.log(obj6) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}   
    

    2.深拷贝

    概念

    深拷贝是将一个对象从内存中完整的拷贝出一份,从内存中开辟出一个新的区域来存放新对象,新对象跟原对象不共享内存,修改新对象不会影响原对象。

    2.1. 深拷贝与浅拷贝对比

    浅拷贝与深拷贝对比

    2.2. 深拷贝的实现方式

    2.2.1. JSON.parce(JSON.stringify())
    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"]
    }
    var obj7=JSON.parse(JSON.stringify(student))
    console.log(obj7) //{name: "Lily", age: 15, sex: "女", friends: Array(3)}
    obj7.friends[0]="Alice"
    console.log(student.friends[0]) //Jack 未影响源对象
    

    利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的内存空间,实现深拷贝。

    这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,JSON.stringify()方法在处理undefined、任意的函数以及 symbol 值时,作为非数组对象的属性值时会被忽略,作为数组对象中的属性值时,会被转换成 null。函数、undefined 被单独转换时,会返回 undefined。

    使用JSON.parce(JSON.stringify())克隆非数组对象,源对象中的方法被忽略
    var student={
        name:"Lily",
        age:15,
        sex:"女",
        friends:["Jack","Rose","Ben"],
        say:function(){
            console.log("hello")
        }
    }
    var obj7=JSON.parse(JSON.stringify(student))
    console.log(obj7) //{name: "Lily", age: 15, sex: "女", friends: Array(3)} say方法被忽略
    
    使用JSON.parce(JSON.stringify())克隆数组对象,源对象中的方法被转换成null
    var arr= [1,2,3,function say(){console.log("hello")}];
    var obj=JSON.parse(JSON.stringify(arr));
    console.log(arr,obj)
    

    数组中的函数会被转换为null
    这里列举了一些最常用情况下的弊端,其他方面的弊端可参考JSON.stringify()

    2.2.2.手写递归

    递归方法实现深度克隆原理:遍历对象直到最内层都是原始数据类型,然后再去复制,就是深度拷贝
    有种特殊情况需注意就是对象存在循环引用的情况,即对象的属性直接的引用了自身的情况,解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝。

    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; 
      // 如果是null或者undefined我就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
      if (typeof obj !== "object") return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);
    
    解析:

    WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
    方法:

    WeakMap.prototype.delete(key)
    移除key的关联对象。执行后 WeakMap.prototype.has(key)返回false。
    WeakMap.prototype.get(key)
    返回key关联对象, 或者 undefined(没有key关联对象时)。
    WeakMap.prototype.has(key)
    根据是否有key关联对象返回一个Boolean值。
    WeakMap.prototype.set(key, value)
    在WeakMap中设置一组key关联对象,返回这个 WeakMap对象
    

    参考文章:
    如何写出一个惊艳面试官的深拷贝?(找不到文章的网址,只能贴个题目)
    一文读懂 javascript 深拷贝与浅拷贝(公众号推文,找不到文章的网址,只能贴个题目)
    WeakMap 对象

    展开全文
  • 一篇文章彻底搞懂浅拷贝和深拷贝

    万次阅读 多人点赞 2018-07-16 16:36:32
    强烈推荐30个原生JavaScript的demo,包括canvas时钟特效、自定义视频播放器、搜索栏快速匹配、fetch访问资源、console调试...浅谈深拷贝和浅拷贝 深拷贝和浅拷贝的区别 为什么要使用深拷贝? 深拷贝的要求程度...

    强烈推荐30个原生JavaScript的demo,包括canvas时钟特效、自定义视频播放器、搜索栏快速匹配、fetch访问资源、console调试技巧等,先fork后学习,详见点击打开链接,欢迎点赞~~~谢谢,共同进步学习!

     

    由博主《前端初级工程师面试系列一JS基础》文章一JS变量类型引伸的考点,变量类型分为基本类型和引用类型,那么在变量拷贝赋值时,也是不一样的,分为浅拷贝和深拷贝,是面试中常考的知识点,也是实际开发中经常会用到的内容。

    目录

    正文

    前言: 最开始意识到深拷贝的重要性是在我使用redux的时候(react + redux), redux的机制要求在reducer中必须返回一个新的对象,而不能对原来的对象做改动,事实上,当时我当然不会主动犯这个错误,但很多时候,一不小心可能就会修改了原来的对象,例如:var newObj = obj; newObj.xxx = xxx  实际上,这个时候newObj和obj两个引用指向的是同一个对象,我修改了newObj,实际上也就等同于修改了obj,这,就是我和深浅拷贝的第一次相遇。

    深拷贝和浅拷贝的区别

    1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用

    2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

    为什么要使用深拷贝?

    我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

    深拷贝的要求程度

    我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素还是递归拷贝所有层级的对象属性和数组元素?

    怎么检验深拷贝成功

    改变任意一个新对象/数组中的属性/元素,     都不改变原对象/数组

    只对第一层级做拷贝

    深拷贝数组(只拷贝第一级数组元素) 

    1. 直接遍历
    var array = [1, 2, 3, 4];
    function copy (array) {
       let newArray = []
       for(let item of array) {
          newArray.push(item);
       }
       return  newArray;
    }
    var copyArray = copy(array);
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    该方法不做解释(逃...)

    2. slice()

    var array = [1, 2, 3, 4];
    var copyArray = array.slice();
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    slice() 方法返回一个从已有的数组中截取一部分元素片段组成的新数组(不改变原来的数组!)

    用法:array.slice(start,end) start表示是起始元素的下标, end表示的是终止元素的下标

    当slice()不带任何参数的时候,默认返回一个长度和原数组相同的新数组

    3. concat()

    var array = [1, 2, 3, 4];
    var copyArray = array.concat();
    copyArray[0] = 100;
    console.log(array); // [1, 2, 3, 4]
    console.log(copyArray); // [100, 2, 3, 4]

    concat() 方法用于连接两个或多个数组。( 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。)

    用法:array.concat(array1,array2,......,arrayN)

    因为我们上面调用concat的时候没有带上参数,所以var copyArray = array.concat();实际上相当于var copyArray = array.concat([]);也即把返回数组和一个空数组合并后返回

     

    但是,事情当然不会这么简单,我上面的标题是 “深拷贝数组(只拷贝第一级数组元素)”,这里说的意思是对于一级数组元素是基本类型变量(如number,String,boolean)的简单数组, 上面这三种拷贝方式都能成功,但对第一级数组元素是对象或者数组等引用类型变量的数组,上面的三种方式都将失效,例如:

    
    var array = [
      { number: 1 },
      { number: 2 },
      { number: 3 }
    ];
    var copyArray = array.slice();
    copyArray[0].number = 100;
    console.log(array); //  [{number: 100}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    深拷贝对象

    1.直接遍历

    var obj = {
      name: '彭湖湾',
      job: '学生'
    }
    
    function copy (obj) {
       let newObj = {};
         for (let item in obj ){
           newObj[item] = obj
         }
         return newObj;
    }
     var copyObj = copy(obj);
    copyObj.name = '我才不是彭湖湾呢! 哼 (。・`ω´・)';
    console.log(obj); // {name: "彭湖湾", job: "学生"}
    console.log(copyObj); // {name: "我才不是彭湖湾呢! 哼 (。・`ω´・)", job: Object}

    该方法不做解释(逃...)

    2.ES6的Object.assign

    var obj = {
      name: '彭湖湾',
      job: '学生'
    }
    var copyObj = Object.assign({}, obj);
    copyObj.name = '我才不叫彭湖湾呢! 哼  (。・`ω´・)';
    console.log(obj);   // {name: "彭湖湾", job: "学生"}
    console.log(copyObj);  // {name: "我才不叫彭湖湾呢! 哼  (。・`ω´・)", job: "学生"}

    Object.assign:用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回合并后的target

    用法: Object.assign(target, source1, source2);  所以 copyObj = Object.assign({}, obj);  这段代码将会把obj中的一级属性都拷贝到 {}中,然后将其返回赋给copyObj

    3.ES6扩展运算符:

    var obj = {
        name: '彭湖湾',
        job: '学生'
    }
    var copyObj = { ...obj }
    copyObj.name = '我才不叫彭湖湾呢! 哼  (。・`ω´・)'
    console.log(obj.name) //   彭湖湾
    console.log(copyObj.name)  // 我才不叫彭湖湾呢! 哼  (。・`ω´・)

    扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中

    ⚠️注意:实际上,无论是使用扩展运算符(...)还是解构赋值,对于引用类型都是浅拷贝。所以在使用splice()、concat()、...对数组拷贝时,只有当数组内部属性值不是引用类型是,才能实现深拷贝。对多层嵌套对象,也即是存在,很遗憾,上面三种方法,都会失败:

    var obj = {
       name: {
          firstName: '彭',
          lastName: '湖湾'
       },
       job: '学生'
    }
    var copyObj = Object.assign({}, obj)
    copyObj.name.lastName = '湖水的小浅湾';
    console.log(obj.name.lastName); // 湖水的小浅湾
    console.log(copyObj.name.lastName); // 湖水的小浅湾

    拷贝所有层级

    有没有更强大一些的解决方案呢?使得我们能够

    1.不仅拷贝第一层级,还能够拷贝数组或对象所有层级的各项值

    2. 不是单独针对数组或对象,而是能够通用于数组,对象和其他复杂的JSON形式的对象

    请看下面:

    下面这一招可谓是“一招鲜,吃遍天”

    1.JSON.parse(JSON.stringify(XXXX))

    var array = [
        { number: 1 },
        { number: 2 },
        { number: 3 }
    ];
    var copyArray = JSON.parse(JSON.stringify(array))
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    JSON.parse() 方法用于将一个 JSON 字符串转换为对象--(反序列化)

    JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串--(序列化)

    序列化的缺点:

    1. 不支持基本数据类型的undefined,序列化后将其省略
    2. 不支持函数
    3. Nan,Infinity序列化的结果是null

    能用大招杀的就不要用q杀嘛!!

    2.手动写递归

    你说啥? 你说上面的那种方法太无脑,  一定要自己写一段递归才有做技术的感觉? OK成全你!

    let array = [
       { number: 1 },
       { number: 2 },
       { number: 3 }
    ];
    function copy (obj) {
            //首先判断需要拷贝的“东西”是什么类型
            if(typeof obj !== 'object' || obj == null){
                return;
            }
            let newobj = obj.constructor === Array ? [] : {};
            //obj是数组类型,下面的i就是index;obj是对象,i就是key
            for(let i in obj){
               newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
            }
            return newobj
    }
    
    let copyArray = copy(array)
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    【注意】上文的所有的示例都忽略了一些特殊的情况: 对对象/数组中的Function,正则表达式等特殊类型的拷贝

    上述代码中还隐藏这两个知识点:

    (1)obj == null 为何不是 obj === null?

    obj==null =>obj为null 或者 obj 为undefined,因为null == undefined,这样写能默认处理两种情况,obj===null ,成立前提只有obj是null

    (2)怎样判断一个对象是不是数组?

    先上方法(确定的方法有两种)

    1. 根据对象的class属性来判断,跨原型链调用toString()方法。  Object. prototype.toString.call(obj)===[ object Array]
    2. Array.isArray直接判断   Array. isArray(obj)。

    补充:

    • 推荐文章:为什么用Object.prototype.toString.call(obj)检测对象类型

      toString为Object的原型方法,返回一个用来描述该对象的字符串,所以可以调用对象原型方法toString()来探明对象的信息。
      那么原型方法怎么调用呢,利用call,将其this指向需要判断的对象, 就可以用toString()方法了。
      let arr=[1,2,3];
      console.log(Array.prototype.hasOwnProperty("toString"));//true
      console.log(arr.toString());//1,2,3 arr作为对象Object的实例,重写了toString()方法。
      delete Array.prototype.toString;//delete操作符可以删除实例属性
      console.log(Array.prototype.hasOwnProperty("toString"));//false
      console.log(arr.toString());//"[object Array]" 
      // 由于删除了实例对象中的toString()方法,找不到,顺着原型链往上走,就调用了对象Object的方法,返回的结果就和Array.prototype.toString(arr)一样的。
    • 根据构造函数来判断 instanceof 操作符可以来表示实例是否属于某个构造函数创建的。

    这种方法有一个问题,就是验证不够严格。 即使对象创建时不是使用数组创建的,但是只要原型链上有数组类型,也认为是数组,亦或者,即便创建时是数组创建,但其原型上有对象类型,便不再被认为是数组。

    推荐文章

    (3)for...in 和for...of,forEach的区别,for...in用于对象复制时需要注意什么?

    for... in特点

    • 遍历对象返回的对象的key值,遍历数组返回的数组的下标(key)。

    • for ... in 会遍历原型上的属性值

    • 遍历返回数据是乱序

    总结一句:  for in 循环特别适合遍历对象。

    for... of特点

    • for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

    • for ... in 会遍历原型上的属性值

    • 遍历返回数据是乱序

    • for of 不同与 forEach, 它可以与 break、continue和return 配合使用,也就是说 for of 循环可以随时退出循环。

    总结一句:  for of 比较适合遍历数组,及其他具有遍历器的集合

    forEach特点

    • 使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。forEach与break和return 不搭

    • forEach()无法在所有元素都传递给调用的函数之前终止遍历

    for…in循环可应用于对象的复制,不过其有一个缺点,就是会从原型属性里继承prototype()属性。 

    例如: 

    let array = [1,2,3,4,5] 
    Array.prototype.age = 13;
    var result = []; 
    for(let i in array){ 
        result.push(array[i]); 
    } 
    alert(result.join(“,”)); 
    result返回结果【1,2,3,4,5,13】 

    如何避免从原型属性里继承prototype()属性,这里使用hasOwnProperty(name),该函数指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有返回true,如果没有返回false。 

    let array = [1,2,3,4,5] 
    Array.prototype.age = 13; 
    var result = []; 
    for(let i in array){ 
    if(array.hasOwnProperty(i)){ 
        result.push(array[i]); 
    } 
    alert(result.join(“,”)); 
    } 
    result返回结果【1,2,3,4,5】

    所以上面的深拷贝代码应优化为如下: 

    let array = [
       { number: 1 },
       { number: 2 },
       { number: 3 }
    ];
    function copy (obj) {
            //首先判断需要拷贝的“东西”是什么类型
            if(typeof obj !== 'object' || obj == null){
                return;
            }
            let newobj = Array.isArray(obj) ? [] : {};
            //obj是数组类型,下面的i就是index;obj是对象,i就是key
            for(let i in obj){
               if(obj.hasOwnProperty(i)){
                   newobj[i] = typeof obj[i] === 'object' ? copy(obj[i]) : obj[i];
                }
            }
            return newobj
    }
    
    let copyArray = copy(array)
    copyArray[0].number = 100;
    console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
    console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

    存在大量深拷贝需求的代码——immutable提供的解决方案

    实际上,即使我们知道了如何在各种情况下进行深拷贝,我们也仍然面临一些问题: 深拷贝实际上是很消耗性能的。(我们可能只是希望改变新数组里的其中一个元素的时候不影响原数组,但却被迫要把整个原数组都拷贝一遍,这不是一种浪费吗?)所以,当你的项目里有大量深拷贝需求的时候,性能就可能形成了一个制约的瓶颈了。

    immutable的作用

    通过immutable引入的一套API,实现:

    1.在改变新的数组(对象)的时候,不改变原数组(对象)

    2.在大量深拷贝操作中显著地减少性能消耗

    先睹为快:

    const { Map } = require('immutable')
    const map1 = Map({ a: 1, b: 2, c: 3 })
    const map2 = map1.set('b', 50)
    map1.get('b') // 2
    map2.get('b') // 50
    展开全文
  • js代码-浅拷贝和深拷贝的实现

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,812
精华内容 36,324
关键字:

浅拷贝和深拷贝