原始值与参考值

Lac*_*iet 34 javascript

我读了一本名为"面向Web开发人员的专业Javascript"的书,它说:"变量由参考值或原始值指定.参考值是存储在内存中的对象".然后它没有说明如何存储原始值.所以我猜它没有存储在内存中.基于此,当我有这样的脚本:

var foo = 123;
Run Code Online (Sandbox Code Playgroud)

Javascript如何记住foo变量供以后使用?

Aad*_*hah 102

好吧,想象你的变量是一张纸 - 一个粘滞便笺.

注1:一个变量是一个便条.

现在,粘滞便笺非常小.你只能写一点信息.如果你想写更多信息,你需要更多便利贴,但这不是问题.想象一下,你有无穷无尽的粘滞便笺.

注意2:您有无穷无尽的便签,可存储少量信息.

太棒了,你能用粘滞便笺写什么?我可以写:

  1. 是或否(布尔值).
  2. 我的年龄(一个数字).
  3. 我的名字(一个字符串).
  4. 什么都没有(未定义).
  5. 涂鸦或其他任何对我毫无意义的东西(null).

所以我们可以在我们的便签上写下简单的东西(让我们屈尊俯就,称之为原始的东西).

注3:您可以在便签上写原始内容.

所以说我写30一个便条,提醒自己为我今晚扔在我家里的小派对买30片奶酪(我的朋友很少).

当我把我的便签放在冰箱上时,我发现我的妻子已经在冰箱上放了另一个便条,也说30(提醒我她的生日是在本月30日).

问:两个便签都传达相同的信息吗?

答:是的,他们都说30.我们不知道它是30片奶酪还是本月30日,坦率地说,我们不在乎.对于一个不知道更好的人,它都是一样的.

var slicesOfCheese = 30;
var wifesBirthdate = 30;

alert(slicesOfCheese === wifesBirthdate); // true
Run Code Online (Sandbox Code Playgroud)

注4:两个粘滞便笺上写有相同的东西会传达相同的信息,即使它们是两个不同的粘滞便笺.

我今晚真的很兴奋 - 和老朋友一起出去玩,玩得很开心.然后我的一些朋友打电话给我,说他们将无法参加聚会.

所以我去冰箱擦掉30我的粘滞便笺(不是我妻子的粘滞便笺 - 这会让她很生气)然后把它弄成一个20.

注5:你可以删除粘滞便笺上写的内容并写下其他内容.

问:这一切都很好,但是如果我的妻子想在我出去吃奶酪的时候写一份杂货清单让我拿起来怎么办呢.她需要为每件商品写一张便条吗?

答:不,她会拿一长纸,然后在那张纸上写下杂货清单.然后她会写一个便条,告诉我在哪里可以找到杂货清单.

那么这里发生了什么?

  1. 杂货清单显然不是简单的(呃...... 原始)数据.
  2. 我的妻子在一张较长的纸上写下了它.
  3. 她用粘滞便笺写到了哪里找到它.

亲爱的,杂货清单在你的键盘下面.

回顾一下:

  1. 实际对象(杂货清单)在我的键盘下.
  2. 粘滞便笺告诉我在哪里找到它(对象的地址).

注6:参考值是对象的引用(将在其中找到它们的地址).

问:我们怎么知道两个粘滞便笺何时说同样的话?说我的妻子制作另一个购物清单,以防我放错了第一个,并为它写了另一个粘滞便笺.两个列表都说同样的事情,但粘滞便笺说同样的事情吗?

答:不会.第一个粘滞便笺告诉我们在哪里可以找到第一个清单.第二个告诉我们在哪里找到第二个列表.两个名单是否说同样的事情并不重要.它们是两个不同的列表.

var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];
var groceryList2 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];

alert(groceryList1 === groceryList2); // false
Run Code Online (Sandbox Code Playgroud)

注7:两个粘滞便笺仅在它们引用同一对象时才传达相同的信息.

这意味着如果我的妻子做了两个便利贴提示我购物清单的位置,那么这两个便利贴包含相同的信息.所以这:

亲爱的,杂货清单在你的键盘下面.

包含与以下相同的信息:

别忘了杂货清单在键盘下面.

在编程方面:

var groceryList1 = ["1 dozen apples", "2 loaves of bread", "3 bottles of milk"];
var groceryList2 = groceryList1;

alert(groceryList1 === groceryList2); // true
Run Code Online (Sandbox Code Playgroud)

因此,您需要了解JavaScript中的原语引用.无需进入和动态内存分配等事情.如果您使用C/C++进行编程,那很重要.

编辑1:哦,重要的是,当您传递变量时,您实际上是通过值传递原始,通过引用传递参考值.

这只是一种复杂的说法,就是说你将一个粘滞便笺上写的所有内容复制到另一个便笺上(无论你是复制一个原始值还是一个引用都没关系).

复制引用时,被引用的对象不会移动(例如,我妻子的购物清单将始终保留在我的键盘下,但我可以将我复制的便签带到我想要的任何地方 - 原始便签仍将放在冰箱上).

编辑2:回应@LacViet发布的评论:

对于初学者来说,我们谈论的是JavaScript,而JavaScript没有堆栈.它是一种动态语言,JavaScript中的所有变量都是动态的.为了解释差异,我将它与C进行比较.

考虑以下C程序:

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;
    int c = a + b;
    printf("%d", c);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当我们编译这个程序时,我们得到一个可执行文件 可执行文件分为多个段(或部分).这些段包括堆栈段,代码段,数据段,额外段等.

  1. 堆栈段用于在调用函数或中断处理程序时存储程序的状态.例如,当函数f调用函数时,函数g的状态f(当时寄存器中的所有值)都保存在堆栈中.当g返回控制时,f将恢复这些值.
  2. 代码段保存了由处理器执行的实际代码.它包含处理器必须执行的一堆指令add eax, ebx(其中add是操作码,eax&ebx是&参数).该指令添加寄存器的内容,eax并将ebx结果存储在寄存器中eax.
  3. 数据段用于为变量保留空间.例如,在上面的程序中,我们需要为整数保留空间a,bc.另外,我们还需要为字符串常量保留空间"%d".因此保留的变量在存储器中具有固定地址(在链接和加载之后).
  4. 除了所有这些之外,您还可以通过操作系统提供一些额外的空间.这称为堆.您需要的任何额外内存都是从此空间分配的.以这种方式分配的内存称为动态内存.

让我们看一个带有动态内存的程序:

#include <stdio.h>
#include <malloc.h>

int main() {
    int * a = malloc(3 * sizeof(int));

    a[0] = 3;
    a[1] = 5;
    a[2] = 7;

    printf("a: %d\nb: %d\nc: %d\n", a[0], a[1], a[2]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

因为我们想要动态分配内存,所以我们需要使用指针.这是因为我们希望使用相同的变量指向任意内存位置(每次都不一定是相同的内存位置).

所以我们创建一个名为的int指针(int *)a.空间for a是从数据段分配的(即它不是动态的).然后我们调用malloc从堆中为3个整数分配连续空间.int返回第一个的内存地址并存储在指针中a.

问:我们学到了什么?

答:为所有变量分配固定数量的空间.每个变量都有一个固定的地址.我们还可以从堆中分配额外的内存,并将这个额外内存的地址存储在指针中.这称为动态存储器方案.

从概念上讲,这类似于我解释的变量是粘滞便笺.所有变量(包括指针都是粘滞便笺).但是,指针是特殊的,因为它们引用了一个内存位置(就像引用JavaScript中的对象一样).

然而,这是相似之处的结束.以下是不同之处:

  1. 在C中,所有内容都按值传递(包括指针中的地址).要传递引用,您需要通过指针使用间接.JavaScript只按值传递基元.传递引用是由引擎透明处理的,就像传递任何其他变量一样.
  2. 在C中,您可以创建指向原始数据类型的指针int.在JavaScript中,您无法创建对原始值的引用number.所有基元始终按值存储.
  3. 在C中,您可以对指针执行各种操作.这称为指针算术.JavaScript没有指针.它只有参考.因此,您无法执行任何指针运算.

除了这三者之外,C和JavaScript之间的最大区别在于JavaScript中的所有变量实际上都是指针.因为JavaScript是动态语言,所以可以使用相同的变量来存储numberstring在不同的时间点.

JavaScript是一种解释型语言,解释器通常用C++编写.因此,JavaScript中的所有变量都映射到宿主语言中的对象(甚至原语).

当我们在JavaScript中声明一个变量时,解释器会为它创建一个新的泛型变量.然后,当我们为它赋值(是原语或引用)时,解释器只是为它分配一个新对象.在内部,它知道哪些对象是原始的,哪些是实际的对象.

从概念上讲,这就像做这样的事情:

JSGenericObject ten = new JSNumber(10); // var ten = 10;
Run Code Online (Sandbox Code Playgroud)

问:这是什么意思?

答:这意味着JavaScript中的所有值(原语和对象)都是从堆中分配的.甚至变量本身也是从堆中分配的.声明从堆栈分配基元并且只从堆分配对象是错误的.这是C和JavaScript之间的最大区别.

  • 嗨Aadit M Shah,你的回答很清楚.尽管它的长度,它真的很有趣,并抓住我的注意力.我从头到尾都读过它.但是......(保守说)我需要像上面那样对堆和堆栈进行解释,而不是你花时间去解释的事情.我评价你回答但是...我不能接受它.你能不能对堆和堆栈做一个例子,我肯定会非常感激.谢谢. (2认同)
  • aadit m shah,你的比喻非常有用,真的有助于更好地理解材料!感谢分享. (2认同)
  • 嘿,喜欢这个比喻!阅读它直到最后,只是想添加注释8 :).只是为了表明你正在操纵同一个对象:http://jsfiddle.net/DefML/ (2认同)

Tal*_*lha 62

A variable可以包含两种值类型之一:primitive valuesreference values.

  • Primitive values是存储在堆栈中的数据.
  • Primitive value 直接存储在变量访问的位置.
  • Reference values是存储在堆中的对象.
  • Reference value 存储在变量位置的是指向存储对象的存储器中的位置的指针.
  • 原始类型包括Undefined,Null,Boolean,Number,或String.

基础:

对象是属性的聚合.财产可以引用objectprimitive.Primitives are values,他们没有财产.

更新:

JavaScript有6种原始数据类型:String,Number,Boolean,Null,Undefined,Symbol(ES6中的新增功能).除了null和undefined之外,所有基元值都具有包围原始值的对象等价物,例如String对象环绕字符串基元.所有原语都是不可变的.

  • 是.但原始类型包括:`string,number,boolean,null,undefined` - 带有大写"S"的`String`是`string`的对象包装器.请参阅:[MDN词汇表 - 原语](https://developer.mozilla.org/en-US/docs/JavaScript/Glossary#primitive) (7认同)
  • 值是存储在堆栈还是堆上不是由JS中的类型决定的.它取决于它所存储的变量的寿命(受范围,尤其是闭合影响,并取决于引擎的分析能力). (6认同)
  • 重要的区别在于,当一个引用值传递给一个函数,并且该函数修改了它的内容时,调用者和任何其他引用该对象的函数都会看到该更改. (3认同)