数据类型
JS的数据类型分为两种:
- 基础数据类型
- 引用数据类型
基础数据类型
主要包括: Number、String、Boolean、Null、Undefined、Symbol、BigInt。
前面5中比较常见,后面两种属于ES版本迭代中新移入的。
- Symbol
Symbol是ES6引入的一种基本数据类型,用于表示一个独一无二的值,不能与其他数据类型进行运算
const a = Symbol();
console.log(a); //Symbol()
//因为Symbol是基本数据类型,而不是对象,不能 new 。
const a = new Symbol();//报错,Symbol is not a constructor
- BigInt
BigInt是ES11引入的一种基本数据类型,用于当整数值大于Number数据类型支持的范围时,这种数据类型允许我们安全地对大整数执行算术操作
JS中的 Number类型只能安全地表示-9007199254740991(-(253-1))和9007199254740991((253-1)),任何超出此范围的整数值都可能失去精度
使用BigInt类型数据,有两种方式:
// 1.在数字末尾加n
console.log(9007199254740995n);
// 9007199254740995n
// 2.使用BigInt构造函数
var num = BigInt("9007199254740995");
console.log(num);
// 9007199254740995n
引用数据类型
主要指Object类型数据,但细分也可分为: 普通Object对象、Math、RegExp、Date、Function、Array
这几个都是比较常用的数据。
二者的区别
二者的区别一般会从存储角度来说。存储就会提到——栈内存和堆内存。
栈内存是类似数组的一种结构,存储变量名和变量值。堆内存则是一个整体块,按照存储内容多少分隔区域,会有一个存储地址指向该区域
- 基本类型的数据,变量名和变量值都存储在栈内存中。
如果变量被重新赋值,则修改栈内存中与该变量对应的变量值
如果将A变量的值赋给B变量,则栈内存中会存储B变量的变量名和对应的变量值(A变量的值)
- 引用类型的数据,变量值是存储在堆内存中的对象,栈内存中存储的是变量名和指向堆内存中变量值对象的地址(在其他语言中也叫指针)。
如果变量被重新赋值,不会修改堆内存中的原变量值对象,而是将栈内存中与该变量对应的指针修改为堆内存中新变量值对象的地址
如果将A变量的值赋给B变量,则栈内存中会存储B变量的变量名和对应的变量值(A变量的地址指针)。所以会有修改B变量中属性的值的情况下A变量中属性的值也会变,即为"共享"
深拷贝与浅拷贝
拷贝,也就是复制。在JS中就是将B变量的值复制给A变量,深浅拷贝的区别来源就是引用类型数据的"共享"问题
浅拷贝
自己创建一个新的对象,来接受你要重新复制或引用的对象值。
- 如果对象属性是基本的数据类型,复制的就是基本类型的值给新对象;
- 如果属性是引用数据类型,复制的就是内存中的地址( 共享:如果其中一个对象改变了这个内存中的地址,肯定会影响到另一个对象)
浅拷贝常用方法
常用方法主要有:Object.assign()、扩展运算符、concat()、slice()
- Object.assign(target,source)
let target = {};
let source = { a: { b: 2 } };
Object.assign(target, source);
console.log(target); // { a: { b: 10 } };
source.a.b = 10;
console.log(source); // { a: { b: 10 } };
console.log(target); // { a: { b: 10 } };
- 扩展运算符
let obj = {a:1,b:{c:1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj) //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2
console.log(obj) //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
- concat()、slice()
let arr = [1, 2, 3];
let newArr = arr.concat();
newArr[1] = 100;
console.log(arr); // [ 1, 2, 3 ]
console.log(newArr); // [ 1, 100, 3 ]
let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr); //[ 1, 2, { val: 1000 } ]
深拷贝
将一个对象从内存中完整地拷贝出来一份给目标对象,并从堆内存中开辟一个全新的空间存放新对象,且新对象的修改并不会改变原对象,二者实现真正的分离。
深拷贝常用方法
常用方法主要有: JSON.parse(JSON.stringify())、for…in遍历加递归调用、forEach加Object.*方法
- JSON.parse(JSON.stringify(xxx))
let obj1 = { a:1, b:[1,2,3] }
let obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj2); //{a:1,b:[1,2,3]}
obj1.a = 2;
obj1.b.push(4);
console.log(obj1); //{a:2,b:[1,2,3,4]}
console.log(obj2); //{a:1,b:[1,2,3]}
- for…in遍历加递归调用
const deepClone = (obj) => {
if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {}
for (let key in obj) {
if (typeof obj[key] === 'object') {
newObj[key] = deepClone(obj[key])
} else {
newObj[key] = obj[key]
}
}
return newObj
}
- forEach加Object.*方法
var deepClone = function (obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function (name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
var obj1 = {
family: { brother: "wangzhipeng", father: "wanglicai", mother: "sunaiyun" },
name: "gino",
sex: "male",
age: "27"
};
var obj2 = deepClone(obj1);
obj1.sex = "close";
console.log(obj1);
/*
{ family: { brother: 'wangzhipeng',father: 'wanglicai',mother: 'sunaiyun' },
name: 'gino',
sex: 'close',
age: '27' }
*/
console.log(obj2);
/*
{ family: { brother: 'wangzhipeng',father: 'wanglicai',mother: 'sunaiyun' },
name: 'gino',
sex: 'male',
age: '27' }
*/