JS基本数据类型和引用数据类型的区别及深浅拷贝
前言
首先我们先来了解一下什么叫栈堆,基本数据类型与引用数据类型
1.栈(stack)和堆(heap)stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小也不一定会自动释放。
2.基本的数据类型:String, Number, boolean, Null, Undefined,Symbol(ES6新增)
特点: 存储的是该对象的实际数据,(存放在栈中)
3.对象数据类型(也称为引用数据类型):Array,Object,Function
特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里,(存放在堆内存中的对象,每个空间大小不一样,要根据情况进行特定的配置)
注:在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象
1、区别: 浅拷贝/深度拷贝
判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
2、常用的拷贝技术
1). arr.concat(): 数组浅拷贝
2). arr.slice(): 数组浅拷贝
3).Object.assign()对象浅拷贝
4). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
5). 浅拷贝包含函数数据的对象/数组
6). 深拷贝包含函数数据的对象/数组
1.浅拷贝
在ES6中,Object对象新增了一个assign方法,可以实现对象的浅复制。这里谈谈Object.assign方法的具体用法,因为稍后会分析jQuery的extend方法,实现的原理同Object.assign方法差不多。
对于基本数据类型来说,复制一个变量值,本质上就是copy了这个变量。一个变量值的修改,不会影响到另外一个变量。
<script type="text/javascript">
/*1.直接赋值给一个变量*/
let obj = {username:'genius'};
console.log(obj); //genius
//拷贝数组/对象 没有生成新的数据而是复制了一份引用。
let arr = [1,4,{username:'lucas',age:23}];
let arr2 = arr;
arr2[0] = 'abcd0';
console.log(arr,arr2); //俩个输出结果相同 /*Object.assign*/
let colors=['red','green','blue'];
let colors2=Object.assign([],colors);
colors2[0]="orange";
console.log(colors2);//['orange','green','blue']
console.log(colors);//['red','green','blue'] /*3.concat方式*/
let arr = [1,3,{username:'lucas'}];
let testArr = [2,4];
let arr2 = arr.concat(testArr); //连接函数
//arr2[2] = {username:'tiantian'};
console.log(arr2); //输出如下:数组中有对象
// 输出数组:
// 0:1
// 1:3
// 2:{username:'lucas'} //这个值会随下面的更改而变
// 3:2
// 4:4 /*4.slice方式*/
console.log('-------------------');
arr2[2].username = 'fengdudu'; //修改了arr的值,因为arr2[2]等于arr[2]
console.log(arr); //和922行输出结果一样,只是下标2的内容改变了
let arr3 = arr.slice(); //有startindex,endindex选项
arr3[2].username = 'HHHHH'; //修改了arr的值,因为arr2[2]等于arr[2]
console.log(arr); //和922行输出结果一样,只是下标2的内容改变了 /*5.深度克隆*/
console.log('===================');
let arr4 = JSON.parse(JSON.stringify(arr));
console.log(arr4); //只拷贝了arr并没有testArr的数组,所以只有下标3长度
arr4[2].username = 'duncan';
console.log(arr,arr4);
arr[2].username='MM'; //修改了arr、arr2、arr3的下标2的值。
//JSON.stringify要求你放入的是原生的JS对象或数组,不能放数组。
//JSON.stringify(arr)执行后arr的数据已经是JSON 字符串了,然后parse又转字符串,最终拿到一个JSON字符串,然后转换为一个对应的JS数组。
console.log('-------------------');
</script>
注:只会对只是一级属性复制,比浅拷贝多深拷贝了一层而已,所以还是无法达到深度克隆的目的.
2.深拷贝
对于复杂数据类型来说,同基本数据类型实现的不太相同。对于复杂数据类型的复制,要注意的是,变量名只是指向这个对象的指针。当我们将保存对象的一个变量赋值给另一个变量时,实际上复制的是这个指针,而两个变量都指向都一个对象。因此,一个对象的修改,会影响到另外一个对象。
在实际的开发项目中,前后端进行数据传输,主要是通过JSON实现的。
JSON对象下有两个方法,一是将JS对象转换成字符串对象的JSON.stringify方法;一个是将字符串对象转换成JS对象的JSON.parse方法。
这两个方法结合使用可以实现对象的深复制。也就是说,当我们需要复制一个obj对象时,可以先调用JSON.stringify(obj),将其转换为字符串对象,然后再调用JSON.parse方法,将其转换为JS对象。就可以轻松的实现对象的深复制。
例1
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4];
b=deepClone(a); a[0]=1;
a[2][0]=1;
console.log(a,b);
效果如下:
例2
<script type="text/javascript">
/*
思考:
如何实现深度拷贝(克隆)
拷贝的数据里有对象/数组
拷贝的数据里不能有对象/数组,即使有对象/数组可以继续遍历对象、数组拿到里边每一项值,一直拿到是基本数据类型,然后再去复制,就是深度拷贝。
*/ //知识点储备
/*
如何判断数据类型:arr-Array null -Null
1.typeof返回的数据类型有:String,Number,Boolean,Undefined,Object,Function。
2.Object.prototype.toString.call(obj)。
*/ let result = 'abcd';
result = null;
result = [1,3];
console.log(Object.prototype.toString.call(result).slice(8,-1)); //[object Array],sclice截取字符串后:Array(拿到分类)。
//console.log(typeof Object.prototype.toString.call(result)); //string //for in 循环对象(属性名)、数组(下标),推荐在循环对象属性的时候,使用for...in,在遍历数组的时候的时候使用for...of。
//for in 循环遍历对象属性名
let obj = {username:'zhangsan',age:22};
for(let i in obj){
console.log(i); //username age
} //for in 循环遍历数组下标
let arr = [1,3,'abc'];
for(let i in arr){ //数组的可以用for of对应数组值
console.log(i); //0 1 2
} //定义检测数据类型的功能函数
function checkedType(target){
return Object.prototype.toString.call(target).slice(8,-1);
}
console.log(checkedType(result)); //Array //实现深度克隆--对象/数组
function clone(target){
//判断拷贝的数据类型
//初始化变量的result 成为最终克隆的数据
let result,targetType = checkedType(target);
if(targetType === 'Object'){
result = {};
}else if(targetType === 'Array'){
result = [];
}else{
return target; //如果是基本数据类型:(String, Number, boolean, Null, Undefined)就直接反回去。
} //遍历目标数据
for(let i in target){
//获取遍历数据结构的每一项值。
let value = target[i]; //i=1,i=2,i=..
//判断目标结构里的每一值是否存在对象/数组
if(checkedType(value) === 'Object' || checkedType(value) === 'Array'){ //如果对象OR数组里嵌套了对象/数组
//继续遍历获取到的value值
result[i] = clone(value); //这个只执行一次,数组里只有一个对象
}else{ //获取到的value值是基本的数据类型或者是函数。
result[i] = value; //因为arr3数组的下标0和1都是Number类型,只有下标2才是Object(转去执行1046行)
}
}
return result;
}
let arr3 = [1,2,{username:'dudu',age:32}];
let arr4 = clone(arr3); //相当于复制了一份arr3的基本数据
console.log(arr4);
arr4[2].username = 'gate';
arr4[2].age = 65;
console.log(arr3,arr4); //arr3下标2是{username:'dudu':age:32},arr4下标2是{username:gate,age:65}
</script>
输出如下:
总结基本数据类型和引用数据类型区别
1、声明变量时内存分配不同
*原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值
*引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。
这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响
2、不同的内存分配带来不同的访问机制
在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。
而原始类型的值则是可以直接访问到的。
3、复制变量时的不同
1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。
2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,
部分参考资料:JS基本数据类型和引用数据类型的区别
JS基本数据类型和引用数据类型的区别及深浅拷贝的更多相关文章
- JS基本数据类型和引用数据类型区别
1.栈(stack)和堆(heap) stack为自动分配的内存空间,它由系统自动释放:而heap则是动态分配的内存,大小也不一定会自动释放 2.数据类型 JS分两种数据类型: 基本数据类型:Numb ...
- js中基本数据类型和引用数据类型的区别
1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象. 当我们把变量赋值给一个变 ...
- js原始数据类型和引用数据类型=>callback数据传输原理
摘要:js的数据类型有种划分方式为 原始数据类型和 引用数据类型. 原始数据类型 存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置.栈区包括了 变量的标识符和变量的值. ...
- Object 对象(对象的分类、属性(属性名和属性值)、基本数据类型与引用数据类型区别)
Object——引用数据类型 基本数据类型的不足之处:基本数据类型是单一的值,不能表现出值与值之间的所属关系 object分为内建对象.宿主对象和自定义对象 a 内建对象:ES标准中定义的对象,在任何 ...
- JavaScript中基本数据类型和引用数据类型的区别(栈——堆)
JavaScript中基本数据类型和引用数据类型的区别 1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本数据类型指的是简单的数据段,引用数据 ...
- js 原始数据类型、引用数据类型
js的数据类型划分方式为 原始数据类型和 引用数据类型 栈: 原始数据类型(Undefined,Null,Boolean,Number.String) 堆: 引用数据类型(对象.数组.函数) 两种类型 ...
- JAVA-基本数据类型与引用数据类型区别
package com.liu.u6.copy1; /* * 基本数据类型与引用数据类型有什么区别 */ public class Sjlx { public int age; } package c ...
- int与integer的区别(基本数据类型与引用数据类型)
一.先说说int与integer的区别 int 是基本数据类型,默认值为0,不需要进行实例化 integer 是引用数据类型,是int的封装类型,默认值为null,创建该类型需要进行实例化. 基本数据 ...
- JavaScript中基本数据类型和引用数据类型的区别
1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象. 当我们把变量赋值给一个变 ...
随机推荐
- MySQL 数据库出现导入xls数据出现1062主从错误错误问题解决方案
今天把xls数据表导入MySQL数据库时发现出现1062错误 ,并且有20-700条数据一直导入不了所以开始找解决方案. 解决方案1: 数据库表设计问题导致相同字段的重复数据不能导入 解 ...
- Jmeter 之 逻辑控制器 if 控制器
最近工作不忙,利用空闲时间整理了下Jmeter的相关知识,下面给大家分享下Jmeter中 如果(if)控制的使用和应用. 如下图:线程组 > 添加 > 逻辑控制器 > 如果 (if) ...
- CentSO7.6下部署Maridb Galera Cluster 实践记录(二)
早上三个节点的数据库都启动正常,下午上班就都不行了,哎,VM啊,中午就是让主机休息了一些而已么. 今天继续折腾中,第二天再来一遍:重启第一台服务器上的galera时竟然报错了:错误如下: It ...
- mariadb报:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111 "Connection refused")
我这边移除了mysql.sock文件后,重启服务就成功了. 还有一种情况,就是加入galera后,可能是server.cnf配置信息出了问题导致的,修改后,重新运行galera即可,数据库就可以启动成 ...
- linux下使用yum安装新版php7.0
这两天又装了一下虚拟机,又要编译lnmp,还要弄各种拓展,很麻烦,能不能直接yum安装呢?答案是可以的! 1.首先要更新yum源,不然是默认的老版本,一般都在5.6及以下,但是php7都出来好久了,性 ...
- 关于jstl和web.xml之间的版本问题
jstl的版本1.2对应web.xml3.1版本 jstl的版本1.1对应web.xml2.3版本 (IDEA中默认创建的是2.3的web.xml,最好换成3.1版本的) web.xml模板: < ...
- SSM框架学习笔记(一)
Spring框架 Spring :是一个开源框架,起初是为解决企业应用开发的复杂性而创建的,但是现在已经不止 企业级应用开发,Spring的核心就是提供了一个轻量级的控制反转和面向切面编程. SPri ...
- flume learning---Flume 集群搭建
在flume搭建集群模式时,首先需要进入conf目录, 1.cp flume-env.sh.template flume-env.sh 2.cp flume-conf.properties.templ ...
- ScrollView内嵌ViewPager导致ViewPager滑动困难问题
转自:http://titanseason.iteye.com/blog/1858874 解决方式:重写ScrollView,然后在xml中定义布局的时候,使用自定义的PagerScrollView而 ...
- 【学习笔记】python3核心技术与实践--如何逐步突破,成为python高手
众所周知,Facebook 的主流语言是 Hack(PHP 的进化版本).不过,我敢拍着胸脯说,就刚入职的工程师而言,100 个里至少有 95 个,以前都从未用过 Hack 或者 PHP.但是,这些人 ...