JavaScript 是按引用传递还是按值传递类型

Sahil Bhosale 2024年2月15日
  1. JavaScript 中的原始数据类型是按值传递的
  2. JavaScript 中的非原始值是通过引用传递的
  3. JavaScript 既是按值传递又是按引用传递
JavaScript 是按引用传递还是按值传递类型

在深入探讨 JavaScript 编程语言是否是按引用类型传递之前,让我们先看看按值传递和按引用传递之间的区别。

当我们进行赋值操作或处理函数本身时,这些东西通常会出现。因为大多数时候,我们执行赋值操作,创建各种函数,并将多个变量传递给这些函数,了解这些东西的内部工作原理将使我们对编程语言有更深入的了解。

每当我们将一个变量的值分配或传递给另一个变量或函数时,它被称为按值传递。当我们将一个变量的地址分配或传递给另一个变量或函数时,它被称为通过引用传递。

现在我们已经看到了值传递和引用传递类型的基本定义,让我们进一步了解它们在下面的示例中的使用方式。

JavaScript 中的原始数据类型是按值传递的

请注意,JavaScript 中有两种数据类型:原始数据类型和非原始数据类型。原始数据类型包括数字、布尔值、字符串、未定义和空值。

这些数据类型没有像我们在对象或数组中那样的任何预定义方法。原始数据类型是不可变的;这意味着无论何时你将一个原始变量的值存储或分配给另一个变量并对第二个变量的值进行一些修改时,第一个变量的值都不会改变。它将保持原样,直到并且除非你更改该变量本身的值。只有这样,第二个变量才会改变。

你可以在 MDN 上找到与原始数据类型相关的更多信息。

// Example 1
let a = 10;
let b = a;
b = b + 1;

console.log(a);
console.log(b);


// Example 2
let name = 'Google';

let greet =
    (val) => {
      val = 'Hi ' + name + '!';
      return val;
    }

             console.log(name);
console.log(greet(name));

输出:

10
11
Google
Hi Google!

借助下图可以更好地理解上面的代码。请注意,它只解释了第一个代码示例;第二个示例的工作方式与示例一大致相同。

原始类型示例可视化

在我们的第一个示例中,我们有两个变量:ab。我们为变量 a 分配了数值 10;这将在内存中为变量 a 分配一个空间,由具有地址 0x01 的黄色表示(这只是本示例中采用的任意地址)。

然后,我们将变量 a 分配给变量 b。这个过程将为变量 b 分配新的内存空间。在这里,它将存储变量 a 的值,即 10

从上面的例子中,我们可以清楚地看到变量 ab 的地址是不同的,这意味着它们在内存中分配了不同的空间。这个结果意味着,无论何时你将变量 b 的值更改或修改为 b = b + 1,变量 a 内的值都不会改变。只有变量 b 的值会改变,它会变成 11,因为我们将它加一。

在我们的第二个例子中,我们有一个变量 name,里面有一个值 Google。我们还有一个名为 greet() 的函数,它接受一个字符串值作为参数并返回一个新字符串。作为此函数的参数,我们传递变量 a(请注意,传递变量意味着传递其值而不是其地址)。Google 值将存储在名为 val 的函数局部变量中。

目前,变量 val 包含值 Google。在函数中,我们将此值更改为 Hi Google!然后返回这个值。现在,如果你输出 name 变量值和函数,以及返回值,它们都会不同。name 变量保持不变,因为 nameval 变量存储在不同的位置,我们只是将值 Google 传递给函数和地址。

之前,我们已经看到了按值传递的定义,它表示我们只在赋值期间或将变量传递给函数时传递变量的值。上面的两个例子在实践中证明了这一点。

JavaScript 中的非原始值是通过引用传递的

非原始数据类型由数组、对象或类组成。这些数据类型确实有预定义的内置函数,允许我们操作数组或对象内的数据。

原始数据类型是可变的;这意味着以后可以修改的数据类型被称为可变的。此外,非原始数据类型是通过引用调用的。让我们通过下面的例子来理解这一点。

let a = [1, 2];
let b = a;
b.push(3);

console.log(a)
console.log(b)

输出:

[ 1, 2, 3 ]
[ 1, 2, 3 ]

上例的图像如下所示。

非前置式示例

在此示例中,我们创建了一个数组 a,其中包含两个元素:[1,2]。由于数组是非原始数据类型,变量 a 不会存储所有数组元素。相反,值 [1,2] 将存储在内存中的另一个位置。在这种情况下,该数组的起始地址,在这种情况下,0x01,将存储在变量 a 中,如上图所示。由于变量 a 具有存储实际数组元素的地址,因此它将指向该位置。

当我们将变量 a 分配给变量 b 时,我们将存在于 a 中的地址存储到变量 b 中。现在,变量 b 也将指向与变量 a 相同的内存位置 0x011,因为它们具有相同的内存地址。因此,如果你更改由 b 指向的数组的元素,那么 a 也会受到影响,因为两者都指向相同的位置。

在这里,我们使用变量 b(例如 b.push(3))将一个新元素推送到数组中。现在,变量 b 将从第一个元素 1 开始遍历整个数组,直到最后一个元素 2。在最后一个元素之后,它将新元素 3 插入到数组中。如果你同时打印变量 ab,你将看到由于引用,它们都将显示与 [1,2,3] 相同的值。

这被称为通过引用传递,因为我们传递的是内存地址本身而不是值。

JavaScript 既是按值传递又是按引用传递

JavaScript 编程语言支持按值传递和按引用传递。数字、布尔值、字符串、undefined 和 null 等原始值都是通过值传递的。非原始类型(例如数组、对象和类)通过引用传递。

作者: Sahil Bhosale
Sahil Bhosale avatar Sahil Bhosale avatar

Sahil is a full-stack developer who loves to build software. He likes to share his knowledge by writing technical articles and helping clients by working with them as freelance software engineer and technical writer on Upwork.

LinkedIn