这是一个简单的例子:
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
所以这里有两件事要注意:
基于第一点,可能会认为,当var指向全局对象时,您实际上只是在创建指向该全局对象相同内存位置的另一个指针。这可以解释为什么更新g也会更新window.gamelogic。
但是,不更新window.gamelogic.points似乎可以解决这个问题。
这里发生了什么?
最佳答案
在JavaScript中,变量(和属性)包含值。值可以具有许多不同的类型(数字,字符串, bool(boolean) 值),其中之一是对象引用,它是对对象的引用,但不是实际的对象本身。想到对象引用的一种简单方法是,它只是一个数字,就像一个很大数组的索引一样,告诉我们对象在哪里。 (从字面上看,这不是真的,但这是一种有用的思考方式。)或者用非编程的方式,Joe可能会在纸上写上“123 Any St.”。上面写着,这是乔的家。纸张是变量(或属性); “123 Any St.”是一个值(在这种情况下是对象引用),而房子是一个对象。
对象不是值,因此不能将它们存储在变量或属性中(或作为函数参数传递)。只能引用它们。
当您将值分配给变量或属性(或将其作为参数传递给函数)时,就是从源将值复制到其中。因此a = b
将值从b
复制到a
中。当b
包含对象引用时,将复制该引用,而不是对象。然后a
和b
都引用同一个对象。就像玛丽拿出一张纸(a
)并抄写乔的纸(b
)上的内容一样。现在两张纸都说乔的房子在哪里。房子不是被复制的,只是信息告诉我们它在哪里。
考虑到这一点,让我们看一下您的代码。当你做
window.gamelogic = {};
它创建一个对象并将其引用(一个值)复制到属性
gamelogic
中。这是此时内存中的粗略草图,省略了很多不必要的细节:+-------------------+ | (stuff omitted) | +-----------+ window:ref429--->| gamelogic: ref758 |------>| | +-------------------+ +-----------+
Then you do this:
var g = gamelogic;
(挥手)创建一个变量(我将在稍后说明挥手),并为其分配(复制)
gamelogic
中的值。由于该值是对象引用,因此g
和gamelogic
现在指向同一位置;也就是说,它们引用相同的对象:+-------------------+ | (stuff omitted) | window:ref429--->| gamelogic: ref758 |---+ +-------------------+ | +-----------+ +-->| | | +-----------+ g: ref758--------------------------------+
Then you do
g.points = 1;
它在该对象上创建一个名为
points
的属性,并将值1
复制到其中:+-------------------+ | (stuff omitted) | window:ref429--->| gamelogic: ref758 |---+ +-------------------+ | +-----------+ +-->| points: 1 | | +-----------+ g: ref758--------------------------------+
Let's highlight what we've done here: We haven't changed the value in g
in any way, it's still the same as it was: A reference to the object that gamelogic
also references. What we've done is changed the state of that object (by adding a property to it). This is one of the key things about objects: They have state that can be changed. When that state is changed, it doesn't matter which copy of the reference to it you have when you look at it; you'll see the same object, with its (updated) state, regardless.
Okay, continuing:
g.array = ["foo","bar"];
这将创建一个数组(是一个对象),并在我们的对象上创建一个名为
array
的属性,并将该数组的引用值复制到该属性中:+-------------------+ | (stuff omitted) | window:ref429--->| gamelogic: ref758 |---+ +-------------------+ | +---------------+ +----------+ +-->| points: 1 | | 0: "foo" | | | array: ref804 |---->| 1: "bar" | g: ref758--------------------------------+ +---------------+ +----------+
Then you do:
var b = g.points;
(挥手)创建一个变量并将值从
g.points
(1
)复制到其中:+-------------------+ | (stuff omitted) | window:ref429--->| gamelogic: ref758 |---+ +-------------------+ | +---------------+ +----------+ +-->| points: 1 | | 0: "foo" | | | array: ref804 |---->| 1: "bar" | g: ref758--------------------------------+ +---------------+ +----------+ b: 1
Then
b = b + 1;
从
1
获取值b
,向其中添加1
,并将新值(2
)存储在b
中。 g.points
完全不受影响:+ ------------------- +
| (省略的东西)
窗口:ref429 ---> | gamelogic:ref758 | --- +
+ ------------------- + | + --------------- + + ---------- +
+-> |点:1 | | 0:“foo” |
| |数组:ref804 | ----> | 1:“栏” |
g:ref758 -------------------------------- + + ------------- -+ + ---------- +
b:2
上面的关键点是:
(*如果允许的话。可以创建一个不允许更改其状态的对象;这些对象称为“不可变的”对象。这样做非常方便,强大。在JavaScript中,您可以使用
Object.freeze
和类似的方法可以做到这一点,因为默认情况下对象是非常松散的,您可以通过赋值来向它们添加属性。不能定义任何更改对象状态的公共(public)方法。)关于那“创造一个变量”的挥舞着,我在那儿忽略了两个细节,因为那时它们并不重要:
var
声明,因此在逐步代码的第一行运行之前就已经创建了g
和b
变量。最初,它们中的值为undefined
。 var
的,所以b
和g
成为全局对象的属性,这是window
所指向的。实际上,window
本身是全局对象的属性。直到ES5,所有全局变量都是全局对象的属性。 (在ES6 / ES2015中,我们没有一个新的全局类别:使用let
,const
或class
创建的。)因此从技术上讲,我们的第一个图应该看起来像这样:
+ -------------------------- +
| + ------------------- + |
| | (省略的东西) |
+-> |窗口:ref429 |-+ + ----------- +
| gamelogic:ref758 | ------> | |
| g:未定义| + ----------- +
| b:未定义|
+ ------------------- +
...但是,看来这没什么用。 :-)
关于javascript - 将变量设置为全局变量时,变量指向何处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34534471/