1.js数据类型
(1)基本数据类型
- Number:用于表示整数和浮点数。例如:42,3.14159。
- String:用于表示文本数据或字符序列。例如:'Hello, world!'。
- Boolean:有两个值,true 和 false,用于逻辑运算。
- Null:有一个值 null,表示一个空值或“无”的值。
- Undefined:有一个值 undefined,表示未定义。
- Symbol(ES6 新增):表示独一无二且不可变的数据类型,经常用于对象属性的键。
- BigInt(ES2020 新增):用于表示大于 2^53 - 1 的整数。
(2)复杂数据类型
- Object:最复杂的数据类型,可以包含各种数据类型的值、方法和其他对象。
- Array:一种特殊的对象,用于表示有序的元素集合。
- Function:可执行的代码块,也称为方法或函数。
- Date:用于表示日期和时间。
- RegExp:表示正则表达式,用于模式匹配和“搜索-替换”文本操作。
2.存储方式
JavaScript 的数据类型在内存中的存储方式主要取决于其类型:
- 基本数据类型:直接存储在栈(Stack)内存中,它们的值直接存储在其自身的内存空间内。由于它们的值是不可变的,因此不需要额外的内存空间来存储指针或引用。
var a=1
var str="123"
- 复杂数据类型:通常存储在堆(Heap)内存中。当我们在代码中创建一个对象时,JavaScript 会在堆内存中为这个对象分配空间,并返回一个指向该对象的指针(或引用)给栈内存。这意味着我们实际上是在操作对象的引用,而不是对象本身。当对象的引用被重新赋值时,原来的对象如果没有其他引用指向它,就会被垃圾回收机制(Garbage Collection)自动回收。
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' } }
(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' } }
附加题:
let originalObject = {
name: 'Alice',
address: {
street: '123 Main St',
city: 'Springfield'
}
};
let newOriginalObject=originalObject
console.log(originalObject===newOriginalObject) //true
//newOriginalObject 不是浅拷贝也不是深拷贝,是赋值,指针指向同一个引用对象
(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