【前端面试】javascript中类型判断和实现深拷贝的两种方式

类型判断

JS中的变量分为值类型和引用类型

【值类型】

包括: undefinedstringNumberBooleanSymbol

这里需要注意:

  1. string在C++中属于STL容器,在python中也属于需要deepcopy的那种,但在js里它是直接以value的形式直接存在栈空间的。
#include<string>
string* a = new string("123");
a = "1231241"
b = a
print(id(a)==id(b)) #True
  1. NaN不是数据类型,它和0,1,3.14159一样都属于Number,只不过比较特殊罢了
console.log(typeof NaN) // Number

【引用类型】

包括:Objectnullfunction,Array

这里需要说明:

  1. FunctionArray实例本质上原型链最终也指向Object.prototype,属于特殊的对象。
  2. null可以理解为空对象,let a = null,则a对应栈空间中的value指向空地址。
  3. 这也就不难解释,为什么typeof 区分不出 普通ObjectnullArray了,不过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. 设置递归出口
    这里有三个细节:
    (1) !obj包含了 obj 是 nullundefined两种情况;
    (2) ‘object’ 小写,这不是Object构造函数,只是一个字符串;
    (3) typeof obj !== "object"过滤掉了所有除null之外的基本类型和Function的情况,而null在第一个!obj的时候就已经命中了;
  2. 初始化返回值:读取objconstructor构造函数,用这个构造函数来new一个实例对象newObj,这个对象就是要返回的值;
  3. 遍历obj所有的key(这里注意下for...infor...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"

可以看到成功拷贝
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值