类型判断
JS中的变量分为值类型和引用类型
【值类型】
包括: undefined
,string
,Number
,Boolean
,Symbol
这里需要注意:
string
在C++中属于STL容器,在python中也属于需要deepcopy的那种,但在js里它是直接以value的形式直接存在栈空间的。
#include<string>
string* a = new string("123");
a = "1231241"
b = a
print(id(a)==id(b)) #True
NaN
不是数据类型,它和0
,1
,3.14159
一样都属于Number
,只不过比较特殊罢了
console.log(typeof NaN) // Number
【引用类型】
包括:Object
,null
,function
,Array
这里需要说明:
Function
和Array
实例本质上原型链最终也指向Object.prototype
,属于特殊的对象。null
可以理解为空对象,let a = null,则a对应栈空间中的value指向空地址。- 这也就不难解释,为什么typeof 区分不出 普通
Object
,null
和Array
了,不过typeof 会将function
类型识别出来(需要特殊记忆一下)。
typeof [] //object
typeof {} //object
typeof null //objcet
typeof console.log //function
typeof function(){} //function
如果要判断Object null 和 Array ,则需要 instanceof
来看变量究竟是谁的实例了。
深拷贝
众所周知,js里的赋值语句是浅拷贝:
如果赋的是值类型,则两个变量相互不影响:
const a = 100
const b = a
a = 200
console.log(b) //还是100
如果赋的值是引用类型,则两个变量会相互影响:
const a = {
age:20
}
const b = a
a.age = 21
console.log(b.age); // 变成21
可以看到,上面这个例子,a的age变了,b的也跟着变了,原因是本质上a和b两个变量指向的都是堆空间的同一个地址。如果要实现a 和 b 独立,则需要在堆空间重新开辟一个对象,也就是深拷贝:
案例:假设我要拷贝这个在北京住了20年,在上海工作了4年的叫张三的对象
const obj1 = {
name: "zhangsan",
age: 23,
address: {
home: {
city: "beijing",
year: 20
},
work: {
city: "shanghai",
year: 4
}
},
arr: [1, 2, 3]
}
用JSON实现深拷贝
最简单的方式,把这个对象变成JSON字符串,再转回对象。
const obj2 = JSON.parse(JSON.stringify(obj1))
这个方法的问题在于,无法拷贝
通过递归实现深拷贝
自己写一个deepClone函数。分为三个步骤:
- 设置递归出口
这里有三个细节:
(1)!obj
包含了 obj 是null
或undefined
两种情况;
(2) ‘object’ 小写,这不是Object构造函数,只是一个字符串;
(3)typeof obj !== "object"
过滤掉了所有除null
之外的基本类型和Function
的情况,而null
在第一个!obj
的时候就已经命中了; - 初始化返回值:读取
obj
的constructor
构造函数,用这个构造函数来new
一个实例对象newObj
,这个对象就是要返回的值; - 遍历
obj
所有的key
(这里注意下for...in
和for...of
的区别),递归拷贝。
注意这里(obj.hasOwnProperty(key)
过滤了原型链上的属性,因为这一块的东西在new constructor()
的时候已经指向了obj.__proto__
,况且原型链上的东西应该是子类共享的,不应该再去拷贝一份)
function deepClone(obj = {}) {
if (!obj || typeof obj !== "object") {
return obj //递归出口
}
const constructor = obj.constructor
const newObj = new constructor()
for (let key in obj) {
if (obj.hasOwnProperty(key)) { //防止拷贝原型链上的属性
//递归拷贝
newObj[key] = deepClone(obj[key])
}
}
return newObj
}
测试,我这里自定义了一个继承类,来查看deepClone
的效果:
class Person{
constructor(name, age) {
this.name = name
this.age = age
}
getName(){
return this.name
}
static sayName(){
console.log(this.name)
}
}
class Student extends Person{
constructor(name, age, score) {
super(name, age)
this.score = score
}
getName() {
console.log('student', this.name)
}
}
然后创建一个测试用例:
// 要被克隆的对象
const container = []
// 添加一些Student实例
for(let i = 0; i<5; ++i) {
container.push(new Student(`No${i}`,10+i,100))
}
// 添加一个Object对象
container.push({
name: "zhangsan",
age: 23,
address: {
home: {
city: "beijing",
year: 20
},
work: {
city: "shanghai",
year: 4
}
},
arr: [1, 2, 3],
fun: function(){
console.log("I'm a function")
}
})
执行一下测试
const container2 = deepClone(container)
container2[container2.length -1 ].fun() // "I'm a function"
可以看到成功拷贝