基本数据类型的值和引用类型的值
1.基本数据类型的值
基本类型值指的是存储在栈中的一些简单的数据段,它是按值访问的,从一个变量复制基本类型的值到另一个变量后这两个变量的值是完全独立的,互不影响
- Number
- String
- Null
- Undefined
- Boolean
- Symbol (ES6 新增的基本数据类型,独一无二的值)
- BigInt (ES10 新增的基本数据类型,用于当整数值大于Number数据类型支持的范围时)
关于 BigInt 详细信息
2.引用类型的值
引用类型的值(对象)是引用类型的一个实例。它是保存在堆内存中的一个对象,引用类型是一种数据结构。在ECMAScript 中,引用类型是一种数据结构,用于将数据和功能组织在一起。
- Object
- Array
- Function
- Date
- RegExp
- Error
- Set
- Map
- WeakSet
- WeakMap
浅拷贝
1. Object.assign
ES6中拷贝对象的方法,接受的第一个参数是拷贝的目标,剩下的参数是拷贝的源对象(可以是多个)
语法: Object.assign(target, ...sources)
let target = {}
let sources = {
a: { aa: 1},
b: function () {},
c: null,
d: undefined,
e: NaN,
sym: Symbol(1)
}
Object.defineProperty(sources, 'innumerable', { value: '不可枚举属性', enumerable: false })
sources.a.aa = 1217
Object.assign(target, sources)
console.log('Object.assign-----sources: ', sources)
console.log('Object.assign-----target: ', target)
Object.assign() 注意点:
- 不能拷贝对象的继承属性
- 不能拷贝不可枚举的属性
- 只是在 根属性(对象的第一层)创建了一个新的对象,但是对于属性的值是对象的话只会拷贝一分相同的内存地址
- 可以拷贝 Symbol 类型
2. 扩展运算符
利用扩展运算符可以在构造字面对象时,进行克隆或者属性拷贝
语法:let cloneObj = { ...obj }
let obj1 = {
a: { aa: 1},
b: function () {},
c: null,
d: undefined,
e: NaN,
sym: Symbol(1)
}
Object.defineProperty(obj1, 'innumerable', { value: '不可枚举属性', enumerable: false })
obj1.a.aa = '1226'
let obj2 = { ...obj1 }
console.log('扩展运算符-----obj1: ', obj1)
console.log('扩展运算符-----obj2: ', obj2)
扩展运算符和 Object.assign()
有着相同的缺陷
深拷贝
将一个对象从内存中完成的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改了新对象不会影响原对象
1.通过 JSON.stringify()
实现简单的深拷贝:
let deepObj = {
a: {
aa: 1
},
b: function () {},
c: null,
d: undefined,
e: NaN,
f: [2, 3, 4, 5],
g: new Date(),
h: new RegExp(),
sym: Symbol(1)
}
Object.defineProperty(deepObj, 'innumerable', {
value: '不可枚举属性',
enumerable:false
})
let str = JSON.stringify(deepObj)
let deepCloneObj = JSON.parse(str)
console.log('JSON.stringify()-----deepObj: ' , deepObj)
console.log('JSON.stringify()-----deepCloneObj: ' , deepCloneObj)
JSON.stringify()
实现深度拷贝的注意点:
- 拷贝对象的值中如果有
函数
undefined
symbol
经过 JSON.stringify() 序列化后的 JSON 字符串中的这个键值对会消失 - 无法拷贝不可枚举的属性,无法拷贝对象原型链
- 拷贝
Date
引用类型会转变成字符串 - 拷贝
RegExp
引用类型会转变成空对象 - 对象中包含有
NaN
Infinity
-Infinity
序列化后的结果会变成null
- 无法拷贝对象的循环引用(对象成环)
(obj[key] = obj)
2.使用第三方库实现对象的深拷贝
3.本人总结的深度拷贝的方法
参考了很多关于深拷贝的博客,本人总结出了一个能够深拷贝ECMAScript的原生引用类型的方法
// 深拷贝封装
// 1. 封装函数判断 obj 的类型
const isObjectType = obj => {
return obj !== null && (typeof obj === 'object' || typeof obj === 'function')
}
const deepClone = (obj, hash = new WeakMap()) => {
// 2. 如果 obj 是日起对象就返回一个新的日期对象
if (obj.constructor === Date) return new Date(obj)
// 3. 若果 obj 是正则对象就返回一个新的正则对象
if (obj.constructor === RegExp) return new RegExp(obj)
// 4. 如果成环了,参数 obj = obj.loop = 最初的 obj 会在 weakMap 中知道到第一次放入的 obj 提前返回第一次放入的 WeakMap 的 cloneObj
if (hash.has(obj)) return hash.get(obj)
// 5. 遍历传入参数所有键的特性
let allKeyDesc = Object.getOwnPropertyDescriptors(obj)
// 6. 继承原型链
let cloneObj = Object.create(Object.getPrototypeOf(obj), allKeyDesc)
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
// Reflect.ownKeys(obj)可以拷贝不可枚举属性和符号类型
// 如果值是引用类型(非函数)则递归调用deepClone
cloneObj[key] = isObjectType(obj[key]) && typeof obj[key] !== 'function' ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
let obj = {
num: 0,
str: "",
boolean: true,
unf: undefined,
nul: null,
obj: {
name: "我是一个对象",
id: 1
},
arr: [0, 1, 2],
func: function() {
console.log("我是一个函数");
},
date: new Date(0),
reg: new RegExp("/我是一个正则/ig"),
[Symbol("1")]: 1
}
Object.defineProperty(obj, "innumerable", {
enumerable: false,
value: "不可枚举属性"
})
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj
let cloneObj = deepClone(obj)
console.log("obj", obj)
console.log("cloneObj", cloneObj)
for (let key of Object.keys(cloneObj)) {
if (
typeof cloneObj[key] === "object" ||
typeof cloneObj[key] === "function"
) {
console.log(`${key}相同吗? `, cloneObj[key] === obj[key]);
}
}