概述
通常,数值、字符串、布尔值这三种类型,合称为原始类型(primitive type)的值,即它们是最基本的数据类型,不能再细分了。对象则称为合成类型(complex type)的值,因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器。至于undefined
和null
,一般将它们看成两个特殊值。
栈内存和堆内存
栈内存
- 存储的值大小固定
- 空间较小
- 可以直接操作其保存的变量,运行效率高
- 由系统自动分配存储空间
堆内存
- 存储的值大小不定,可动态调整
- 空间较大,运行效率低
- 无法直接操作其内部存储,使用引用地址读取
- 通过代码进行分配空间
值类型
解析
// 值类型
let a = 100
let b = a
a = 200
console.log(b) // 100
JavaScript
中的原始类型的值被直接存储在栈中,在变量定义时,栈就为其分配好了内存空间。
内存中有一个变量a
,值为100
。我们从变量a
复制出一个变量b
,此时在内存中创建了一个块新的空间用于存储b
,虽然两者值是相同的,但是两者指向的内存空间完全不同,这两个变量参与任何操作都互不影响。当改变a后,b指向的空间存储的值仍为100
比较
对于原始类型,比较时会直接比较它们的值,如果值相等,即返回true
。
var a = 'abc';
var b = 'abc';
console.log(a === b); // true
常见值类型
// 常见值类型
let a // undefined
const s = 'abc'
const n = 100
const b = true
const s = Symbol('s')
引用类型
解析
// 引用类型
let a = { age: 20 }
let b = a
b.age = 21
console.log(a.age) // 21
引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
当我们复制引用类型的变量时,实际上复制的是栈中存储的地址,所以复制出来的b
实际上和a
指向的堆中同一个对象。因此,我们改变其中任何一个变量的值,另一个变量都会受到影响,这就是为什么会有深拷贝和浅拷贝的原因。
比较
var obja = { age: 20 }
var objb = { age: 20 }
console.log(obja === objb); // false
对于引用类型,比较时会比较它们的引用地址,虽然两个变量在堆中存储的对象具有的属性值都是相等的,但是它们被存储在了不同的存储空间,因此比较值为false
。
常见引用类型
// 常见引用类型
const obj = { x: 100 }
const arr = ['a', 'b', 'c']
// 特殊引用类型,指针指向为空地址
const n = null
// 特殊引用类型,但不用于存储数据,所以没有"拷贝、复制函数"一说
function fn() {}