浅拷贝

创建了一个新的对象,这个对象有着原始对象属性值得精确拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

1
2
3
4
Object.assign(target, ...sources)
let target = {}
Object.assign(target, source)
target.a === source.a // true

判断类型的方式

typeof

typeof可以用于判断以下js的8种类型:

js的8中数据类型:

  • Boolean
  • Null
  • Undefined
  • Number
  • BigInt – 浏览器已经支持了
  • String
  • Symbol
  • Object

但也有缺点 — 暂时性死区(TDZ)

我们知道let和const 具有暂时性死区(即在代码块内,使用let/const命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”)

typeof也具有暂时性死区

1
2
typeof x; // ReferenceError -- 声明在后导致出错
let x;
1
typeof undeclared_variable // "undefined"  -- 若一个变量根本没有声明,倒不会报错
1
2
// 考烂了的面试题
typeof null === 'object' // true

instanceof

原理: 右边变量的prototype在左边变量的原型链上即可(右边构造函数的原型在不在左边实例的原型链上)

1
2
3
4
String instanceof String // false
Function instanceof Function // true
Object instanceof Object // true
Function instanceof Object // true

JS原型链神图

Object.prototype.toString()

深拷贝

将一个对象从内存中完整的拷贝出来一份,从内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

最常用的一种方式 – 业务中用(不适合于面试)

1
let d = JSON.parse(JSON.stringify(x)) // 将x深拷贝给d

基本版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function deepCopy( obj ){
if (typeof obj === 'object') {
if (obj.constructor === Array) {
var newArr = []
for (var i = 0; i < obj.length; i++) {
newArr.push(obj[i])
}
} else {
var newObj = {}
for (var key in obj) {
newObj[key] = this.deepCopy(obj[key])
}
return newObj
}
} else {
return obj
}
}

面试版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 面试版本
// 判断是不是一个复杂类型
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepCopy = function (obj, hash= new WeakMap()) {
if (hash.has(obj)) return hash.get(obj);
let type = [Date, RegExp, Set, Map, WeakMap, WeakSet];
if (type.includes(obj.constructor)) return new obj.constructor(obj);
// 如果成环了,参数obj = obj.loop = 最初的obj 会在WeakMap中找到第一次放入的obj提前返回第一次放入 WeakMap的cloneObj
let allDesc = Object.getOwnPropertyDescriptors(obj) // 遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc) // 继承原型
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) { //Reflect.ownKeys可以拷贝不可枚举属性和符号类型
// 如果值是引用类型则递归调用deepClone
cloneObj[key] =
(isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj
}
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
27
28
29
30
31
32
33
34
35
36
// 测试用例
let obj = {
bigInt: BigInt(12312),
set: new Set([2]),
map: new Map([['a', 222], ['b', 33]]),
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: {
name: '我是一个对象',
id: 1
},
arr: [1,2,3],
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
console.log(obj);
let cloneObj = deepCopy(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]);
}
}

学习自京程一灯董老师课程,如有侵权,联系删除