当我将它设置为等于全局变量时,变量点在哪里?

时间:2015-12-30 17:44:15

标签: javascript

这是一个简单的例子:

  1| window.gamelogic = {};
  2| var g = gamelogic;
  3| g.points = 1;
  4| g.array = ["foo","bar"];
  5| var b = g.points;
  6| b = b + 1;
  7| console.log(window.gamelogic);
  8| console.log(b);

这将打印:

Object { points=1, array=[2] }
2

所以这里要注意两件事:

  1. 一个(看似本地的)变量 - g - 当设置为全局对象并更新时,也会更新golbal对象 - window.gamelogic 。 (更新 g 也更新 window.gamelogic )。

  2. 本地 int ,b(设置为全局int, points )在更改时不会更新全局变量。 (更新 b 未更新 window.gamelogic.points

  3. 基于第一点,人们会认为当var指向全局对象时,实际上只是创建另一个指向该全局对象的相同内存位置的指针。这可以解释为什么更新 g 还会更新 window.gamelogic

    然而, b 没有更新 window.gamelogic.points 似乎反驳了这个论点。

    这里发生了什么?

4 个答案:

答案 0 :(得分:6)

在JavaScript中,变量(和属性)包含。值可以有许多不同的类型(数字,字符串,布尔值),其中一个是对象引用,它是对象的引用但不是实际的对象本身。想一个对象引用的简单方法是它只是一个数字,就像一个真正大的数组的索引,告诉我们对象的位置。 (这不是真的,但它是一种有用的方式来思考它。)或者在非编程术语中,乔可能会有一张纸和#34; 123 Any St。&#34 ;写在上面,这是乔的家。这篇论文是变量(或属性); " 123 Any St。"是一个值(在这种情况下是一个对象引用),而house是一个对象。

对象不是值,因此它们不能存储在变量或属性中(或作为函数参数传递)。只有引用才可以。

当您为变量或属性赋值(或将其作为参数传递给函数)时,您将从源复制值。因此a = b b的值复制到a。当b包含对象引用时,它是被复制的引用,而不是对象;然后ab都指向同一个对象。这就像玛丽拿出一张纸(a)并抄写Joe的那张纸上的内容(b)。现在两张纸都说乔的房子在哪里。 house 没有被复制,只是告诉我们它在哪里的信息。

考虑到这一点,让我们看看你的代码。当你这样做

window.gamelogic = {};

它创建一个对象并将其引用(值)复制到属性gamelogic中。这是一个粗略的草图,记录了当时记忆中的内容,省略了很多不必要的细节:

                 +-------------------+
                 | (stuff omitted)   |       +-----------+
window:ref429--->| gamelogic: ref758 |------>|           |
                 +-------------------+       +-----------+

然后你这样做:

var g = gamelogic;

(挥手)创建一个变量(我将在稍后解释挥手)并在gamelogic中分配(复制)它。由于该值是对象引用,ggamelogic现在指向相同的位置;也就是说,他们引用到同一个对象:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +-----------+
                                         +-->|           |
                                         |   +-----------+
g: ref758--------------------------------+

然后你做

g.points = 1;

在该对象上创建一个名为points的属性,并将值1复制到其中:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +-----------+
                                         +-->| points: 1 |
                                         |   +-----------+
g: ref758--------------------------------+

让我们重点介绍一下我们在这里所做的事情:我们没有以任何方式改变g中的价值,它仍然与以下相同:A对gamelogic也引用的对象的引用。我们所做的是更改了该对象的状态(通过向其添加属性)。这是关于对象的关键事项之一:它们具有可以改变的状态。当这种状态发生变化时,看到它时你所引用的副本的副本并不重要;无论如何,你都会看到同一个对象及其(更新的)状态。

好的,继续:

g.array = ["foo","bar"];

创建一个数组(它是一个对象),并在我们的对象上创建一个名为array的属性,并将数组引用的值复制到属性中:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+

然后你做:

var b = g.points;

(挥手)创建一个变量并将值从g.points1)复制到其中:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+
b: 1

然后

b = b + 1;

1获取值b,向其添加1,并将新值(2)存储在b中。 g.points完全不受影响:

                 +-------------------+
                 | (stuff omitted)   |    
window:ref429--->| gamelogic: ref758 |---+
                 +-------------------+   |   +---------------+     +----------+
                                         +-->| points: 1     |     | 0: "foo" |
                                         |   | array: ref804 |---->| 1: "bar" |
g: ref758--------------------------------+   +---------------+     +----------+
b: 2

以上要点是:

  • 变量和属性(和函数参数)包含
  • 具有类型,例如字符串,数字,布尔值或对象引用
  • 对象引用只是一个表示对象所在位置的值。
  • 对象不是值; 引用到对象是值。
  • 对象具有可以更改的状态。*

(*如果他们允许的话。可以创建一个不允许其状态被更改的对象;这些对象被称为"不可变的"对象。它可以非常,非常方便和强大。在JavaScript中,你用Object.freeze和类似的方法做到这一点,因为默认情况下对象非常松散,你只需通过赋值就可以为它们添加属性。在许多其他语言中,它更基本:你只是没有定义任何可以改变的公共领域,也没有定义任何改变对象状态的公共方法。)

关于那个"创建变量"挥手,我忽略了那里的两个细节,因为它们并不重要:

  1. 在JavaScript中,var声明在代码开始运行之前处理,因此变量gb都已在步骤的第一行之前创建-step代码运行。最初,其中包含值undefined

  2. 由于您在全局范围内使用了var,因此bg成为全局对象的属性,这是window所指向的属性。实际上,window本身是全局对象的属性。通过ES5,所有全局变量都是全局对象的属性。 (在ES6 / ES2015中,我们有一个新的全局变量类别:使用letconstclass创建了一个全局变量。)

  3. 从技术上讲,我们的第一张图应该是这样的:

    +--------------------------+
    |   +-------------------+  |
    |   | (stuff omitted)   |  |
    +-->| window: ref429    |--+    +-----------+
        | gamelogic: ref758 |------>|           |
        | g: undefined      |       +-----------+ 
        | b: undefined      |
        +-------------------+
    

    ......但是,好吧,这似乎不太有用。 : - )

答案 1 :(得分:2)

这不是整数和对象之间的区别*;你正在对每个人进行不同的操作。考虑一下,没有涉及的整数,而是相同的操作:分配给变量,而不是对象的属性,即该变量的值:

var a = {};
var b = a;
var c = a;

b.x = 'hello, world!'; // a, b, and c now all refer to { x: 'hello, world!' }

c = { y: 'foo' };      // a and b still refer to { x: 'hello, world!' };
                       // c refers to a different object now

将此视为一切参考,并=覆盖引用。您可以更改变量或属性引用的内容,但更改对象的属性(变量或属性可以引用)会更改对象。

请注意,这与许多其他常用语言的工作方式相同,包括C#,Java和Python。

*有人可能认为原语是按值传递或复制的,但由于它们是不可变的,所以你无法区分它们。

答案 2 :(得分:1)

与JavaScript一起使用的简单心理模型是忘记在众多书籍中讲述的内容并接受JavaScript具有指针这一事实。虽然根据语言规范,JavaScript没有指针,但传统的指针模型可以成为接近语言的有用方法。

当这样考虑时,它只有 指针用于基元的对象和值类型。当使用这个模型时,没有任何神秘感。这是细分:

window.gamelogic = {}; // creates an object, stores *pointer* to the object in window.gamelogic (global)
var g = gamelogic; // creates an pointer g, which points to the same object as gamelogic
g.points = 1; // Access object by pointer, set's object value to 1
g.array = ["foo","bar"]; // same
var b = g.points; // g.points is a primitive, so b is a copy of g.points
b = b + 1; // b is incremented indepently of g.points

JavaScript引用的声明简直令人困惑。它根本没有参考。

我将解释为什么我认为JavaScript没有引用。首先,让我们来定义一些基础知识。通常认为引用是对象的另一个名称,而指针是可用于访问它们指向的对象的独立对象。就此而言,指针不必具有实际的内存地址。有一个指针允许一个人访问它指向的对象,并且指针本身是一个可以修改的对象就足够了。

另一方面,引用不是独立的对象。相反,对于现有对象,它们是别名或第二名。指针可以更改并指向不同的对象。引用不能 - 它将始终是它所分配的对象的第二个名称。差异很微妙,但至关重要。

指针和引用都是我可以称之为'间接访问' type - 即允许间接访问底层对象的类型。

因此,让我们考虑以下示例:

function foo(datum) {
    datum = datum2;
}

var dat = datum1;
// Initialize dat
foo(dat);
// what is dat now? (1)

foo()中会发生什么?我们都知道点(1)中的dat仍将保持datum1的值,尽管foo()中似乎有所改变。我们可以假设整个对象(dat)按值传递给foo() - 也就是说,复制到独立对象并提供给foo() - 这样的操作保留了原始{{} 1}}来自任何修改。

但是,众所周知,如果我们要修改foo如下:

dat

我们知道在第(1)点,dat.property也是42.(假设它之前未设置为此值)。这意味着,关于将整个dat object 传递给foo的建议是错误的 - 修改副本不会影响原始版本。我们还会注意到原始的datum1对象也将属性设置为42 - 这意味着传入函数与简单赋值没有什么不同。为简单起见,我们甚至可以从图片中删除函数调用,并使用以下代码:

function foo(datum) {
    datum.property = 42;
}

那么,var datum1 = Object(); var datum2 = Object(); // datum1 and datum2 are differently initialized var dat = datum1; dat = datum2; // datum1 and datum2 are unchanged dat.property = 42; // now datum2 property is 42, datum1 is unchanged dat = datum1; // datum2 is unchanged, still has 42 dat.property = 42; // datum1.property is 42 是什么?它不是对象本身,因为修改它会改变其他对象(如果是的话就不会这样)。它是我前面提到的间接访问类型的某种方式,因此它是指针或引用。让我们看看它是如何发挥作用的。我们假设它是一个参考。

如果它可以作为参考,     dat = datum1; //(1)     dat = datum2; //(2) 会改变datum1(并使其等于datum2)。由于引用是别名,line(1)会将dat建立为datum1的别名,而line(2)会将别名(datum1)更改为与datum2相同。不是这里的情况。

让我们检查指针逻辑是否适用。让我们假设,dat是一个指针。第一行(dat)确实很合适 - dat是一个指针,它停止指向datum1,现在指向datum2。到现在为止还挺好。接下来呢?

让我们看一下dat = datum1; dat = datum2。如果我们假设dat是一个指针,那么查看dat.property = 42;唯一合理的方法是假设(。)是一个成员解引用运算符。也就是说,取消引用左侧指针的操作员访问右侧的解除引用对象的成员。像这样阅读,我们可以清楚地看到指针类比成立 - 解除引用dat.指向datum1,而datum1的dat更改为42.相同的逻辑仍然适用。

所以指针比喻比参考更好!为了进一步说服,让我们考虑一下JS中的对象可以是未定义的事实。这对于引用没有意义 - 因为它们是第二个名称,无对象的第二个名称是什么意思(没有实体不能有第二个或第一个名称),而指针使一切都清楚 - 当然,指针,作为独立的对象,可以指向任何东西。

当然,这些指针与C / C ++中的指针不同 - 它们没有直接的内存地址,并且没有指向它们的指针。尽管如此,指针类比保持更好,并且比引用更容易清除混淆。

答案 3 :(得分:0)

g引用gamelogic引用的同一对象,因此g引用的对象所做的任何更改都会反映在gamelogic中。

这里,

 var b = g.points;
 b = b + 1;

您已将b设置为字面值1,然后将其添加1,因此它现在为2并且它不会影响g.points