JavaScript内存机制,浅拷贝和深拷贝
JavaScript 内存机制
内存分配:
JavaScript在定义变量时就自动完成了内存分配:
//给数值变量分配内存
var num = 123
//给字符串分配内存
var str = "abc"
//给对象及其包含的值分配内存
var obj = {
a: 1,
b: null
}
//给数组及其包含的值分配内存(就像对象一样)
var ary = [1, null, "abc"]
//给函数(可调用的对象)分配内存
function fn(a){
return a
}
//函数表达式也能分配一个对象
element.addEventListener('click', function(){
element.style.color = 'pink'
}, false)
有些函数调用结果是分配对象内存:
//分配一个Date对象
var date = new Date()
//分配一个DOM元素
var ele = document.createElement('div')
有些方法分配新变量或者新对象:
//str2是一个新的字符串,因为字符串是不变量,JavaScript 可能决定不分配内存,只是存储了 [0-3] 的范围
var str1 = "abc"
var str2 = str1.substr(0, 3)
//新数组ary3有四个元素,是ary1连接ary2的结果
var ary1 = ["ab", "cd"]
var ary2 = ["ef", "gh"]
var ary3 = ary1.concat(ary2)
内存模型:
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)
其中栈存放变量,堆存放复杂对象,池存放常量 基础数据类型的变量存储在栈中,引用数据类型的变量存储在堆中,引用数据类型的地址也存储在栈中 访问基础数据类型变量时,直接从栈中读取值 访问引用数据类型变量时,先从栈中读取引用数据类型指向的地址,再根据地址到堆中读取数据
基础数据类型和栈内存:
js基础数据类型(简单数据类型):String,Number,Boolean,Null,Undefined
基础数据类型的变量指向的是栈内存中的值当声明一个变量a时,会在栈内存里面分配一块存放变量a存储的值的内存,当变量a存储的值发生改变时,栈内存里面对应的a存储值的内存里的值也会发生相应改变,如果又声明了一个变量b,并把变量a的值赋值给变量b,此时在栈内存里面会新分配一块存放变量b存储的值的内存,a和b两个变量分别对应两块存储的值相同的栈内存,且两块栈内存互不影响
//将变量a的值赋值给变量b
var a = 1
var b = a
console.log(a) //1
console.log(b) //1
//改变变量a的值,变量b的值不受影响
a = 2
console.log(a) //2
console.log(b) //1
//改变变量b的值,变量a的值不受影响
b = 3
console.log(a) //2
console.log(b) //3
引用数据类型和堆内存:
js引用数据类型(复杂数据类型):Object (Array,Date,Function …)
引用数据类型指向的是其在栈内存中的地址,通过这个地址,指向对应堆内存中的数据当声明一个对象变量obj时,会在栈内存中分配一块存放obj地址的内存,堆内存中会分配一块内存存放地址指向的obj的数据,如果将obj赋值给另一个对象时,其实是把这个对象在栈内存中的地址传递给了另一个对象,他们共享地址指向的堆内存中的同一块内存和里面存放的数据,如果改变其中一个对象的一个属性,其对应的堆内存中的数据就会被改变,所以另一个对象中的属性也会被改变,如果对其中一个对象重新赋值,这个对象在栈内存中的地址就会指向另一块堆内存, 不再与另一个对象共享同一块堆内存,且两块堆内存互不影响
//将obj1的值赋值给obj2
var obj1 = {
a: 1,
b: 2
}
var obj2 = obj1
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
//改变obj1的属性值,obj2发生相应改变
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:3, b:2}
//将obj1重新赋值,obj2的值不受影响
obj1 = {
a: 3,
b: 4
}
console.log(obj1) //{a:3, b:4}
console.log(obj2) //{a:3, b:2}
内存生命周期:
-
分配内存:当申明变量、对象、函数的时候,系统会自动完成内存的分配 -
使用内存:使用变量、对象、函数等分配到的内存(读,写) -
回收内存:使用完毕,不需要时将内存释放归还
浅拷贝 和 深拷贝
浅拷贝:
浅拷贝
可以理解为:在栈内存中的拷贝行为,只能拷贝栈内存中的基本值和引用地址
-
浅拷贝只是拷贝栈内存中的引用地址,两个变量共用同一堆内存中的数据
浅拷贝的实现:
-
直接使用
=
实现引用地址赋值,实现浅拷贝var obj1 = {
a: 1,
b: 2
}
var obj2 = obj1
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:3, b:2} -
使用
Object.assign()
实现浅拷贝,该方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,返回目标对象var obj1 = {
a: 1,
b: 2
}
var obj2 = Object.assign(obj1)
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:3, b:2}
深拷贝:
深拷贝
可以理解为:同时在栈内存和堆内存中的拷贝行为,不仅拷贝栈内存中的基本值和引用地址,引用地址指向的堆内存中的数据也会被拷贝
-
深拷贝拷贝栈内存中的引用地址,也拷贝堆内存中的数据,两个变量拥有不同堆内存中的数据
深拷贝的实现:
-
通过对象属性直接赋值(或者使用扩展运算符),实现深拷贝
var obj1 = {
a: 1,
b: 2
}
var obj2 = {
a: obj1.a,
b: obj1.b
}
//或者使用扩展运算符:var obj2 = { ...obj1 }
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:1, b:2} -
使用
JSON.parse()
和JSON.stringify()
实现深拷贝,缺点为:无法实现对象中方法的深拷贝var obj1 = {
a: 1,
b: 2
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:1, b:2} -
使用 jQuery 的
$.extend()
方法实现深拷贝var obj1 = {
a: 1,
b: 2
}
var obj2 = $.extend(true, {}, obj1) //true为深拷贝,false为浅拷贝
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:1, b:2} -
使用 Lodash.js 的
_.cloneDeep
方法实现深拷贝var obj1 = {
a: 1,
b: 2
}
var obj2 = _.cloneDeep(obj1)
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:1, b:2} -
使用数组的
slice
或者concat
方法实现对数组的深拷贝(slice 和 concat 方法都会返回一个新数组)var arr1 = ["a","b","c"]
var arr2 = arr1.slice(0)
//或者:var arr2 = arr1.concat()
console.log(arr1) //["a","b","c"]
console.log(arr2) //["a","b","c"]
arr1[0] = "d"
console.log(arr1) //["d","b","c"]
console.log(arr2) //["a","b","c"] -
使用
递归
函数实现深拷贝(递归实现深层级属性拷贝)//封装深拷贝函数
function deepCopy(obj){
//判断深拷贝对象的数据类型
if(typeof obj === "object" && obj !== null) {
//判断深拷贝对象是数组还是对象
var target = obj instanceof Array ? [] : {}
for(var key in obj) {
//递归实现深层级属性拷贝
target[key] = deepCopy(obj[key])
}
return target
}else {
//基础数据类型直接返回
return obj
}
}
//对象
var obj1 = {
a: 1,
b: 2
}
var obj2 = deepCopy(obj1)
console.log(obj1) //{a:1, b:2}
console.log(obj2) //{a:1, b:2}
obj1.a = 3
console.log(obj1) //{a:3, b:2}
console.log(obj2) //{a:1, b:2}
//数组
var arr1 = ["a","b","c"]
var arr2 = deepCopy(arr1)
console.log(arr1) //["a","b","c"]
console.log(arr2) //["a","b","c"]
arr1[0] = "d"
console.log(arr1) //["d","b","c"]
console.log(arr2) //["a","b","c"]
原文始发于微信公众号(前端24):JavaScript内存机制,浅拷贝和深拷贝
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/217190.html