前端都应该懂的:js的数据类型,存储方式,浅拷贝/深拷贝

102 阅读6分钟

1.js数据类型

(1)基本数据类型
  1. Number:用于表示整数和浮点数。例如:42,3.14159。
  2. String:用于表示文本数据或字符序列。例如:'Hello, world!'。
  3. Boolean:有两个值,true 和 false,用于逻辑运算。
  4. Null:有一个值 null,表示一个空值或“无”的值。
  5. Undefined:有一个值 undefined,表示未定义。
  6. Symbol(ES6 新增):表示独一无二且不可变的数据类型,经常用于对象属性的键。
  7. BigInt(ES2020 新增):用于表示大于 2^53 - 1 的整数。
(2)复杂数据类型
  1. Object:最复杂的数据类型,可以包含各种数据类型的值、方法和其他对象。
  2. Array:一种特殊的对象,用于表示有序的元素集合。
  3. Function:可执行的代码块,也称为方法或函数。
  4. Date:用于表示日期和时间。
  5. RegExp:表示正则表达式,用于模式匹配和“搜索-替换”文本操作。

2.存储方式

JavaScript 的数据类型在内存中的存储方式主要取决于其类型:

  • 基本数据类型:直接存储在栈(Stack)内存中,它们的值直接存储在其自身的内存空间内。由于它们的值是不可变的,因此不需要额外的内存空间来存储指针或引用。

截屏2024-03-02 14.43.06.png

var a=1
var str="123"
  • 复杂数据类型:通常存储在堆(Heap)内存中。当我们在代码中创建一个对象时,JavaScript 会在堆内存中为这个对象分配空间,并返回一个指向该对象的指针(或引用)给栈内存。这意味着我们实际上是在操作对象的引用,而不是对象本身。当对象的引用被重新赋值时,原来的对象如果没有其他引用指向它,就会被垃圾回收机制(Garbage Collection)自动回收。

截屏2024-03-02 14.44.45.png

var user={
  name:'张三',
  age:18
}
var arr=[1,2,'456']

3.浅拷贝/深拷贝

(1)浅拷贝:
  • 浅拷贝是指创建一个新对象,并将原对象中的非静态字段复制到新对象中。
  • 如果字段是值类型,则对该字段执行逐位复制。
  • 如果字段是引用类型,则复制引用但不复制引用的对象。因此,原对象及其副本引用同一个对象。
  • 浅拷贝是一种相对较快且内存效率较高的操作,因为它不需要复制引用的对象。
  • 在浅拷贝中,对原对象所做的更改可能会影响副本,反之亦然,因为它们引用的是同一个对象。
  • 我们可以使用多种方法来实现浅拷贝,例如使用对象字面量、Object.assign()方法或数组的slice()方法。
let originalObject = {  
  name: 'Alice',  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  }  
};  
  
let copiedObject = { ...originalObject };  
  
console.log(copiedObject); // { name: 'Alice', address: { street: '123 Main St', city: 'Springfield' } }  
  
// 修改原对象的属性,浅拷贝的对象不会受到影响  
originalObject.name = 'Bob';  
console.log(copiedObject); // { name: 'Alice', address: { street: '123 Main St', city: 'Springfield' } }  
  
// 修改原对象内部引用对象的属性,浅拷贝的对象也会受到影响  
originalObject.address.street = '456 Elm St';  
console.log(copiedObject); // { name: 'Alice', address: { street: '456 Elm St', city: 'Springfield' } }

截屏2024-03-02 14.47.20.png

(2)深拷贝:
  • 深拷贝是指创建一个新对象,并将原对象中的所有字段复制到新对象中。
  • 如果字段是值类型,则对该字段执行逐位复制。
  • 如果字段是引用类型,则不仅复制引用,还复制引用的对象。因此,原对象及其副本引用不同的对象。
  • 深拷贝需要更多的时间和内存,因为它需要复制引用的对象及其所有子对象。
  • 在深拷贝中,对原对象所做的更改不会影响副本,因为它们引用的是不同的对象。
  • 我们可以使用JSON.parse(JSON.stringify(object))来实现一个简单的深拷贝,或者使用第三方库如lodash的_.cloneDeep()方法。
let originalObject = {  
  name: 'Alice',  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  }  
};  
  
// 注意:此方法仅适用于可以被JSON表示的数据结构  
let deepCopiedObject = JSON.parse(JSON.stringify(originalObject));  
  
console.log(deepCopiedObject); // { name: 'Alice', address: { street: '123 Main St', city: 'Springfield' } }  
  
// 修改原对象的属性,深拷贝的对象不会受到影响  
originalObject.name = 'Bob';  
console.log(deepCopiedObject); // { name: 'Alice', address: { street: '123 Main St', city: 'Springfield' } }  
  
// 修改原对象内部引用对象的属性,深拷贝的对象也不会受到影响  
originalObject.address.street = '456 Elm St';  
console.log(deepCopiedObject); // { name: 'Alice', address: { street: '123 Main St', city: 'Springfield' } }

截屏2024-03-02 14.48.48.png 附加题:

let originalObject = {  
  name: 'Alice',  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  }  
};  
let newOriginalObject=originalObject
console.log(originalObject===newOriginalObject) //true

//newOriginalObject 不是浅拷贝也不是深拷贝,是赋值,指针指向同一个引用对象

截屏2024-03-02 14.49.56.png

截屏2024-03-02 14.49.56.png

(3)手写实现浅拷贝/深拷贝:

a.手写浅拷贝


let params = {  
  name: 'Alice',  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  }  
};  

function shallowCopy(originalObject) {  
   if(!originalObject||typeof originalObject!=='object'||originalObject instanceof Date|| originalObject instanceof RegExp) return originalObject
  // 创建一个空对象作为拷贝的目标  
  let copiedObject = Array.isArray(originalObject)?[]:{};  
  
  // 遍历原对象的所有自身属性  
  for (let key in originalObject) {  
    // 使用hasOwnProperty确保只复制对象自身的属性,不复制原型链上的属性  
    if (originalObject.hasOwnProperty(key)) {  
      // 复制属性值到目标对象  
      // 如果属性值是引用类型(对象或数组),则只复制引用,不复制对象或数组本身  
      copiedObject[key] = originalObject[key];  
    }  
  }  
  
  // 返回拷贝后的对象  
  return copiedObject;  
} 

shallowCopy(params)

b:手写深拷贝(该拷贝有缺陷)



let params = {  
  name: 'Alice',  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  }  
}; 

function deepCopy(originalObject){
  if(!originalObject||typeof originalObject!=='object'||originalObject instanceof Date|| originalObject instanceof RegExp) return originalObject
  
  if(typeof originalObject!=='object') return originalObject
  
  let copiedObject = Array.isArray(originalObject)?[]:{};  
  
  for (let key in originalObject) {  
    
    if (originalObject.hasOwnProperty(key)) {  
      
      copiedObject[key] = deepCopy(originalObject[key])
    }  
  }
  return copiedObject
}

deepCopy(params)

let root={
  name:'root'
}
let obj1={
  name:'obj1'
}
let obj2={
  name:'obj2'
}
root.child=obj1
obj1.child=obj2
obj2.child=root
console.log(root===root.child.child.child) //true

let rootCopy=deepCopy(root) //deepCopy函数报错bug

c:手写深拷贝(完美版)

function deepCopy(original, hash = new WeakMap()) {  
  // 如果是基本类型,直接返回  
  if (typeof original !== 'object' || original === null) {  
    return original;  
  }  
  
  // 如果是日期对象,直接复制  
  if (original instanceof Date) {  
    return new Date(original);  
  }  
  
  // 如果是RegExp对象,直接复制  
  if (original instanceof RegExp) {  
    return new RegExp(original.source, original.flags);  
  }  
  
  // 如果是函数,则不执行深拷贝,直接返回原函数  
  if (typeof original === 'function') {  
    return original;  
  }  
  
  // 如果对象已经在hash中存在,说明存在循环引用,直接返回hash中的值  
  if (hash.has(original)) {  
    return hash.get(original);  
  }  
  
  // 创建一个新对象或数组  
  let copy;  
  if (Array.isArray(original)) {  
    copy = [];  
  } else {  
    copy = {};  
  }  
  
  // 将当前对象存入hash中  
  hash.set(original, copy);  
  
  // 递归复制对象的每一个属性  
  for (let key in original) {  
    if (original.hasOwnProperty(key)) {  
      copy[key] = deepCopy(original[key], hash);  
    }  
  }  
  
  // 返回深拷贝后的对象  
  return copy;  
}  
  
// 使用示例  
let originalObject = {  
  name: 'Alice',  
  age: 30,  
  address: {  
    street: '123 Main St',  
    city: 'Springfield'  
  },  
  friends: ['Bob', 'Charlie'],  
  greet: function() {  
    console.log('Hello!');  
  }  
};  
  
let deepCopiedObject = deepCopy(originalObject); 

let root={
  name:'root'
}
let obj1={
  name:'obj1'
}
let obj2={
  name:'obj2'
}
root.child=obj1
obj1.child=obj2
obj2.child=root
console.log(root===root.child.child.child) //true

let rootCopy=deepCopy(root) 
console.log(rootCopy===rootCopy.child.child.child) //true

截屏2024-03-02 14.52.13.png