到目前为止,这是我的思考过程:
在 JS 中,原语是按值传递的,对象是按引用传递的。
let var1 = 10 // var1 is assigned a reference to the primitive 10
let var2 = var1 // var1 is evaluated to the primitive 10, var2 is assigned a reference to the primitive 10
// var1 and var2 are independent. This is pass by value
const obj1 = {} // obj1 is given a reference to the object created in memory by the object literal
const obj2 = obj1 // obj2 is given a reference to the exact same object obj1 is pointing to
// obj1 and ob2 are pointing to the same object, they are not independent variables. This is pass by reference
我感到困惑的是闭包。在闭包中,似乎一切都是通过引用传递的。甚至是引用原语的变量。
function func() {
let a = 0;
return [
function () {
a++;
return a;
},
function () {
a++;
return a;
},
];
}
const [a, b] = func();
console.log(a(), b()); //will result in logging 1, 2
在此示例中,func 将局部变量 a 定义为 0,然后将其包含在返回的 2 个匿名函数的闭包中。然后,通过从任一函数中改变 a,很明显它们引用了内存中的同一位置。
以前我以为这两个函数只是创建了一个不可访问的对象来存储其闭包并复制变量。如果是这种情况,那么变量将按值复制,并且每个匿名函数在其闭包中都会有一个名为 a 的变量,该变量引用基元 0。然后您可以安全地在每个函数中对 a 进行变异。
但现在很明显,函数具有对执行 func 时定义的实际变量 a 的引用。
我对闭包如何工作的理解正确吗?有没有其他方法来引用变量本身,而不是它指向的原语?
在 JS 中,原语是按值传递的,对象是按引用传递的。
“按引用传递”的概念在 JavaScript 中不适用。通过引用传递意味着您可以设计一个函数
func
,这样可能会发生以下情况:
let a = {};
let b = a;
func(a);
if (a !== b) console.log("This will not happen. This is not C++");
这仅在支持“按引用传递”的语言中可行。 JavaScript 中并非如此。
在 JavaScript 中,对象是引用,它们按值传递,就像任何其他值按值传递一样。令人困惑的是,在 JavaScript 领域,术语“对象”既用于引用,又用于引用的“结构”。
obj1和ob2指向同一个对象,它们不是独立变量。这是通过引用传递的
不,这不是“通过引用传递”的概念。尽管
obj1
和 obj2
指向同一个对象,但它们是独立变量。无论这两个变量是否是对象类型,您都可以为其中一个变量“分配”不同的值,并且不会影响另一个变量。
对象有一个显着的特征,那就是它们可以被变异,但那是另一个概念。基元不能改变——它们是不可变的。这与不适用于 JavaScript 的“引用传递”概念无关。
在闭包中,似乎一切都是通过引用传递的。甚至是引用原语的变量。在您的示例中,变量
a
不是引用原语,其值是原语。在您的示例中,只有一个变量
a
,它可以由函数
func
和两个匿名函数访问。这里任何地方都不会发生“路过”的情况。
在此示例中,func 将局部变量 a 定义为 0,然后将其包含在返回的 2 个匿名函数的闭包中。我不会在这里使用术语“包含”。只是函数可以访问它们定义的外部作用域,在这种情况下,这意味着两个匿名函数可以访问
func
的作用域,其中包括变量
a
。
但现在很明显,这些函数反而引用了执行它们对
a
时定义的实际变量func
。
a
的访问权限与
func
中的代码对该变量的访问权限相同。没有什么区别。
有没有其他方法来引用变量本身,而不是它指向的原语?引用变量而不是其值正是对象属性所提供的。对象属性是标识属性的名称。
例如:
const myVars = { "a": 1, "b": 2 };
现在我有了对该属性的引用,我什至可以将其保留在变量中:
let prop = "a";
现在我有了一个名称变量,可以使用它来检索值:
console.log(prop); // a
console.log(myVars[prop]); // 1
我可以更改 prop
以引用其他属性:
prop = "b";
console.log(prop); // b
console.log(myVars[prop]); // 2
所以你可以说,通过 prop
我有一个对原始值的引用。
但现在很明显,相反,这些函数引用了 执行 func 时定义的实际变量 a。这是一个正确的想法。 JS 函数在其闭包中保留对外部作用域(创建函数的作用域)的“引用”,并且对该作用域的访问具有在其中创建的所有函数。
下面的示例与您的示例类似。由于闭包,可以访问全局变量:
let gl = 100;
function f(){
gl++;
return gl
}
function g(){
gl++;
return gl
}
f();
g();
gl;