作为一名前端工作者,传递数据是我们最常见不过的操作。所以,这篇我整理了原来的学习笔记,在此做一个 ‘ 深拷贝和浅拷贝 ’ 的总结;在上方法之前,我们需要知道几个概念:
1,数据类型是什么玩意儿?
数据类型,说白了就是对数据按照某种规则(呵,我不知道)打上不同的分类标签。这样做的目的首先是方便 ‘ 程序工程狮’ 逻辑进展和纠错;其次是在计算机分配内存的时候,按照不同的类型分配空间,避免了内存的浪费;
基本数据类型:也称为 ‘ 简单数据类型 ’ ,JavaScript的基本数据类型有6种:字符串(String)、数字(Number)、布尔(Boolean)、空(null)、未定义(undefined)、symbol(ES6新定义的一种数据结构,唯一一个定义后不可更改的);
引用数据类型:也称为 ‘ 复合数据类型 ’ ,JavaScript的引用数据类型有1种;对象(Object);
这个时候,可能有人上来就给我一巴掌!胡说!还有函数和数组;
其实 ‘ 数组(Array)’、‘ 函数(Function)’、‘ 正则(RegExp)’、‘ 时间对象(Date)’、‘ 数学函数(Math)’ 都是对象的 子类别 。我们可以看做:对象是豆子,她是统称,但是豆子还分各种,比如绿豆,红豆,黄豆等等;
2,堆和栈是什么玩意儿?
对于 ‘ 堆 ’ 和 ‘ 栈 ’ 的概念,相信会一些C和Java的大佬来说,一点都不陌生。不过,俺就是小菜鸟,讲真的我还说不出来个子丑寅卯来,总结来说:‘ 堆(堆内存)和栈(栈内存)都是操作系统的内存 ’、‘ 堆是动态分配内存,内存大小不一,也不会自动释放。栈是自动分配相对固定大小的内存空间,并由系统自动释放 ’、‘ js 的基本数据类型保存在栈内存中,引用数据类型实际保存在堆内存中,栈中保存堆内存的地址指针 ’。嘿,刺激。
后来学习了一下,看到了一个令我大吃一 ‘ 斤 ’ 的事儿:JavaScript中没有堆栈的概念!
what!!! 这瞬间把握搞懵X了。我们知道,变量声明后都会被分配内存空间,如果JavaScript没有堆栈的概念,如何做到存储变量和分配空间呢?
后来,经过多方的求证,我总结了一下:JavaScript中没有 ‘ 堆和栈 ’ 的概念,但是用到了 ‘ 堆和栈’ 的概念。是不是有点拗口?解释来说,js是没有堆内存、栈内存的概念的,它有的是 ‘ 内存堆 ’ 和 ‘ 调用栈 ’ 。像我们熟知的 谷歌V8引擎 是JavaScript引擎之一,也是最流行的一个。
这个引擎主要由两部分组成:
- 内存堆:这是内存分配发生的地方
- 调用栈:这是你的代码执行时的地方
其实呢,和堆栈差不多。所以 js 的基本数据类型保存在栈内存中,引用数据类型实际保存在堆内存中,栈中保存堆内存的地址指针 也是没啥问题的。
3,浅拷贝和深拷贝是什么玩意儿?
深拷贝和浅拷贝都是针对引用数据类型而言的,基本数据类型直接存储在 ‘ 栈 ’ 中,拷贝时直接都拿来了,谈不上深浅。( 拿来吧你!)
简单来说:
浅拷贝:只拷贝一层对象属性,对象拷贝的时候只是复制了对象在 ‘ 栈 ’ 中保存的地址指针,所以我们改变了其中的一个对象的属性值,另一个也跟着改变;
var a = { name: 'lili' , age: '12'}
var b = a
a.name = 'mary'
console.log(a) // 打印:{ name: 'mary' , age: '12'}
console.log(b) // 打印:{ name: 'mary' , age: '12'}
深拷贝:递归拷贝了对象所有的层级,另外在 堆 中开一个空间,将被复制的对象的堆内存中的数据都复制过去,两个互不影响;
var a = { name: 'lili' , age: '12'}
var b = {}
for(key in a){
b[key] = a[key]
}
a.name = "mary"
console.log(a) //打印:{ name: 'mary' , age: '12'}
console.log(b) //打印:{ name: 'lili' , age: '12'}
以上是理解深拷贝和浅拷贝过程中,我们需要知道的东西,下面是 ‘ 干货 ’:
数组深拷贝方法总结:
在总结数组的深拷贝方法前,需要说明一个注意点:‘ 数组 ’ 和 ‘ 数组对象 ’ 是不一样的。
数组是一个值得集合,例如 [ 1,2,3,'a', 'b' ],它的本质是一个数组;
数组对象是对象的集合,例如[ { a:'123' , b:'234' }, { c:'345' , d:'456' } ],它的本质是一个对象,不过它是一个拥有数组方法的对象;
数组深拷贝的方法不适用于数组对象。切记!
1,递归遍历方法(这里以foreach为例)
var ary1 = ['1','2',3,'a','s','d']
var ary2 = []
ary1.forEach(item => {
ary2.push(item)
})
2,concat拼接方法
var ary1 = ['1','2',3,'a','s','d']
var ary2 = []
ary2 = [].concat(ary1)
3,slice提取方法
var ary1 = ['1','2',3,'a','s','d']
var ary2 = []
ary2 = ary1.slice(0)
4,ES6扩展运算符+解构赋值方法
var ary1 = ['1','2',3,'a','s','d']
var [...ary2] = ary1
注意:用扩展运算符对数组或者对象进行拷贝时,只能扩展和深拷贝第一层的值,对于第二层极其以后的值,扩展运算符将不能对其进行打散扩展,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。
对象深拷贝方法总结:
1,递归遍历方法
var obj1 = {
name:'aaaaa',
age:'10',
sex:'girl',
scorl:{
math:'100',
chinese:'99',
english:'98'
}
}
function deepClone(obj){
let cloneObj = Array.isArray(obj) ? [] : {}
if(obj && typeof obj == 'object'){
for( key in obj ){
if(obj.hasOwnProperty(key)){
if(obj[key] && typeof obj[key] == 'object'){
cloneObj[key] = deepClone(obj[key])
}else{
cloneObj[key] = obj[key]
}
}
}
}
return cloneObj
}
var obj2 = deepClone(obj1);
2,JSON转换方法
var obj1 = {
name:'aaaaa',
age:'10',
sex:'girl',
scorl:{
math:'100',
chinese:'99',
english:'98'
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
3,JQuery的extend()方法
var obj1 = {
name:'aaaaa',
age:'10',
sex:'girl',
scorl:{
math:'100',
chinese:'99',
english:'98'
}
}
var obj2 = $.extend(true,{},obj1); // true为深拷贝,false为浅拷贝
说明:JQuery的extend()方法可以用在数组上,数组对象上,对象上。
4,ES6扩展运算符+解构赋值方法
var obj1 = {
name:'aaaaa',
age:'10',
sex:'girl',
scorl:{
math:'100',
chinese:'99',
english:'98'
}
}
var {...obj2} = obj1
方法总结的可能不全,我脑子里觉得好像是还有个深拷贝的方法,但是一时间想不起来了。以后转角遇见或者想起来了,我随时更新文章。不过我相信已经总结的几种方法,足够现实项目中使用了。希望对大家有帮助。
拜了个拜!迪迦,,,