JS中的两种数据类型及实现引用类型的深拷贝的方法

一.前言

我们知道,在JS中数据类型按照访问方式和存储方式的不同可分为基本类型和引用类型。

基本类型

基本类型有String、Boolean、Number,Undefined、Null,这些基本类型都是按值传递的,也称为值类型。

引用类型

引用类型有对象、数组、函数,它们都是按引用访问的。

二.存储方式区别

基本类型和引用类型由于两者在内存中存储的方式不同,造成两者访问的方式也不同。其中,基本类型存储在内存的栈中,是按值访问;引用类型存储在内存的堆中,是按引用访问。可如下图所示:

当有

 var n1 = 10;
 var n2 = 10;
 var arr1 = [1,2,3,4]
 var arr2 = [1,2,3,4]

其在内存中存储方式如图:

值类型是在栈中直接保存的变量的实际值,而引用类型在栈中仅仅保存的是变量指向堆中的地址,从上图中可以看出,值类型的变量n1和n2都是10,但是在栈中却为这两个变量分别开辟了两块空间来存储,而引用类型的变量arr1和arr2也相同,但是在堆中仅仅开辟了一块内存来存储,而在栈中存储的是这两个变量指向堆中的地址,这两个变量都指向堆中的同一片地址。

三.拷贝区别

正是由于两者在存储方式上的不同,造成了两者在拷贝时的差异。首先,先看两段代码:

 var n1 = 10;
 //将n1拷贝给n2
 var n2 = n1;
 n1 = 12;
 console.log(n1);
 console.log(n2);

先定义变量n1=10,然后将n1拷贝给n2,再接着改变n1的值为12,分别打印n1和n2的值,打印结果为:

从结果中我们可以看到,n1变为12了,但是n2不受影响,依旧是10。

再看另外一段代码:

 var arr1 = [1,2,3,4]
 //将arr1拷贝给arr2
 var arr2 = arr1;
 //向arr1中尾部添加一个元素5
 arr1.push(5);
 console.log(arr1);
 console.log(arr2);

先定义数组arr1,然后将arr1拷贝给arr2,再接着向arr1中尾部追加一个元素,分别打印arr1和arr2的值,打印结果为:

从结果中我们可以看到:我们先将arr1拷贝给了arr2,但是当我们改变arr1时,arr2也跟着一起改变了。这印证了前文所说的,arr1和arr2实际上是指向了内存中的同一片地址,当arr1发生变化时,实际上是将指向的这片内存地址中的数据变化了,而arr2也指向的是这片地址,所以arr2也会跟着变化。

上面代码中的arr2=arr1,就是我们俗称的浅拷贝,浅拷贝仅仅拷贝的是变量名,其在内存的存储没有被拷贝,指向的还是同一片内存地址,这就是造成了一个变化另外一个也跟着变化,这在日常开发中不是我们想要的。

那如何实现真正的拷贝呢?

四.实现深拷贝

真正的拷贝,就是拷贝过后,arr1和arr2指向的不再是同一片内存地址,而是分别指向各自的地址,这样发生变化的时候就不会出现同时变化,这就是深拷贝。

下面我们封装一个深拷贝函数,来实现引用类型的深拷贝:

 //参数p为原对象
 //参数c为原对象的类型,若原对象为数组,则传入c为[],若原对象是对象传入c为{},也可不传默认为{}
 function deepCopy(p,c){
 var c = c || {};
 for(var i in p){
   if(typeof p[i] === "object"){
     c[i] = (p[i].constructor === Array)?[]:{};
     deepCopy(p[i],c[i])
     }else{
     c[i] = p[i]
    }
   }
   return c;
 }

五.测试

 //参数p为原对象
 //参数c为原对象的类型,若原对象为数组,则传入c为[],若原对象是对象传入c为{},也可不传默认为{}
 function deepCopy(p,c){
 var c = c || {};
 for(var i in p){
   if(typeof p[i] === "object"){
      c[i] = (p[i].constructor === Array)?[]:{};
      deepCopy(p[i],c[i])
     }else{
       c[i] = p[i]
     }
   }
   return c;
 }
 //定义数组arr1
 var arr1 = [1,2,3,4]
 //将arr1拷贝给arr2
 var arr2 = deepCopy(arr1,[]);
 //向arr1中尾部添加一个元素5
 arr1.push(5);
 console.log('arr1:',arr1);
 console.log('arr2:',arr2);

测试结果:

结果中可以看到,arr1变化没有引起arr2的变化,实现了真正的拷贝。

总结

以上所述是小编给大家介绍的JS中的两种数据类型及实现引用类型的深拷贝的方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • JavaScript 中对象的深拷贝

    对象的深拷贝与浅拷贝的区别如下: 浅拷贝:仅仅复制对象的引用,而不是对象本身: 深拷贝:把复制的对象所引用的全部对象都复制一遍. 一. 浅拷贝的实现 浅拷贝的实现方法比较简单,只要使用是简单的复制语句即可. 1.1 方法一:简单的复制语句 /* ================ 浅拷贝 ================ */ function simpleClone(initalObj) {     var obj = {};     for ( var i in initalObj) {   

  • JavaScript中值类型和引用类型的区别

    JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和布尔值.此外,JavaScript中还有两个特殊的原始值:null和undefined,它们既不是数字也不是字符串,更不是布尔值.它们通常分别代表了各自特殊类型的唯一成员.JavaScript中除了数字.字符串.布尔值.null和undefined之外的就是对象了. JavaScript中值类型和引用类型的区别 JavaScript中的值类型的值是不可变的: 任何方法都无法改变值类型的值.数字.布尔值.

  • 浅谈JS的基础类型与引用类型

    两种类型: ECMAScript变量包含两种不同类型的值:基本类型值.引用类型值: 基本类型值:指的是保存在栈内存中的简单数据段: 引用类型值:指的是那些保存在堆内存中的对象,意思是,变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,由该位置保存对象: 两种访问方式: 基本类型值:按值访问,操作的是他们实际保存的值: 引用类型值:按引用访问,当查询时,我们需要先从栈中读取内存地址,然后再顺藤摸瓜地找到保存在堆内存中的值: 两种类型复制 1.基本类型变量的复制:从一个变量向一个变量复

  • 再谈Javascript中的基本类型和引用类型(推荐)

    一.基本类型和引用类型概述 js中数据类型的值包括:基本类型值和引用类型值 基本数据类型:undefined;null;boolean;number;string 引用类型值:保存在内存中,js不允许直接访问内存位置,因此时操作引用而不是实际对象 二.如何检测数据类型 1.基本数据类型的检测:使用typeof var s = "AAA"; alert(typeof s); //返回string 2.引用类型(对象类型)检测:使用instanceof alert(person insta

  • javascript深拷贝和浅拷贝详解

    一.数组的深浅拷贝 在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生. 这是为什么呢? 因为如果只是简单的赋值,它只是进行了地址的引用,所以改变一个另一个也会跟着变. var arr = ["One","Two","Three"]; var arrto = arr; arrto[1] = "te

  • js对象浅拷贝和深拷贝详解

    本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: var Chinese = { nation:'中国' } var Doctor = { career:'医生' } function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } c.uber = p; return c; } 使用的时候,这样写

  • JS中的两种数据类型及实现引用类型的深拷贝的方法

    一.前言 我们知道,在JS中数据类型按照访问方式和存储方式的不同可分为基本类型和引用类型. 基本类型 基本类型有String.Boolean.Number,Undefined.Null,这些基本类型都是按值传递的,也称为值类型. 引用类型 引用类型有对象.数组.函数,它们都是按引用访问的. 二.存储方式区别 基本类型和引用类型由于两者在内存中存储的方式不同,造成两者访问的方式也不同.其中,基本类型存储在内存的栈中,是按值访问:引用类型存储在内存的堆中,是按引用访问.可如下图所示: 当有 var

  • JS中的四种数据类型判断方法

    目录 1.typeof 2.instanceof 3.constructor 4.toString() 本文总结了四种判断方法: 1.typeof typeof是一个运算符,其有两种使用方式:(1)typeof(表达式); (2)typeof 变量名;返回值是一个字符串,用来说明变量的数据类型;所以可以用此来判断number, string, object, boolean, function, undefined, symbol 这七种类型,每种情况返回的内容如下表所示: // 字符串 con

  • 对pandas中两种数据类型Series和DataFrame的区别详解

    1. Series相当于数组numpy.array类似 s1=pd.Series([1,2,4,6,7,2]) s2=pd.Series([4,3,1,57,8],index=['a','b','c','d','e']) print s2 obj1=s2.values # print obj1 obj2=s2.index # print obj2 # print s2[s2>4] # print s2['b'] 1.Series 它是有索引,如果我们未指定索引,则是以数字自动生成. 下面是一些例

  • 简单介绍Python中的几种数据类型

    大体上把Python中的数据类型分为如下几类: Number(数字) 包括int,long,float,complex String(字符串) 例如:hello,"hello",hello List(列表) 例如:[1,2,3],[1,2,3,[1,2,3],4] Dictionary(字典) 例如:{1:"nihao",2:"hello"} Tuple(元组) 例如:(1,2,3,abc) Bool(布尔) 包括True.False 由于Pyt

  • JS入门必备之八种数据类型

    目录 一.开门见山 二.动态数据类型 (1)字符串string (2)数值number (3)布尔boolean (4)null和undefined (5)symbol (6)对象object 三.基本数据类型与引用数据类型区别 3.1 声明变量时不同的内存分配 3.2 正是因为内存分配不同,在复制变量时结果也不一样 3.3 参数传递不同 总结 一.开门见山 在ES5的时候,我们认知的数据类型确实是 6种:Number.String.Boolean.undefined.object.Null E

  • 详解js中的几种常用设计模式

    工厂模式 function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18) 工厂函数

  • 浅谈TreeSet中的两种排序方式

    直接上代码: package exercise1; public class Person implements Comparable{ private int id; private String name; public Person(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { th

  • 浅谈js中的三种继承方式及其优缺点

    第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = 'yellow'; this.view = function(){ return this.hair + ',' + this.eye + ',' + this.skin; } } //子类 function man(){ this.feature = ['beard','strong']; } man.pr

  • 浅谈JS中的三种字符串连接方式及其性能比较

    工作中经常会碰到要把2个或多个字符串连接成一个字符串的问题,在JS中处理这类问题一般有三种方法,这里将它们一一列出顺便也对它们的性能做个具体的比较. 第一种方法 用连接符"+"把要连接的字符串连起来: str="a"; str+="b"; 毫无疑问,这种方法是最便捷快速的,如果只连接100个以下的字符串建议用这种方法最方便. 第二种方法 以数组作为中介用 join 连接字符串: var arr=new Array(); arr.push(a);

  • js中 计算两个日期间的工作日的简单实例

    实例如下: //起始日期,/pattern/是正则表达式的界定符,pattern是要匹配的内容,只用于第一个符号的匹配,g为全局匹配标志 var beginDate = new Date("2013-01-01".replace(/-/g, "/")); //结束日期 var endDate = new Date("2013-01-31".replace(/-/g, "/")); //日期差值,即包含周六日.以天为单位的工时,

随机推荐