> For the complete documentation index, see [llms.txt](https://1425816423.gitbook.io/my-knowledge-base/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://1425816423.gitbook.io/my-knowledge-base/qian-duan-ji-shu/shen-ru-li-jie-javascript-xi-lie/javascript-yuan-shi-lei-xing-he-yin-yong-lei-xing.md).

# JavaScript原始类型和引用类型

## 原始值和引用值

JavaScript的变量可以包含两种类型的值：原始类型和引用类型。

原始类型有

* String
* Boolean
* Number
* Null
* Number
* BigInt
* Symbol

引用类型有

* Array
* Function
* Date
* RegExp
* Set
* Map
* WeakSet
* WeakMap
* .......

引用类型的种类很多，可以这样认为，如果不是以上7种原始类型之一，那么它就必定是引用类型。

包含原始类型的值称为原始值，包含引用类型的值称为引用值。

## 按值和按引用

对于原始值变量是按值保存，对于引用值变量是按引用保存。

按值保存就是变量保存的就是值本身，而按引用保存就是变量保存的并不是值，而是一个指向存放真实值的地址。

对于保存原始值的变量来说，访问变量就是访问值，而对于引用类型的变量来说，访问变量会先获取到存放真实数据的地址，然后通过这个地址访问到真实的数据。

## 复制变量

除了存储方式不同，原始值和引用值在通过变量复制时也有所不同。

在复制原始值时，新变量的值是旧变量的值的副本，两者互相独立，改变其中一个不会影响另一个。

```js
let num1 = 5
let num2 = a;
console.log(num1,num2)
// 5 5
num1 = 1;
console.log(num1,num2)
1,5
```

![image-20211031155542082](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/image-20211031155542082.png)

但是引用值则不同，在复制引用值时，因为引用类型的变量的值是指向真实数据的地址，因此复制的也是这段地址，也就是说新变量和旧变量保存的都是指向同一个对象的地址。

```js
let a = [1,2,3]
// b = a 其实就是复制了a保存的地址，使a和b都指向同一块地址的数据
let b = a ;
console.log(a,b)
// [1,2,3]  [1,2,3]
```

`b = a` 其实就是复制了a保存的地址，使a和b都指向同一块地址的数据。而因为此时a和b都指向同一块地址的数据，也就是说两者共用一块地址的数据，那么无论通过哪个变量来改变数据，都必然会影响到另一个变量。

```js
a[0] = 10;
console.log(a,b)
// [10,2,3]  [10,2,3]
```

## 传递参数

既然变量分为按值保存和按引用保存，那么传递参数时又是如何呢？对于引用值，是传递变量保存的地址(指针)，还是传递指向的真实数据。

首先我们要知道，函数里的参数本质上是局部变量，可以简单理解

```js
function fn(a,b){
    // 这等于
    let a = a;
    let b = b
}
```

如果传递的是原始值，那么很好理解，参数和函数外的变量相互独立,互不影响。

但是如果是引用值呢？

```js
function fn(obj){
	obj.a = 2
    console.log(obj.a,value.a) // 2  2
}
let value = {
    a: 1
}
fn(value)
console.log(value.a) // 2
```

如果这样看，你可能以为函数参数是按引用传递的，就是说将`value`保存的真实对象传递到函数参数`obj`，但是我们稍微改一下代码。

```js
function fn(obj){
	obj.a = 2
    console.log(obj.a,value.a) // 2  2
    obj = {a: 100}
}
let value = {
    a: 1
}
fn(value)
console.log(value) // {a: 2}
```

这里只增加了一条代码，就是将obj重新赋值为一个对象。如果参数传递是按值传递，那么`obj={a: 100}`改变了真实的数据，而`value`又指向了这个数据，那么最后打印`value`的时候就应该输出`{a: 100}`,而不是`{a: 2}`，由此可见，参数传递是按值传递的。

**函数只有按值传递**，简单来说就是变量保存了什么值，就传递什么值。原始值变量保存的就是真实的数据，所以就传递真实的数据的副本，而对于引用值来说，变量保存的是地址，那么传递也就是那个地址的副本。

## 从内存角度理解

### 栈内存和堆内存

JavaScript内存空间分为栈内存和堆内存。

栈是一种特殊的列表，栈内的元素只能通过列表的一端访问，这一端称为栈顶。 栈被称为是一种后入先出（LIFO，last-in-first-out）的数据结构。 由于栈具有后入先出的特点。

堆是一种经过排序的树形数据结构，每个结点都有一个值。 通常我们所说的堆的数据结构，是指二叉堆。 堆的特点是根结点的值最小（或最大），且根结点的两个子树也是一个堆。

**原始类型的变量保存在栈内存里，引用类型的真实数据保存在堆内存里，但是同时会保存一个变量在栈内存，这个变量的值就是指向真实数据的地址。**

这样区分是因为原始值大小固定，占用内存空间小，而引用类型大小不固定、占用内存空间大。

### 从内存角度看变量复制

#### 基本数据类型的复制

```
let a = 20;
let b = a;
b = 30;
console.log(a); // 此时a的值是多少，是30？还是20？
复制代码
```

答案是：20

在这个例子中，a、b 都是基本类型，它们的值是存储在栈内存中的，a、b 分别有各自独立的栈空间， 所以修改了 b 的值以后，a 的值并不会发生变化。

从下图可以清晰的看到变量是如何复制并修改的。

![image-20211031163839389](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/image-20211031163839389.png)

#### 引用数据类型的复制

```
let m = { a: 10, b: 20 };
let n = m;
n.a = 15;
console.log(m.a) //此时m.a的值是多少，是10？还是15？
复制代码
```

答案是：15

在这个例子中，m、n都是引用类型，栈内存中存放地址指向堆内存中的对象， 引用类型的复制会为新的变量自动分配一个新的值保存在变量中， 但只是引用类型的一个地址指针而已，实际指向的是同一个对象， 所以修改 n.a 的值后，相应的 m.a 也就发生了改变。

![image-20211031163904250](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/image-20211031163904250.png)

## 参考

《JavaScript高级程序设计第四版》

《JavaScript权威指南》

[《「前端进阶」JS中的栈内存堆内存》](https://juejin.cn/post/6844903873992196110#heading-2)

部分图片出自了[《「前端进阶」JS中的栈内存堆内存》](https://juejin.cn/post/6844903873992196110#heading-2)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://1425816423.gitbook.io/my-knowledge-base/qian-duan-ji-shu/shen-ru-li-jie-javascript-xi-lie/javascript-yuan-shi-lei-xing-he-yin-yong-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
