JavaScript 基础天花板:函数内改了函数参数值,会影响函数外的变量吗?

83 阅读3分钟

思考以下代码最终打印的结果:

function test(base, obj){
    base = '王五';
    obj.name = '赵六';
}
let base = '张三';
let obj = { name: '李四' };
test(base, obj);

console.log('=====base====', base);
console.log('=====obj====', obj);

函数参数实际传递了什么,函数内部修改函数参数会影响函数外的变量吗?

在讲明白这个问题之前,我们需要先渐进地知道几个知识点:

  • 基础类型与引用类型;

  • 栈空间与堆空间;

  • 基础值与引用值的复制;

  • 函数参数传递;

1. 基础类型与引用类型

1.1. 基础类型
  • 7 种基础数据类型:Undefined、Null、Boolean、Number、String、 Symbol 和 BigInt;

  • 基础类型存储在栈空间;

1.2. 引用类型
  • 1 种引用类型:Object;

  • 引用类型实际值存储在堆空间;

参考下面的代码,思考其中的变量以及变量值到底是怎么存储的?

const a = 1;
const obj = { k: 1 };

2. 栈空间与堆空间

栈空间和堆空间是两种不同的内存分配方式。

2.1. 栈空间
  • 自动分配和释放;

  • 大小是在编译时确定的,随着程序的执行而动态地分配和释放;

  • 分配速度很快;

  • 大小有限,只能存储内存占用较小的数据;

  • 代码执行结束,栈空间释放;

  • 主要用来存储执行上下文和局部临时变量(注意:如果是引用类型的变量,那只存值对于的内存地址);

2.2. 堆空间
  • 手动分配和释放;

  • 大小是根据程序运行时的需求进行动态分配的;

  • 分配和释放速度慢;

  • 大小通常比栈空间大得多,可以存储更大的数据;

  • 代码执行时,浏览器会通过垃圾回收机制自动回收栈空间;

  • 主要用来存储引用类型的值;

回到上文的代码:

const a = 1;
const obj = { k: 1 };

其存储结构如下图:

duizhan.png

3. 基础值与引用值的复制

猜猜下面的代码,会是如何打印?

let base1 = '张三';
let base2 = base1;
base1 = '李四';
let obj1 = { name: '王五' };
let obj2 = obj1;
obj1.name = '赵六';
console.log(base1);
console.log(base2);
console.log(obj1);
console.log(obj2);

其打印结果如下:

image-3.png

由此我们可以得出以下结论:

  • 对于基础类型的复制,完完全全是具体值的复制,复制之后 2 个变量互不干扰完全独立;

  • 对于引用类型的复制,知识引用内存地址的复制,复制之后 2 个变量对应的真正的值其实是同一个,如下图;

image-4.png

4. 函数参数传递

一句话:函数参数传递与基础值与引用值的复制无异。

  • 基础类型值修改,函数内外互不影响,因为函数参数只是一个基础值的副本,两者互不影响;
  • 引用类型修改,函数内修改函数参数会影响函数外该参数变量的值,相反,函数外修改变量也会影响该变量对应的函数参数值。

看下图打印:

image-5.png

温馨提示:尽可能不要在函数内部修改函数传进来的参数,避免意外修改了函数外的变量值而自己还傻傻的排查了几个小时不知道原因!!!


总结:函数参数传递与基础值与引用值的复制无异,基础类型参数修改互不影响,引用类型参数修改互相影响。