Featured image of post js的按值访问和按引用访问

js的按值访问和按引用访问

为什么不能原生实现swap函数?

最近在学习数据结构和算法的时候,常常会遇到需要交换两个数值的情况,使用解构赋值虽然简单快速,但如果能把代码封装成一个swap函数,可以使代码更加简洁,也更具可读性,但是在写出来后才发现其中的问题所在。

众所周知,JavaScript 中的变量分为原始值引用值两种数据类型,其中原始值对应 Undefined、null、Boolean、Number、String 和 Symbol 六种类型,而引用值对应 Object、Array 等。访问原始值是按值访问的,而引用值则是按引用访问的。在复制变量和传参的时候,两种访问方式存在根本上的不同。

按值访问的类型,在复制和传递变量时,会生成一个独立的新变量,互不影响,变量独立地储存于栈内存中。

按引用访问的类型,复制和传递变量时,实际上传递的是同一个指针,指向同一个堆内存中的对象。

正因此,如果想要编写一个swap函数,如下

1
2
3
function swap(a, b) {
  [a, b] = [b, a];
}

在外部调用时会发现毫无效果

1
2
3
4
a = 10;
b = 20;
swap(a, b);
console.log(a, b); // 1020

这是因为调用函数传参时,实际上复制了一份新的 a 和 b 作为变量进入函数,在函数结束的时候这两个变量便被清除,因此对函数外部的变量无法造成影响。

但是对于对象而言,在传参的时候实际上传入的是指向该对象的指针,所以在函数内部对该对象的修改会反映到全局中,如下

1
2
3
4
5
6
7
let Person = new Object(); // 使用new关键字创建的变量都是Object类型
setName(Person);
console.log(Person.name); // niko

function setName(obj) {
  obj.name = "niko";
}

所以对于按值访问的对象,从根本上就不可能通过函数传参进行改变,这是因为传入的参数实际上是独立于外部变量的一个新变量,其只存在于该函数的生命周期内,函数执行完毕后就会清除自身作用域内的局部变量,所以 swap 函数对原始值是无法生效的。

但是对于对象而言,在任何地方对其进行的修改都会导致该对象本身值的变化,这也是为什么Vueref函数调用时需要把数据封装成对象的原因。

点击查看动图

“在任何值周围都有一个封装对象,这样我们就可以在整个应用中安全地传递它,而不必担心在某个地方失去它的响应性”

——Vue 官方文档

Built with Hugo
主题 StackJimmy 设计