Q:
- JS的数据类型有哪些?
- JS的引用数据类型在内存里存放在哪里?
- JS基本数据类型和引用数据类型的区别?
- JS检测数据类型的方法有哪些?
- 什么是深拷贝,什么是浅拷贝?
- Object.assign()是深拷贝还是浅拷贝?
下面带着问题我们来学习下:
Q1:JS的数据类型有哪些?
基本数据类型:
- undefiend
- null
- boolean
- number
- string
- symbol (ES6): 创建后独一无二不可变的数据类型
- bigInt (ES6):数字数据类型,表示【任意精度格式的整数】
📢:使用bigInt 可以安全的存储和操作大整数,即使这个数已经超过了Number能够表示的安全整数范围
📢:symbol:主要为了解决可能出现的全局变量冲突问题
引用数据类型:
- object
- Array
- function
Q2: JS的引用数据类型在内存里存放在哪里?
- 基本数据类型:存储在栈里,占据空间小,大小固定,属于被频繁使用的字段
- 引用数据类型:栈中存指针,堆中存实体,占据空间大,大小不固定。
如果引用类型存储在栈里,将会影响程序运行的性能;
引用类型在栈里存储指针,该指针指向堆里该实体的起始地址。当解释器寻找引用值时,会首先检索在栈里的指针,取到地址后再从堆里获取实体。
Q3:JS基本数据类型和引用数据类型的区别?
1. 在内存中存储位置不同
在操作系统中,内存被分为栈区和堆区:
栈stack:
- 数据结构:先进后出
- 内存:由编译器自动分配内存空间
- 释放:系统自动释放,调用完释放
- 使用一级缓存
堆heap:
- 数据结构:先进先出(是个优先队列)
- 内存:动态分配
- 释放:多数由程序员手动释放,若不手动释放,由垃圾回收机制回收
- 使用二级缓存
2. 传值还是传址
- 基本类型:传值
- 引用类型:传地址指针
Q4: JS 检测数据的方法
1. typeof:
问题❌: null, 对象,数据 检测都为object
typeof(null) // 'object'
2.instanceof 机制:判断原型链上是否能找到该原型
问题❌:不能判断基本数据类型
instanceof 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
2 instanceof Number // false
[] instanceof Array // true
3. constructor
- 判断数据类型
- 对象实例通过constructor对象【访问他的构造函数】
问题❌:如果原型被改变,不准确
// 函数
function Fn(){}
// 创建一个对象,改变原型
Fn.prototype = [];
// new 一个实例
const f = new Fn();
//
f.constructor === Fn // false
f.constructor === Array // true
4. 最佳方案: Object.prototype.toString.call()
为什么是使用原型上的方法,而不是直接使用xx.toString()
var a = {name:"1"};
var b = [1,2];
var c = function(){}
a.toString(); // '[object Object]'
b.toString(); // '1,2'
c.toString(); // 'function(){}'
为什么会这样:因为数据和函数是对象的实例,原型链上都重写了toString方法。所以直接调用会按自己实现的结果输出。 因为想得到对象的具体类型,要调用原型链上的toString方法
Q5:什么是深拷贝,什么是浅拷贝?
进行拷贝时,
当{key:value}里的value是对象或者数组时, 拷贝的只是一个地址,在堆里的实体是同一实体,
所以改动A的时候,B也会被改变
var a = {name:"清华"}
var b = a;
a.name = "北大";
// 因为拷贝的是引用,所以b.name 不是清华 而是北大
console.log(b.name)
深拷贝的方法:
- 自己实现:递归遍历所有属性重新赋值
- 简单粗暴:JSON.Parse(JSON.stringify(obj))
- 缺点:不能处理function,undefined,symbol,
- lodash库_.cloneDeep
Q6:Object.assign()是深拷贝还是浅拷贝?
浅拷贝