2023-02-09 23:38:45 +08:00

109 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: JavaScript进阶— 函数参数按值传递
createTime: 2020/02/12 11:27:54
author: pengzhanbo
permalink: /article/m4a92nl5/
---
我们知道,在 `ECMAScrip` 中, 函数的参数是 **按值传递** 的。
那么怎么理解 **按值传递**
简单来说, **把函数外部的值复制给函数内部的参数**,即 **把值从一个变量复制到另一个变量**
那么也就是说,在函数内部,修改函数参数的值,不会改变外部变量的值。
我们来看一个例子2
**示例1**
```js
var a = 1
function foo(arg) {
arg = 2
console.log(arg)
}
foo(a) // 2
console.log(a) // 1
```
可以看出,外部变量`a`作为 函数 `foo` 的执行时参数值, 在函数内部修改传入的参数值进行修改,
函数执行后,并不会对外部变量`a` 发生修改。
这个例子确实说明了函数参数是按值传递的。
但是再来看另一个例子:
**示例2**
```js
var obj1 = {
a: 1
}
function foo(arg) {
arg.a = 2
console.log(arg)
}
foo(obj1) // { a: 2 }
console.log(obj1) // { a: 2 }
var obj2 = {
a: 1
}
function bar(arg) {
arg = 2
console.log(arg)
}
bar(obj2) // 2
console.log(obj2) // { a: 1 }
```
在这个例子中, 函数`foo` 执行完后, 打印的 `obj1` 值发生了变化,说明函数`foo` 内部修改了外部变量`obj1`
为什么会发生修改?而在 函数`bar` 执行后,`obj2` 值保持不变,这又是为什么? 函数参数是否真的是 **按值传递**
那么该如何理解 `函数参数是按值传递的`?
在理解这个之前,我们首先需要知道,`JavaScript` 的数据类型,以及不同数据类型的存储方式。
## 数据类型及其存储方式
我们知道, 在 `JavaScript` 中, 有两种 数据类型,分别是:**(1)基本数据类型**和 **(2)引用数据类型**
- 基本数据类型:值 直接保存在 **栈stack** 中。
```js
let a = 1
let b = a
a = 2
console.log(a, b) // 2 1
```
基本类型在 **栈** 中的赋值变动如下:
::: center
![function-value-stack](/images/func-value-stack.png){ style=width:500px; }
:::
- 引用数据类型:值 保存在 **堆heap** 中, 并在 **栈stack** 中保存 值 在 **堆heap** 中的内存地址。
```js
let a = { name: 'Mark' }
let b = a
b.name = 'John'
console.log(a) // { name: 'John' }
```
引用类型在 **栈** 和 **堆** 中的复制变动如下:
::: center
![function-value-stack](/images/func-value-heap.png){ style=width:680px; }
:::
## 按值传递
我们从 数据类型来理解 `按值传递`, 那么可以发现, **传递** 的值, 是指在 **栈stack** 中保存的值。
即, 无论 **参数值** 是 基本数据类型还是引用数据类型, **传递** 的是 **栈stack** 中的值。
- 对于基本数据类型, 函数内部修改参数的值,实际上是修改的是 函数参数重新在 **栈stack** 中的内存片段保存的值。
- 对于引用数据类型, 函数参数 传递是的 引用类型在 **栈stack** 中的内存地址:
- 如果直接修改参数的值,函数参数在 **栈stack** 中的内存片段保存的内存地址被覆盖。
- 如果修改 参数对象的属性值,修改的是根据 函数参数在 **栈stack** 中的内存片段保存的内存地址对应的在 **堆heap** 中的值。
所以回头重新看 **示例1****示例2** 均正确表述了 函数的参数是 **按值传递** 的。