js数据类型主要分基本数据类型和引用数据类型。前者包括Number,String等,后者主要是Object,因此以下会针对不同的数据类型来分析,需要的朋友可以参考一下
基本数据类型(Primary Data Types):
-
String(字符串)
-
Number(数字)
-
Boolean(布尔值)
-
Null(空值)
-
Undefined(未定义)
-
Symbol(符号,ES6 新增)
-
BigInt(大整数,ES2020 新增)
引用数据类型(Reference Data Types):
-
Object(对象)
-
Array(数组)
-
Function(函数)
-
Date(日期)
-
RegExp(正则表达式)
-
Map(映射)
-
Set(集合)
-
WeakMap(弱映射)
-
WeakSet(弱集合)
前言:在学习下面文章前我们简单了解一下的内存的知识,以下先简要提一下
1、js内存
js内存,或者说大部分语言的内存都分为栈和堆。基本数据类型的变量值分配在栈上,引用数据类型的变量值分配在堆上,栈中只是存储具体堆中对象的地址。
2、赋值
对于基本数据类型,赋值操作是拷贝,即新旧变量不会相互影响。
let a = 1
let b = a
b = 2
console.log(a) // 1
console.log(b) // 2
对于引用数据类型,赋值操作只是在栈中新增一个指向堆中对象的变量,即复制引用地址。新旧变量之间会互相影响,即在新变量上改变对象值,旧变量对应值也会改变。
let a = {
name: "mike"
}
let b = a
b.name = "jack"
console.log(a) // {name: "jack"}
3、浅拷贝
对于基本数据类型和不具有嵌套对象的数据,均是拷贝操作,新旧变量之间不会相互影响。
let a = {
name: "mike"
}
let b = {}
b.name = a.name
b.name = "jack"
console.log(a) // {name: "mike"}
但是对于具有嵌套对象的数据,浅拷贝只拷贝第一层对象,深层次的值仍然是复制引用地址。
let a = {
name: "mike",
language: {
first: "english",
second: "chinese"
}
}
let b = {}
b.name = a.name
b.name = "jack"
b.language = a.language
b.language.first = "japanese"
console.log(a) // {"name":"mike", language : {first: "japanese", second: "chinese"}}
js实现浅拷贝,思想:遍历target的每个属性,将起属性名和值赋值给新变量。
如果你明白了赋值的含义,那么在代码的第四行,当此时的target[key]的值是对象的时候,通过赋值赋予新变量,本质上是复制引用数据类型在堆中的地址,就不难理解为什么浅拷贝对于是否是嵌套对象的有不同结果了。
function shallowCopy(target) {
let result = {}
for (const key in target) {
result[key] = target[key]
}
return result
}
4、深拷贝
深拷贝是完完全全的拷贝,新旧变量之间不会相互影响。
对于参数是否是对象有不同的处理方法,如果是对象,对于对象的每个属性和值赋值然后递归处理; 否则直接返回。
function clone(target) {
if (typeof target === "object") {
//判断是否是数组
let result = Array.isArray(target) ? [] : {}
for (const key in target) {
result[key] = clone(target[key])
}
return result
}
else {
return target
}
}
到此这篇关于js中的赋值 浅拷贝和深拷贝详细的文章就介绍到这了
**************************************************************************************************************
补充——浅拷贝和深拷贝的更加完善的函数
// 浅拷贝
const shallowClone = target => {
// 基本数据类型直接返回
if (typeof target === 'object' && target !== null) {
// 获取target 的构造体
const constructor = target.constructor
// 如果构造体为以下几种类型直接返回
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
return target
}
// 判断是否是一个数组
const cloneTarget = Array.isArray(target) ? [] : {}
for (prop in target) {
// 只拷贝其自身的属性
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop]
}
}
return cloneTarget
} else {
return target
}
}
// 深拷贝
const completeDeepClone = (target, map = new WeakMap()) => {
// 基本数据类型,直接返回
if (typeof target !== 'object' || target === null) {
return target
}
// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = target.constructor
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
return new constructor(target)
}
// map标记每一个出现过的属性,避免循环引用
if (map.get(target)) {
return map.get(target)
}
map.set(target, true)
const cloneTarget = Array.isArray(target) ? [] : {}
for (prop in target) {
// 实现一个深拷贝函数通常需要递归地检查每个属性,如果属性值是对象,则递归调用自身进行拷贝;否则,直接复制该属性值。
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = completeDeepClone(target[prop], map)
}
}
return cloneTarget
}