簡體   English   中英

原始值與參考值

[英]Primitive value vs Reference value

我讀了一本名為“Web 開發人員的專業 Javascript”的書,它說:“變量由引用值或原始值分配。引用值是存儲在內存中的對象”。 然后它沒有說明原始值是如何存儲的。 所以我想它沒有存儲在內存中。 基於此,當我有這樣的腳本時:

var foo = 123;

Javascript 如何記住foo變量以備后用?

好的,假設您的變量是一張紙 - 一個便簽。

注 1:變量便簽

現在,便簽非常小。 你只能在上面寫一點信息。 如果你想寫更多的信息,你需要更多的便簽,但這不是問題。 想象一下,您有無窮無盡的便利貼。

注意 2:您有無窮無盡的便簽,其中存儲了少量信息。

太好了,你可以在便簽上寫什么? 我可以寫:

  1. 是或否(布爾值)。
  2. 我的年齡(一個數字)。
  3. 我的名字(一個字符串)。
  4. 什么都沒有(未定義)。
  5. 塗鴉或其他任何對我來說毫無意義的東西( null )。

所以我們可以在便利貼上寫一些簡單的東西(讓我們居高臨下地稱它們為原始的東西)。

注3:您可以在便簽上寫原始的東西。

因此,假設我在便利貼上寫了30 ,以提醒自己為今晚我在我家舉辦的小聚會買 30 片奶酪(我的朋友很少)。

當我去把我的便利貼貼在冰箱上時,我看到我妻子在冰箱上貼了另一張便利貼,上面也寫着30 (提醒我她的生日是這個月的 30 號)。

問:兩個便簽都傳達了相同的信息嗎?

A:是的,他們都說30 我們不知道是 30 片奶酪還是一個月的第 30 天,坦率地說,我們不在乎。 對於一個不知道更好的人來說,一切都一樣。

var slicesOfCheese = 30;
var wifesBirthdate = 30;

alert(slicesOfCheese === wifesBirthdate); // true

注釋 4:兩個寫有相同內容的便簽傳達相同的信息,即使它們是兩個不同的便簽。

今晚我真的很興奮——和老朋友一起出去玩,玩得很開心。 然后我的一些朋友打電話給我,說他們將無法參加聚會。

所以我去我的冰箱,把我便利貼上的30擦掉(不是我妻子的便利貼——那會讓她很生氣)並把它變成20

注 5:您可以擦除便利貼上寫的內容並寫其他內容。

問:這一切都很好,但如果我的妻子想要寫一份雜貨清單,讓我在出去買奶酪的時候去拿,該怎么辦? 她是否需要為每件物品寫一個便利貼?

A:不,她會拿一長串紙,然后在紙上寫下食品清單。 然后她會寫一張便條,告訴我在哪里可以找到雜貨清單。

那么這里發生了什么?

  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

注 7:兩個便簽僅在指代同一對象時傳達相同的信息。

這意味着,如果我的妻子做了兩個便利貼提醒我購物清單在哪里,那么這兩個便利貼包含相同的信息。 所以這:

親愛的,雜貨清單在你的鍵盤下面。

包含與以下相同的信息:

不要忘記雜貨清單在您的鍵盤下方。

在編程方面:

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

alert(groceryList1 === groceryList2); // true

這就是您需要了解的有關 JavaScript 中的原語引用的全部內容。 無需涉及和動態內存分配之類的內容。 如果您使用 C/C++ 編程,這很重要。

編輯1:哦,最重要的事情是,當你傳遞變量身邊你基本上傳遞的原始的價值參考價值的參考

這只是一種精心設計的說法,即您將一個便利貼上寫的所有內容復制到另一個便利貼(無論您復制的是原始值還是引用都無關緊要)。

復制引用時,被引用的對象不會移動(例如,我妻子的購物清單將始終保留在我的鍵盤下,但我可以將復制的便利貼帶到任何我想要的地方——原始便利貼仍將在冰箱上)。

編輯 2:回應@LacViet 發表的評論:

對於初學者來說,我們談論的是 JavaScript,而 JavaScript 沒有stackheap 它是一種動態語言,JavaScript 中的所有變量都是動態的。 為了解釋差異,我將它與 C 進行比較。

考慮以下 C 程序:

#include <stdio.h>

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

當我們編譯這個程序時,我們會得到一個可執行文件。 可執行文件被分成多個段(或節)。 這些段包括堆棧段、代碼段、數據段、額外段等。

  1. 堆棧段用於在調用函數或中斷處理程序時存儲程序的狀態。 例如,當功能f調用函數g然后功能的狀態f (在此時的寄存器的所有值)被保存在堆棧中。 g將控制權返回給f這些值就會恢復。
  2. 代碼段保存要由處理器執行的實際代碼。 它包含一系列處理器必須執行的指令,如add eax, ebx (其中add是操作碼, eax & ebx是參數)。 該指令將寄存器eaxebx的內容相加,並將結果存儲在寄存器eax
  3. 數據段用於為變量保留空間。 例如,在上面的程序中,我們需要為整數abc保留空間。 此外,我們還需要為字符串常量"%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;
}

因為我們想動態分配內存,所以需要使用指針。 這是因為我們想要使用相同的變量來指向任意內存位置(不一定每次都相同的內存位置)。

所以我們創建了一個名為aint指針( int * )。 對於空間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;

問:這是什么意思?

A:這意味着 JavaScript 中的所有值(原語和對象)都是從堆中分配的。 甚至變量本身也是從堆中分配的。 說原語是從堆棧中分配的,而只有對象是從堆中分配的,這是錯誤的。 這是 C 和 JavaScript 最大的區別。

variable可以保存兩種值類型之一: primitive valuesreference values

  • Primitive values是存儲在堆棧上的數據。
  • Primitive value直接存儲在變量訪問的位置。
  • Reference values是存儲在堆中的對象
  • 存儲在變量位置的Reference value是指向存儲對象的內存位置的指針。
  • 原始類型包括UndefinedNullBooleanNumberString

基礎知識:

對象是屬性的聚合。 一個屬性可以引用一個object或一個primitive Primitives are values ,它們沒有屬性。

更新:

JavaScript 有 6 種原始數據類型: StringNumberBooleanNullUndefinedSymbol (ES6 中的新內容)。 除了 null 和 undefined 之外,所有原始值都具有環繞原始值的對象等效項,例如String對象環繞字符串原始值。 所有原語都是不可變的。

在 javascript 中, Primitive values是存儲在stack上的數據

Primitive value直接存儲在變量訪問的位置。

Reference values是存儲在heap對象

存儲在變量位置的引用值是指向存儲對象的內存位置的指針。

JavaScript 支持五種原始數據類型: number, string, Boolean, undefined, and null

這些類型被稱為原始類型,因為它們是可以構建更復雜類型的基本構建塊。

在這五種類型中,從實際存儲數據的角度來看,只有number, string, and Boolean是真正的數據類型。

Undefined and null是在特殊情況下出現的類型。 primitive type在內存中具有固定大小。 例如,一個數字占用 8 個字節的內存,而一個布爾值只能用一位來表示。

並且引用類型可以是任意長度——它們沒有固定的大小。

原始類型在內存中具有固定大小。 例如,一個數字占用 8 個字節的內存,而一個布爾值只能用一位來表示。 數字類型是最大的原始類型。 如果每個 JavaScript 變量保留 8 個字節的內存,則該變量可以直接保存任何原始值。

這是一種過度簡化,並不打算作為實際 JavaScript 實現的描述。

然而,引用類型是另一回事。 例如,對象可以是任意長度——它們沒有固定的大小。 數組也是如此:一個數組可以有任意數量的元素。 同樣,一個函數可以包含任意數量的 JavaScript 代碼。 由於這些類型沒有固定的大小,它們的值不能直接存儲在與每個變量關聯的 8 個字節的內存中。 相反,該變量存儲對該值的引用。 通常,此引用是某種形式的指針或內存地址。 它不是數據值本身,而是告訴變量去哪里尋找值。

原始類型和引用類型之間的區別很重要,因為它們的行為不同。 考慮以下使用數字(原始類型)的代碼:

var a = 3.14;  // Declare and initialize a variable
var b = a;     // Copy the variable's value to a new variable
a = 4;         // Modify the value of the original variable
alert(b)       // Displays 3.14; the copy has not changed

這段代碼沒有什么令人驚訝的。 現在考慮如果我們稍微更改代碼以使用數組(引用類型)而不是數字會發生什么:

var a = [1,2,3];  // Initialize a variable to refer to an array
var b = a;        // Copy that reference into a new variable
a[0] = 99;        // Modify the array using the original reference
alert(b);         // Display the changed array [99,2,3] using the new reference

如果您對這個結果並不感到驚訝,那么您已經非常熟悉原始類型和引用類型之間的區別。 如果確實令人驚訝,請仔細查看第二行。 請注意,在此語句中分配的是對數組值的引用,而不是數組本身。 在第二行代碼之后,我們仍然只有一個數組對象; 我們只是碰巧有兩個引用它。

正如在接受的答案和最高投票的答案中已經提到的,原始值是存儲在堆棧中的數據,而引用值是存儲在堆中的對象。

但這實際上意味着什么? 它們在您的代碼中的表現有何不同?

通過值訪問原始值。 因此,當您將具有原始值的變量 (var a) 分配給另一個變量 (var b) 時,變量 (a) 的值會被復制到新變量 (b) 中。 而當你改變新變量(b)的值時,原變量的值保持不變。

當您將具有引用值的變量 (var x) 分配給另一個變量 (var y) 時,存儲在變量 (x) 中的值也會復制到新變量 (y) 中。 不同之處在於,現在存儲在兩個變量中的值都是存儲在堆中的實際對象的引用。 這意味着,x 和 y 都指向同一個對象。 所以當你改變新變量(y)的值時,原來的value(x)的值也改變了(因為堆中的實際對象改變了)。

原始值是在語言實現的最低級別表示的數據,在 JavaScript 中是以下類型之一:數字、字符串、布爾值、未定義和空值。

原始與參考:

變量是 javascript 引擎為保存數據而保留的內存 (RAM) 段。 在這些變量中可以存儲兩種值:

  • 實際數據:例如字符串或數字。 這意味着實際內存(即內存中的字節)保存數據本身。
  • 對對象的引用:例如數組對象或函數對象。 這意味着這個變量只保存對這個對象所在的內存中另一個位置的引用

原始值和參考值的區別:

當一個原始值被分配給一個變量時,數據(位的值)就被復制了。 例如:

原始值:

 let num1 = 10; // What happens here is that the bits which are stored in the memory container // (ie variable num1) are literally copied into memory container num2 let num2 = num1;

參考值:

 let objRef1 = {prop1: 1} // we are storing the reference of the object which is stored in objRef1 // into objRef2. Now they are pointing to the same object let objRef2 = objRef1; // We are manipulating the object prop1 property via the refernce which // is stored in the variable objRef2 objRef2.prop1 = 2; // The object which objRef1 was pointing to was mutated in the previous action console.log(objRef1);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM