JavaScript 范围引用变量

javascript scope reference variable

本文关键字:变量 引用 范围 JavaScript      更新时间:2023-09-26

所以我在js面试中遇到的一个问题基本上涉及内部函数中的范围和变量,例如假设你有这个:

function(){
var a=b=3;
}

显然,b 没有定义,所以它已经成为一个全局变量,现在当你在全局范围内更改 B 时会发生什么,一个值也会改变吗?

我不确定如何测试这一点,但例如,如果您将 b 更改为 10,a 现在也是 10 还是保持 3?

显然,b没有定义,因此它已成为全局变量

仅在松散模式下。不要使用松散模式。:-)使用严格模式,这将是它一直应该出现的错误。

现在,当您更改全局范围内的b时会发生什么,a的值是否也会更改?

不,ab之间根本没有联系。变量包含(更多内容见下文)。执行a = b时,b中的将复制到a 。不会在两个变量之间创建链接。

面试官可能正在问一个棘手的问题(他们喜欢这样做)和/或他/她可能陷入了一个常见的错误,所以请继续阅读...... :-)

我不确定如何测试这个

由于我们处于松散模式,我们知道原始函数调用将this设置为在调用期间引用全局对象。由于隐式b全局将是全局对象的属性,因此我们可以通过更改this.b(全局)来检查它:

function foo() {
  var a = b = 3;
  console.log("(Before) a is " + a + ", b is " + b);
  this.b = 10;
  console.log("(After) a is " + a + ", b is " + b);
}
foo();

(在浏览器上,我们可以使用 window 而不是上面的this,因为window是浏览器上引用全局对象的默认全局。

值得指出的是,虽然只有一个b,但上述每个调用foo都会有不同的a。但我不认为它真的影响了这个问题。


在评论中,您询问了"参考变量"。JavaScript 中没有"引用变量"。您可能正在考虑对象引用。关键事实即将出现:变量是否包含对象引用或原语,这与我们正在谈论的内容(更改b更改a?)没有区别。但这是一个常见的错误,人们(甚至可能是面试官问问题:-)认为这很重要。但是,他们将更改b(变量)与更改b引用的对象的状态混为一谈。

变量包含。将对象引用分配给变量时,该对象引用是一个,用于告知 javaScript 引擎对象在内存中的位置。例如:

var b = {answer:42};

在内存中,我们有:

                 +------------+[b:REF55134]-----| (对象) |                 +------------+                 |答案: 42 |                 +------------+

b 中的是对对象的引用。我在上面将其表示为 REF55134 ,但我们永远无法实际访问该引用的原始值。这个原始值并不重要,它只是一个东西(类似于一个数字),告诉 JavaScript 引擎对象在哪里。

现在,如果我们这样做:

var a = b;

。我们将 b 的值复制到 a 中,并在内存中获取:

[b:REF55134]--+              |  +------------+              +-->| (对象) |              |  +------------+[a:REF55134]--+ |答案: 42 |                  +------------+

ab 具有相同的值,因此它们都指向同一个对象。

更改b仍然对a没有任何影响。人们感到困惑的是,如果我们改变b指向的对象的状态,自然我们也可以通过a看到这种变化的状态。b中的没有改变,它所指向的事物的状态发生了变化。

例如:

b.question = "Life, the Universe, and Everything";

为我们提供:

[b:REF55134]--+              |  +------------------------------------------------+              +-->|                  (对象) |              |  +------------------------------------------------+[a:REF55134]--+ |答案: 42 |                  |问题:"生命、宇宙和万物" |                  +------------------------------------------------+

b没有改变,对象变了。所以很自然地,如果我们console.log(a.question);,我们会看到一个著名的问题,因为ab指向同一个对象。

如果我们真的改变b,它对a根本没有影响:

b = {foo:"bar"};
                  +------------+[b:REF14359]----->| (对象) |                  +------------+                  |福:"酒吧" |                  +------------+                  +------------------------------------------------+[a:REF55134]----->|                  (对象) |                  +------------------------------------------------+                  |答案: 42 |                  |问题:"生命、宇宙和万物" |                  +------------------------------------------------+

请注意,现在b在其中具有不同的值,指的是不同的对象。

在这种情况下,由于 3 是原始类型的数字。因此,它对b赋值和a赋值使用基元数据类型。JavaScript 中的原始值定义了不可变的值。 这意味着它的价值不会以任何方式改变你。

不过这个问题有点误导。 如果每次调用函数时b更改,则 a 的值将不同。每次都是不同的变量,a,而不是同一个变量。 不过a立即超出了范围,所以谁在乎a呢?该函数等效于具有 b = 3 主体的函数。 附带说明一下,修改函数中的任何类型的全局状态都是糟糕的设计。

ECMAScript 6 定义了七种数据类型(布尔型、空值、未定义型、数字型、字符串型、符号型、对象型),除了 Objects 之外,所有数据类型都是原始的。

最好在采访中明确提及,您了解使用对象会有所不同a假设它不会立即超出范围。 在这种情况下,更改b的属性也会反映在a 中。 但是完全改变b不会对a产生影响。 为什么?因为当您更改属性时,您更改的是可变值,而不是变量。 您可以进一步解释使用包装器对象 Number 的工作方式也不同。

您还会因提及他提供的代码在严格模式下无效而获得奖励积分。 可以通过使用文件顶部的"use strict";来指示您正在使用严格模式,也可以通过将严格模式设置为函数中的第一个语句来将严格模式的范围限定为函数内。 在 ES6 模块系统中,严格模式默认处于打开状态。

附带说明一下,由于a立即超出了范围,因此这个问题非常糟糕,以至于它可能表明一个糟糕的编码人员正在面试你。 在我所知道的每个 JavaScript 实现中,它都不是一个多线程环境......