知识体系
- 栈内存&堆内存
- 语言类型
- JS的数据类型分类
- Undefined与Null的区别
- Symbol的延伸
- JS数据存储(堆/栈存储)
- 闭包的变量存储
- 深/浅拷贝
- 赋值与浅拷贝的区别
- 类型转换
栈内存&堆内存
语言类型
-
强/弱
- 弱类型语言:支持隐式类型转换的语言,如C/JS
- 强类型语言:不支持隐式类型转换的语言
-
动/静
- 静态语言:在声明变量之前需要先定义变量的类型
- 动态语言:在运行过程中需要检查数据类型的语言
【JS是一个动态的弱类型语言】
弱类型:不需要告诉数据类型,JS引擎在运行代码过程中自己计算
动态:意味着可以用同一个变量存储不同类型的数据
JS的数据类型
数据类型分为【基本数据类型】和【引用数据类型】
-
基本数据类型:Null | Undefined | Boolean | Number(整数或浮点型) | Symbol | BigInt(大整数) | String
-
引用数据类型: Object | Array | Function | RegExp | Date
Undefined与Null的区别
- 概念上
-
undefined表示没有任何值- 没有值的
return语句(return;),隐式返回undefined; - 访问不存在的
对象属性(obj.iDontExit),返回undefined; - 变量声明时没有初始化(
let a),隐式初始化为undefined。
- 没有值的
-
null表示一个空对象指针,表示变量未指向任何对象
-
- 表现形式
- typeof
typeof null // 'object' typeof undefined // 'undefined' - == 与 ===
undefined == null // true undefined === null // false !!undefined === !!null // true - +运算与Number()
let a = undefined + 1 // NaN let b = null + 1 // 1 Number(undefined) // NaN Number(null) // 0 null 转化为 number 时,会转换成 `0` undefined 转换为 number 时,会转换为 `NaN` - typeof
- 其他
null是一个关键字,undefined是一个标识符
意味着: let undefined = 'test' // 成立,但不推荐使用 let null = 'test' // 报错
【稍作解释】
Undefined - 一个没有被赋值的变量产生的一个默认值,变量提升时的默认值
Number - 可以存储很大的整数和浮点数
BigInt - 存储一个巨大的,超过Number限制的整数
Symbol - 唯一且不可变的原始值,可以用来作为对象属性的键【解决属性名冲突的问题】。symbol的目的是去创建一个唯一的属性键,保证不会与其他代码中的键产生冲突
Symbol的延伸
【Symbol的延伸】
// 创建一个Symbol类型的值
const mySymbol = Symbol();
console.log(mySymbol); // Symbol()
const myName = Symbol('Elio');
console.log(typeof myName); // Symbol
// 没有两个Symbol的值是相等的。
console.log(Symbol() === Symbole()); // false
// 对象属性
const person = {
[myName] = 'Elio'
}
console.log(person[myName]) // Elio
【注意点】
1. typeof Null = Object (JS的一个bug,为了兼容以前版本,未做修复)
2. Object的value值有上述7中基本类型的数据组成
3. 原始类型(基本类型)和引用类型的区分 根本原因是在内存中存放的位置不一样(下面一节介绍他们在JS内存中的存放)
JS的数据存储
前言:栈存放变量,堆存放复杂对象,池存放常量(常量池)
栈内存
- 先进后出,存放临时变量一片内存块
- 栈顶进行元素的进出栈
元素存储
- 栈内存存储各种基本类型的变量以及对象变量的指针
堆内存
- 数据都存在内存中,无规则可言。
元素存储
- 存储
Obeject类型的变量。 - 对象的指针地址存储在栈中,通过栈中的指针去堆中进行获取查找。
- 指针 & 值
栈/堆内存的优缺点
- 大小分配:基本数据类型变量大小固定,故存放在栈中;引用类型变量大小不固定,故存放在堆中,自己申请大小。
- 效率:引用类型数据的存储,需要将指针地址存放在栈中,堆内存也需分配空间,效率低于栈。
- 内存回收:
栈内存中的变量在当前执行环境结束就会被销毁且回收;堆不会。
【以上的优缺点可以回答一个问题】
问题:为什么会分“堆”和“栈”两个空间呢?
回答:
栈太大 => 影响上下文切换效率 => 影响程序运行效率
【所以】栈不能太大 => 主要存放一些基本类型的小数据
【因为】引用类型的数据占用空间都比较大(存放指针地址&值)=> 使用堆空间进行存储
【但是】堆的内存分配和回收会占用一定的时间
闭包与堆内存
- 产生闭包的核心
- 预扫描内部函数
- 把函数内部引用的外部变量保存在
堆内存中
- 闭包中的变量保存在
堆内存中(这也可以解释,函数调用之后,闭包还能引用函数内部变量)
深/浅拷贝
- 深/浅拷贝只针对引用数据类型的。
- 浅拷贝:只复制对象的指针,不复制对象本身,新旧对象依然共享一个内存空间。
- 深拷贝:创建一个一样的对象,与原来对象不共享内存空间。
独立的!
浅拷贝实现方式
1. Object.assign()
// Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,返回目标对象。
// 创建一个源对象
var obj = {
username: 'elio',
userInfo: {
age: 25,
address: 'china'
}
};
// 创建目标对象,使用Object.assign()进行对象的拷贝。
var targetObj = Object.assign({}, obj);
targetObj.username = 'elioCopy';
targetObj.userInfo.age = 26;
console.log(obj.username); // elio - 第一层的对象属性是深拷贝
console.log(obj.userInfo.age); // 26
2. Array.prototype.concat()
let arr = [1,2,{
username: 'elio'
}];
let arrCopy = arr.concat();
arrCopy[2].username = 'elioCopy';
arrCopy[0] = 100;
console.log(arr); // [1,2,{username: 'elioCopy'}]
3.Array.prototype.slice()
let arr = [1,2,{
username: 'elio'
}];
let arrCopy = arr.slice();
arrCopy[2].username = 'elioCopy';
arrCopy[0] = 100;
console.log(arr); // [1,2,{username: 'elioCopy'}]
【补充】Array的slice和concat不会修改原数组(针对字符串、数字、布尔值,对象是会被改变的),只会返回一个浅拷贝了原数组中的元素的一个新数组。
深拷贝的实现方式
1.JSON.parse(JSON.stringfy())
let arr = [1,2,{
username: 'elio'
}];
let arrCopy = JSON.parse(JSON.stringfy(arr));
arrCopy[2].username = 'elioCopy';
arrCopy[0] = 100;
console.log(arr); // [1,2,{username: 'elio'}]
// 【原理】用JSON.stringfy()将对象转成JSON字符串,在用JSON.parse()把字符串解析成对象。
赋值与浅拷贝
- 赋值:赋的其实是该对象在
栈中的地址,不是堆中的数据。两个对象指向同一个内存空间,两个对象是联动的。 - 浅拷贝:会创建一个新对象。如果属性是基本类型,则拷贝的是基本类型的值;如果属性是引用类型,则拷贝的是内存地址(其中一个对象改变这个地址,会有联动影响)
【总结】
| 和原数据是否指向同一对象 | 第一层数据为基本类型 | 原数据中包含子对象 | |
|---|---|---|---|
| 赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
| 浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
| 深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
JS类型转换
两个方法
- typeof:查看变量的数据类型
typeof 3.14 // number
typeof 'elio' // string
typeof true // boolean
-------------------------
typeof null // object
typeof [1,2,3] // object
typeof NaN // number
// 使用typeof进行引用类型数据的查看,都会返回Object
// 对于这种情况,我们引入instanceof来清晰判断引用数据类型
- instanceof:清晰判断引用类型对象
console.log([1,2,3] instanceof Array) // true
console.log(Function instanceof Function) // true
类型转换
- 数字转字符串
- String()
- toString() : null&undefined除外(会报错)
String(123);
(123).toString();
- 字符串转数字
- Number()
- parseFloat() - 返回浮点数
- parseInt() - 返回整数
- 自动转换类型
'5' + 1 // '51'
'5' - 1 // 4
'5' + null // '5null'
5 + null // 5 null转换为0