zoukankan      html  css  js  c++  java
  • Javascript/js 的浅拷贝与深拷贝(复制)学习随笔

    js变量的数据类型值分基本类型值和引用类型值。

    在ES6(ECMAScript6)以前,基本数据类型包括String、Number、Boolean、Undefined、Null。

    基本类型值的复制(拷贝)

    从一个变量向另一个变量复制基本类型的值。使用赋值操作符 ' = ' 即可。

    如: 

    1 var num1 = 1, num2;
    2 num2 = num1;

    上述代码定义了两个变量num1、num2。num1初始化值是1,num2是undefined。接着把num1赋值给num2。

    num1的值与num2的值增删改减完全独立、互不影响。

    1 ++num1;
    2 num2 = null;
    3 // 2 null

    拓展:基于基本类型值,ES提供了三个特殊的引用类型。String、Number、Boolean。(基本包装类型)

    1 var num3 = 3;
    2 var num4 = num3.toFixed(2);
    3 console.log(num3, num4); // 3 3.00

    如上,变量num3包含一个数字值,数字当然属于基本类型值啦,接着num3调用了toFixed()方法。并将返回结果保存在num4中。最后在控制台输出下。结果是3 3.00。当然了,没有报错。。

    一般来理解,基本类型值不是对象,不应该有方法。(但是它们确实有方法.。查看它们有哪些方法的一个办法是在chrome控制台console.log(new Number(1))。其他基本类型值同理。baidu/翻书/强记。do whatever you want)

    当第二行代码访问num3时,访问过程处于一种读取模式,也就是从内存中读取这个变量的值。此时,在后台,大概是执行了下列的es代码:

    1 var _num3 = new Number(3);// 创建Number类型的一个实例
    2 var _num4 = _num3.toFixed(2);// 在实例上调用指定的方法
    3 _num3 = null;// 销毁这个实例
    4 return _num4;// 可以想象成在一个函数里执行这里的4行代码,函数返回_num4。接着被num4接收。

    这也意味着我们可以对基本类型值做一些扩展。比如:

    1 var num5 = 1;
    2 Number.prototype.addTen = function () {
    3   var res = this + 10;
    4   return res;
    5 };
    6 console.log(num5.addTen());// 11    

    如上,在Number原型上定义addTen()方法,所有Number类型值都可以调用这个方法。

    其他基本类型值同理。

    ES6规范引入了一项新特性--symbol,它也是一种基本数据类型,它的功能类似于一种标识唯一性的ID。

    调用Symbol函数来创建一个Symbol实例:

    1 const S1 = Symbol();
    2 // 可以在调用Symbol函数时传入一个参数,相当于给你创建的Symbol实例一个描述信息。参数可选,可以是任意可转化成字符串的值。
    3 const S2 = Symbol('id9527');

    引用类型的复制(拷贝)

    常见的引用类型包括 Object、Aarry、Date、Function、RegExp...

    引用类型值是引用类型的一个实例。

    通过赋值操作符‘=’复制的引用类型值。实际上复制的是一个指针(地址)。该指针指向存储在堆中的对象。

    1 const obj1 = new Object();
    2 const obj2 = obj1;
    3 obj1.name = 'xm';
    4 console.log(obj2.name);// xm

    obj1与obj2指向同一个对象,对obj1的修改,同样作用于obj2。

    多数时候这不是我们想要的结果。我们需要的是两个相互独立而又长得一模一样的对象。

    由于引用类型值也可能包含引用类型值。由此就派生出了浅拷贝和深拷贝。

    浅拷贝

    数组的浅拷贝常用方法:

    (1)concat方法

    1 const arr1 = ['a', 'b', ['c', 4]];
    2 const arr2 = arr1.concat([]);
    3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false

    (2)slice方法

    1 const arr1 = ['a', 'b', ['c', 4]];
    2 const arr2 = arr1.slice(0);
    3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false

    (3)扩展运算符...方法

    1 const arr1 = ['a', 'b', ['c', 4]];
    2 const arr2 = [...arr1];
    3 // const [...arr2] = arr1; // 等同于上一行
    4 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false

    (4)map方法

    1 const arr1 = ['a', 'b', ['c', 4]];
    2 const arr2 = arr1.map(item => item);
    3 console.log(arr2, arr1 == arr2);// ['a', 'b', ['c', 4]] false

    (5)filter方法 把上面的map改成filter即可。

    ...for循环、forEach、for of、splice、Object.values等方法均可。

    对象的浅拷贝常用方法:

    1、for in遍历方法

     1 const obj = {
     2   say(){
     3     console.log('hello');
     4   }
     5 };
     6 const obj1 = Object.create(obj);
     7 obj1.a = '对象';
     8 obj1.b = [1, 2, 3];
     9 
    10 // const obj2 = Object.create(obj); // 继承obj的属性、方法
    11 const obj2 = {};
    12 for (let p in obj1) {
    13   if (obj1.hasOwnProperty(p)) {
    14     obj2[p] = obj1[p];
    15   }
    16 }

    如上,obj1的原型对象是obj,浅拷贝一般不需要拷贝原型上的属性和方法,而for in循环可以枚举原型上的属性和方法。使用hasOwnProperty()方法过滤掉原型的属性和方法。

    结果如下:

    (2)Object.entries()方法

     1 const obj = {
     2   say(){
     3     console.log('hello');
     4   }
     5 };
     6 
     7 const obj1 = Object.create(obj);
     8 obj1.a = '对象';
     9 obj1.b = [1, 2, 3];
    10 
    11 // const obj2 = Object.create(obj); // 继承obj的属性、方法
    12 const obj2 = {};
    13 Object.entries(obj1).forEach(([key, value]) => obj2[key] = value);
    结果如下:

     之所以称为浅拷贝,其原因在于如果引用类型值里包含引用类型值,上述的所有方法,在对里层的引用类型值复制操作时,使用的还是赋值操作符'='。如下所示:

    如果修改了obj1.b的值,同样会作用于obj2。

    深拷贝

    以下是实现对数组、对象深拷贝的一种方法。

    采用递归的方式,层层遍历。

     1 const deepClone = function handleDeepClone(obj) {
     2   if (typeof obj !== 'object' || obj === null) {
     3     return obj;
     4   }
     5 
     6   let _obj;
     7   if (obj instanceof Array) {
     8     _obj = [];
     9     obj.forEach((item, i) => _obj[i] = handleDeepClone(item));
    10   } else {
    11     _obj = {};
    12     Object.entries(obj).map(([key, value]) => _obj[key] = handleDeepClone(value));
    13   }
    14 
    15   return _obj;
    16 };

     结果如下:

  • 相关阅读:
    Windows Mobile自动更新
    【实用代码片段】将json数据绑定到html元素 (转)
    B/S打印解决方案参考
    python基础之1-安装
    python里的生成器
    python中的函数的执行分类
    python中的return的返回与执行
    自创最精简的python装饰器
    python中的lambda函数用法
    python中的str.strip()的用法
  • 原文地址:https://www.cnblogs.com/caimuguodexiaohongmao/p/11108217.html
Copyright © 2011-2022 走看看