Table of Contents generated with DocToc (opens new window)
# 数据类型
首先数据分为基本类型数据(String,Number,Boolean,Null)和引用类型数据
- 基本数据类型的特点: 直接存储在栈(stack)中的数据
- 引用数据类型的特点: 存储的是该对象在栈中的引用,真实数据存放在堆内存中
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
# 栈内存和堆内存
JS 中的栈内存和堆内存 (opens new window)
# 栈内存
栈内存用来存放基本数据类型(Number、String、Boolean、Null 和 Undefined),在执行完之后销毁。
==先进后出,后进先出==
# 堆内存
堆内存用来存放引用数据类型(object、array、function、date),在没有被引用之后销毁。
当我们声明和定义了一个引用数据类型之后,这个对象保存在堆内存中,而这个对象的地址则保存在栈内存中以用于引用。
在全局作用域声明和定义的引用数据类型,销毁的方法是手动赋值 null。
# 深拷贝和浅拷贝
深拷贝和浅拷贝是只针对 Object 和 Array 这样的引用数据类型的。
- 浅拷贝:拷贝的是地址,最终两个变量指向同一份数据,修改其中一个变量会改变另一个
- 深拷贝:两个变量是独立的,互不影响
实现深拷贝
-
function deepClone(source) { // if source not an object if (typeof source != 'object') return source // if source an array const target = myToString(source) === '[object Array]' ? [] : {} for (const key in source) { // if (source.hasOwnProperty(key)) { //不使用source.hasOwnProperty(key)是为了确保能够调用到正确的方法,防止this指向问题 if (Object.prototype.hasOwnProperty.call(source, key)) { // 如果还有子属性 if ( myToString(source[key]) === '[object Object]' || myToString(source[key]) === '[object Array]' ) { target[key] = deepClone(source[key]) } else { target[key] = source[key] } } } return target } function myToString(variable) { return Object.prototype.toString.call(variable) }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 JSON.stringify()和 JSON.parse()实现
缺点:RegExp 对象无法拷贝
/** * 深拷贝-json方式 */ function deepClone(obj) { let tmp = JSON.stringify(obj) let result = JSON.parse(tmp) return result }1
2
3
4
5
6
7
8Object.create()
Object.create()创建一个新对象,并将新对象的__proto__属性指向原有对象。
function deepClone(obj) { const target = {} for (const key in obj) { // if (obj.hasOwnProperty(key)) { //不使用obj.hasOwnProperty(key)是为了确保能够调用到正确的方法,防止this指向问题 if (Object.prototype.hasOwnProperty.call(obj, key)) { // 如果还有子属性 if (typeof obj[key] === 'object') { target[key] = deepClone(obj[key]) } else { target[key] = obj[key] } } } return target } const obj = { name: 'zs', age: 18, children: { name: 'wx', age: 20 }, } const newObj = deepClone(obj) obj.name = 'ls' obj.children.name = 'ss' console.log('obj: ', obj) console.log('newObj: ', newObj)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27Object.assign(target,...source)
注意: Object.assign 是第一层深拷贝,第二层如果还是复杂类型(对象)浅拷贝
const obj1 = { name: 'zs', age: 18, children: { name: 'wx', age: 20 }, } var obj2 = Object.assign({}, obj1) obj1.name = 'ls' obj1.children.name = 'wx' console.log(obj1) //{ name: 'ls', age: 18, children: { name: 'wx', age: 20 } } console.log(obj2) //{ name: 'zs', age: 18, children: { name: 'wx', age: 20 } }1
2
3
4
5
6
7
8
9
10
# structuredClone
Window (opens new window) 接口的 structuredClone() 方法使用结构化克隆算法 (opens new window)将给定的值进行深拷贝 (opens new window)。
兼容性:Chrome98
structuredClone不能克隆的格式:
- Proxy(很常见:你的数据很可能来自 Vue 的 ref/reactive)
- undefined 的 getter/setter
- function
- DOM 节点
- WebSocket、File 等浏览器对象
- 循环引用
对于vue中的proxy对象可以使用toRaw先转成原生对象
作用域与闭包 →