--- 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**, 均正确表述了 函数的参数是 **按值传递** 的。