一、JavaScript中值的两种类型
简单数据类型:又叫值类型,基本类型。变量中存储的是值本身。
// 值类型
let a = 100;
let b = a;
b = 200;
console.log(a) //100;
常见值类型包含:String(字符串)、Number(数值)、Boolean(布尔值)、Undefined、Symbol(表示独一无二的值)。
let a ; //const 定义必须有值,let不用
const b = 'string';
const c = 123;
const d = true;
const s = Symbol('s');//ES6 新增类型
复杂数据类型:又叫引用类型。变量中存储的仅仅是地址(引用)。
// 引用类型
let a = {time:100};
let b = a;
b.time = 200;
console.log(a) //200;
其中包含:Array(数组)、Object(对象)、Null、Function(函数)。
// 引用类型
const a = [1,2,3];
const b = {time:100};
// 特殊的引用类型,指针指向空地址
const n = null;
// 特殊的引用类型,不用于存储数据,没有拷贝、复制函数的说法
function fn(){}
Undefined和 Null的区别:
null 表示“没有对象”,即该处不应该有值。典型用法是:
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的重点。
undefined 表示 “缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:
- 变量被声明了,但没有赋值时,就等于undefined。
- 调用函数时,应该提供的参数没有提供,该参数等于undefined。
- 对象没有赋值的属性,该属性的值为undefined。
- 函数没有返回值时,默认返回undefined。
二、值类型和引用类型的区别
值类型 | 引用类型 | |
---|---|---|
存储位置 | 值类型的变量内部存储的是值本身(栈) | 引用类型的变量中存储的是指向对象的一个内存地址(堆) |
复制方式 | 值类型的变量直接赋值就是深拷贝 | 引用类型的变量直接赋值实际上是传递引用,只是浅拷贝。 |
属性和方法 | 值类型无法添加属性和方法 | 引用类型可以添加属性和方法 |
比较方式 | 值类型的比较是值的比较,只有当它们的值相等的时候它们才相等 | 引用类型的比较是引用地址的比较 |
堆栈空间分配区别:
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。栈空间的特点是先进后出,后进先出,由上往下排列
堆(操作系统): 存储复杂类型(对象),一般由程序员分配释放, 若程序员不释放,由垃圾回收机制回收。由下往上排列
JavaScript中并没有严格意义上区分栈内存与堆内存。执行上下文的执行顺序借用了栈数据结构的存取方式。
== 和 ===区别:
相等(==),只判断值大小是否相等,不判断数据类型。
全等(=== ),不光判断值相等,还要判断数据类型相等
深拷贝和浅拷贝:
浅拷贝:复制对象的属性值时,只复制第一层的属性值。如果遇到对象类的属性值,不会深入复制,即如果是复制类型的数据,只复制了地址。
深拷贝:复制对象的属性值时,如果遇到对象类的属性值,进行深入复制,不仅仅是复制地址。
为什么要区分值类型和引用类型?
两者的存储速度不同,同时基于内存空间和CPU耗时来做区分。
三、typeof 运算符
能识别所有的值类型
let a ; typeof(a) //'undefined' const b = 'string'; typeof(b) //'string' const c = 123; typeof(c) //'number' const d = true; typeof(d) //'boolean' const s = Symbol('s'); typeof(s) //'symbol'
能识别函数
typeof( console.log) //'function' typeof( function fn(){}) //'function'
能判断是否引用类型(无法再细分)
const a = [1,2,3]; typeof(a) //'object' const b = {time:100}; typeof(b) //'object' const n = null; typeof(n) //'object'
- typeof 始终会返回字符串
请注意:
1、NaN 的数据类型是数值
2、数组的数据类型是对象
3、日期的数据类型是对象
4、null 的数据类型是对象
5、未定义变量的数据类型是 undefined
四、手写深拷贝
function deepClone(obj = {}) {
//判断是否引用地址,不是则返回
if (typeof obj != 'object' || typeof obj == null) return obj;
let result
if (obj instanceof Array) {
result = []
} else {
result = {};
}
for (let k in obj) {
//必须是自身的属性
if (obj.hasOwnProperty(k)) result[k] = deepClone(obj[k]) //使用递归调用
}
return result;
}
let obj1 = {
name: "hxf",
city: { code: "cd", name: "成都市" },
arr: [1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10]
};
let obj2 = deepClone(obj1);
obj2.city.code = 'sccd';
obj2.name = 'gonwe';
console.log(obj1);
console.log(obj2);