简介

image.png
js中数据类型:number string boolean undefined object function symbol

根据各个类型的特征,我们会将这些类型分为两大类:基本类型,引用类型
基本类型:number string boolean undefined Symbol null
引用类型: Array Object Function

基本类型和引用类型的区别

1. 存储方式不同

基本类型存储在:栈
引用数据存储在:堆
引用类型:将值存储在堆中,将堆中存储数据的内存地址,存储在栈中
image.png

2. 赋值的时候操作不同

基本类型: 将变量a赋值给变量b,是将变量b的值,复制了一份,放在b栈空间中
例:

 let a = 1 
    let b = a
    // !将a拷贝一份赋值给b
      a = 2  // 将栈中的a的数据数据修改
    console.log(b); // 1

image.png
基本类型赋值后,改变其中的一个值,另一个值没有被重新赋值,所以另一个的值是不变的
引用类型 将变量arr的值赋值给变量brr,将变量arr在栈中存储的内存地址,复制了一份放在变量brr的栈内存中
例:

let arr = [1,2,3]
    let brr = arr 
    arr = [4,5,6]
    console.log(brr); // 1,2,3
    arr[0] = 9
    console.log(brr); // 9,2,3

image.png
image.png
两个变量共享一个堆内存,只要一个变量将其中的数据改变了,另一个变量也就改变了

3. 比较方式不同

基本类型

  • == 等于判断值是否相同,不比较类型
  • === 全等先判断类型是否相同,如果类型相同,才会判断其中的值是否相等
    例:

     let a = 1 
      let b = 1 
      console.log(a == b ); // true 相等:等价值
      console.log(a === b); // true 全等:完全相同
    
      let n = 1 
      let i = '1'
      console.log(n == i); // true 
      console.log(n === i); // false 

    引用类型

  • == 等于判断栈中的内存地址是否相同
  • === 全等先判断类型,类型形同在判断栈中的内存地址的值是否相等

     let arr = [1,2,3]
      let brr = [1,2,3]
      console.log(arr == brr); //false 内存地址不同
      console.log(arr === brr); //false

    深拷贝浅拷贝

    基本类型太简单,没有深和浅的区别,所以深拷贝和浅拷贝针对的都是引用类型
    拷贝的原因:在某些操作中,我们需要一个和原数组一模一样的数据,但希望原数据和新数据完全没有关联

    浅拷贝

    原数据和新数据不共用一个堆地址,但其中包含的引用类型数据还会共享一个地址
    例:

JavaScript 数组浅拷贝分方法有

扩展运算符

const arr = ['张三','李四','王五']
    const brr = [ ...arr ]
    console.log(arr === brr); // false
    console.log(arr,brr);
    console.log(arr[1] === brr[1]); // true

slice()

const arr = ['张三','李四','王五']
    const brr = arr.slice(0)
    console.log(arr === brr); // false
    console.log(arr,brr);
    console.log(arr[1] === brr[1]); // true

concat()

const arr = ['张三','李四','王五']
    const brr = arr.concat()
    console.log(arr === brr); // false
    console.log(arr,brr);
    console.log(arr[1] === brr[1]); // true

手动进行遍历

const arr = ['张三','李四','王五']
    const brr = []
    for(let a in arr){
        brr.push(arr[a])
    }
    console.log(arr === brr); // false
    console.log(arr,brr);
    console.log(arr[1] === brr[1]); // true

Object.assign

const arr = ['张三','李四','王五']
    const brr = Object.assign([],arr)
    console.log(arr === brr); // false
    console.log(arr,brr);
    console.log(arr[1] === brr[1]); // true

对象浅拷贝

手动遍历放在新对象中

let obj = {
    name: '张三',
    age: 12,
    children: ['大毛', '二毛', '小明']
}

// 手动遍历
let pbj = {}
for(let key in obj) {
    pbj[key] = obj[key]
}
console.log(pbj, obj);
console.log(pbj === obj); // false
console.log( obj.children === pbj.children );

扩展运算

let obj = {
    name: '张三',
    age: 12,
    children: ['大毛', '二毛', '小明']
}
// 扩展运算
let pbj = {...obj}
console.log(pbj, obj);
console.log(pbj === obj);
console.log(pbj.children === obj.children);

assign方法

let obj = {
    name: '张三',
    age: 12,
    children: ['大毛', '二毛', '小明']
}
// assign方法
let pbj = Object.assign({}, obj)
console.log(pbj, obj);
console.log(pbj === obj);
console.log(pbj.children === obj.children);

深拷贝

深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即 发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

利用json转换

//但是这种方式存在弊端,会忽略undefined、symbol和函数
const obj = {
    name: '张三',
    name1: undefined,
    name3: function() {},
    name4:  Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}

手动封装递归函数实现深拷贝

function deepCopy(data) {
    // 定义最终返回的数据,初始值为null(后面需要根据原数据的类型,决定这个数据的类型)
    let newData = null
    // 判断如果原数据是数组
    if (Object.prototype.toString.call(data) === '[object Array]') {
        newData = [] // 最终返回的数据也应该是数组
    } else if (Object.prototype.toString.call(data) === '[object Object]') { // 原数据是对象
        newData = {} // 最终返回的数据也应该是对象
    } else {
        // 原数据既不是数组也不是对象 ===> 将原数据返回出来就是一种数据的拷贝
        return data
    }
    // 遍历原数据
    for (let key in data) {
        if (typeof data[key] === 'object') { // 判断其中包含的数据的类型是否是数组或对象
            // 调用递归函数,处理包含的数组或对象,得到一个新的数据,放在最终返回的数据中
            newData[key] = deepCopy(data[key])
        } else {
            // 遍历出来的是一个基本数据,就直接放在最终返回的数据中
            newData[key] = data[key]
        }
    }
    // 将最终返回的数据返回出去
    return newData
}

小结
前提为拷贝类型为引用类型的情况下:

  • 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
  • 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址

道友
1 声望0 粉丝