本文最初发表在Medium上 。
TL; DR: JavaScript中没有指针,引用的工作方式不同于我们在大多数其他流行编程语言中通常看到的方式。 在JavaScript中,不可能有一个变量到另一个变量的引用。 并且,只能通过引用分配复合值(例如,对象或数组)。
本文通篇使用以下术语:
- 标量–单值或数据单位(例如,整数,布尔值,字符串)
- 复合–由多个值组成(例如,数组,对象,集合)
- 原始–直接值,而不是对包含实际值的内容的引用。
JavaScript的标量类型是基元,但是某些语言(例如Ruby)具有标量引用类型。 请注意,在JavaScript中,标量基本值是不变的,而复合值是可变的。
底线:
- 分配给变量的值的
typeof
决定值是按值分配还是按引用分配存储。 - 在变量分配中,标量基本值(Number,String,Boolean,undefined,null,Symbol)是按值分配的,而复合值是按引用分配的。
- JavaScript中的引用仅指向包含的值,而不指向其他变量或引用。
- 在JavaScript中,标量基本值是不可变的,复合值是可变的。
按值分配的快速示例:
在下面的代码段中,我们为变量分配了标量原始值(数字),因此按值分配在此处适用。 首先,初始化变量batman
,并在变量superman
中分配了存储在batman
的值后,它将创建该值的新副本并进行存储。 修改变量superman
后, batman
不受影响,因为它们指向不同的值。
var batman = 7;
var superman = batman; //assign-by-value
superman++;
console.log(batman); //7
console.log(superman); //8
参考分配的快速示例:
在下面的代码段中,我们为变量分配了复合值(数组),因此按引用分配在此处适用。 变量flash
和quicksilver
是对相同值(也称为共享值)的引用。 修改共享值时,引用将指向更新后的值。
var flash = [8,8,8];
var quicksilver = flash; //assign-by-reference
quicksilver.push(0);
console.log(flash); //[8,8,8,0]
console.log(quicksilver); //[8,8,8,0]
如何创建新参考
重新分配变量中的复合值后,将创建一个新引用。 在JavaScript中,与大多数其他流行的编程语言不同,引用是指向存储在变量中的值的指针,而不是指向其他变量或引用的指针。
var firestorm = [3,6,3];
var atom = firestorm; //assign-by-reference
console.log(firestorm); //[3,6,3]
console.log(atom); //[3,6,3]
atom = [9,0,9]; //value is reassigned (create new reference)
console.log(firestorm); //[3,6,3]
console.log(atom); //[9,0,9]
将值作为函数参数传递时引用如何工作
在下面的代码片段中,可变magneto
是一个复合值(数组),因此将其分配给变量(函数自变量) x
作为参考。
IIFE内部调用的Array.prototype.push
方法通过JavaScript引用对变量magneto
的值进行突变。 但是,变量x
的重新分配会创建新的参考,并且对其进行的进一步修改不会影响对变量magneto
的参考。
var magneto = [8,4,8];
(function(x) { //IIFE
x.push(99);
console.log(x); //[8,4,8,99]
x = [1,4,1]; //reassign variable (create new reference)
x.push(88);
console.log(x); //[1,4,1,88]
})(magneto);
console.log(magneto); //[8,4,8,99]
如何通过JavaScript参考作为函数参数传递的复合变量中的原始值的更改
此处的解决方案是修改参考指向的现有复合值。 在下面的代码片段中,变量wolverine
是一个复合值(一个Array),并且在IIFE调用时,变量(函数参数) x
是通过引用分配的。
Array.prototype.length
属性可以通过将其值设置为0
来创建一个空数组。 因此,通过一个JavaScript引用将金刚狼变量更改为在变量x
设置的新值。
var wolverine = [8,7,8];
(function(x) { //IIFE
x.length = 0; //make empty array object
x.push(1,4,7,2);
console.log(x); //[1,4,7,2]
})(wolverine);
console.log(wolverine); //[1,4,7,2]
如何通过按值分配存储复合值
解决方案是手动复制复合值,然后将复制的值分配给变量。 因此,分配值的参考不会指向原始值。
创建复合值(数组对象)的(浅)副本的推荐方法是在不传递任何参数的情况下在其上调用Array.prototype.slice
方法。
var cisco = [7,4,7];
var zoom = cisco.slice(); //create shallow copy
cisco.push(77,33);
console.log(zoom); //[7,4,7]
console.log(cisco); //[7,4,7,77,33]
如何通过引用分配存储标量原始值?
解决方案是将标量基本值包装在复合值(即对象或数组)中作为其属性值。 因此,可以通过引用进行分配。 在下面的代码片段中,可变speed
标量基本值被设置为对象flash
的属性。 因此,通过IIFE调用将其按引用分配给变量(函数参数) x
。
var flash = { speed: 88 };
(function (x) { //IIFE
x.speed = 55;
})(flash);
console.log(flash.speed); //55
摘要
很好地理解JavaScript中的引用可以帮助开发人员避免许多常见的错误并编写更好的代码。
编码愉快!!
From: https://www.sitepoint.com/how-javascript-references-work/