4.3 KiB
值与引用
在许多编程语言中,赋值和参数传递可以通过值赋值(value-copy)或者引用复制(reference-copy)来完成。
例如在 C 中,传递一个引用值可以通过声明类似于这样的int* num
参数来按引用传递,如果传递一个变量 x,那么num
就是指向 x 的引用。引用就是指向变量的指针,如果不声明为引用的话,参数值总是通过值来传递的。即便是复杂的对象值也是如此(C++)。
与 C/C++ 不同的是,JavaScript 没有指针这一概念,值的传递方式完全由值来决定。JavaScript 中变量不可能成为指向另一个变量的指针。
基本类型(简单类型)的值总是通过以值复制的方式来赋值/传递,这些类型包括:null
、undefined
、字符串、数字、布尔和symbol
。
而复合值,也就是对象(以及对象的子类型,数组、包装对象等)和函数,则总是以引用复制的方式来赋值/传递。
在了解了基本类型和引用类型的值之后,先来看下他们传递有什么不同:
基本类型:
由于基本类型是按值传递的,所以 a 与 b 是分别在内存中两处保存了自己的值。a 有在内存中有自己的空间,b 也有自己单独的空间,他们互不影响。
let a = 123;
let b = a; // 按值进行传递
a += 1; // 修改 a
console.log(a); // 124
console.log(b); // 123 b 不受影响
引用类型:
引用值的情况正好相反,所谓按引用传递,就是arr1
与arr2
指向的是内存中的同一块地址,修改任何一个变量的值,都会立即反应到另一个变量上。因为他们对应的是同一块内存。
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr1.push(99); // 修改 aar1
console.log(arr1); // [ 1, 2, 3, 99 ]
console.log(arr2); // [ 1, 2, 3, 99 ]
但是引用值还有个特性容易犯错,那就是修改:
这里咋一看是修改了arr1
的值,但为什么没有反应到arr2
身上呢?说好的一起变呢?
仔细回想一下引用值的定义,他们是因为指向同一块内存地址,所以修改这段地址中的值时,就会同时反应在两个变量上。但是这里的arr1 = { name: 'xfy' }
并不是修改内存中的值,而是修改了arr1
的指向,使其指向一块新的内存地址。而arr2
还是指向以前的地址,所以arr2
没有改变。
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr1 = { name: 'xfy' };
console.log(arr1); // { name: 'xfy' }
console.log(arr2); // [ 1, 2, 3, 99 ]
使用函数修改值
由于上述值的传递特性,这也会导致在传递给函数参数时发生个中问题。
修改引用值:
function changeValue(value) {
// 按引用传递,可以直接修改
value.push(99);
// 重新赋值,并没有修改内存中的值
value = { name: 'xfy' };
}
let arr = [1, 2, 3];
changeValue(arr);
console.log(arr); // [ 1, 2, 3, 99 ]
修改基本值:
function changeValue(value) {
// 按值传递,value 获取到 num 的值
// 但是他们分别保存在两个内存中
value++;
}
let num = 123;
changeValue(num);
console.log(num); // 123
这也就是为什么在 Vue3 的 Composition API 中使用 ref 封装的响应式变量必须要有.value
属性。
强制类型转换
将值从一种类型转换为另一种类型通常称为类型转换(type casting),这是显式的情况;隐式的情况称为强制类型转换(coercion)。
也可以这样来区分:类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(runtime)。
抽象值操作
在了解强制类型之前,我们需要先掌握类型之间转换的基本规则。ES5 规范第 9 节定义了一些“抽象操作”和转换规则。
ToString
ToString 负责非字符串到字符串的强制类型转换操作。
基本值转换为字符串的规则为直接添加双引号:null 转换为"null"
,true 转换为"true"
等。数字也遵循这种规则,不过极大或极小的数字使用指数形式。
(1.07 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000).toString()
// "1.07e+21"