技術頻道導航
HTML/CSS
.NET技術
IIS技術
PHP技術
Js/JQuery
Photoshop
Fireworks
服務器技術
操作系統(tǒng)
網站運營

贊助商

分類目錄

贊助商

最新文章

搜索

詳解JS按值傳遞與按引用傳遞的方法及區(qū)別

作者:admin    時間:2022-5-26 9:49:52    瀏覽:

在 JavaScript 中,當調用函數(shù)時,參數(shù)可以通過兩種方式傳遞,按值傳遞或按引用傳遞(地址)。 原始數(shù)據(jù)類型(如stringnumber、nullundefined、boolean)是按值傳遞的,而非原始數(shù)據(jù)類型(如對象、數(shù)組和函數(shù))在 Javascript 中是按引用傳遞的。本文將了解兩者之間的區(qū)別以及何時使用哪種方法。

原始和非原始數(shù)據(jù)類型

在理解 JavaScript 中的值傳遞和引用傳遞之前,我們先來了解一下什么是原始數(shù)據(jù)類型和非原始數(shù)據(jù)類型。因此,在 JavaScript 中,數(shù)據(jù)類型分為兩大類:

  • 原始
  • 非原始

stringnumber、nullundefined、boolean等數(shù)據(jù)類型屬于原始數(shù)據(jù)類型,而所有對象、數(shù)組函數(shù)都屬于非原始或引用數(shù)據(jù)類型。

原始數(shù)據(jù)類型按值進行比較。如果兩個值相同,則它們嚴格相等。

var num1 = 40;
var num2 = 40;
numb1 === num2; // true

var string1 = 'webkka.com';
var string2 = 'webkaka.com';
string1 === string2; // true

非原始數(shù)據(jù)類型

var myArray = [ '卡卡網', 'WebKaka' ];
myArray[1] = 'Tutorial';
console.log(myArray) // [ '卡卡網', 'Tutorial' ];

對象和數(shù)組不按值進行比較。這意味著即使兩個對象和數(shù)組分別具有相同的值和屬性或相同的元素,它們也不是嚴格相等的。

var obj1 = { 
    'website': '卡卡網',
    'topic': 'JavaScript'
    };
    
var obj2 = {    
   'website': 'webkaka.com',
   'topic': 'JavaScript'
    };
    
obj1 === obj2;  // false

var myArray1 = [ '卡卡網', 'webkaka.com' ];
var myArray2 = [ '卡卡網', 'webkaka.com' ];
arr1 === arr2;  // false

只有當兩個對象引用同一個對象時,它們才是嚴格相等的。

var obj1 = { 
    'website': 'webkaka.com',
    'topic': 'JavaScript'
    };
    
var obj2 = obj1;    
obj1 === obj2;  // true

非原始值有時也稱為引用類型,因為它們不是值,而是通過引用進行比較。

在 JavaScript 中按值傳遞

JavaScript 中的按值傳遞意味著實際參數(shù)值的副本在內存中進行,即完成了新的內存分配,并且所有更改都在該新值中進行(即復制的值)。原始值和復制值彼此獨立,因為它們在內存中的空間不同,即在更改函數(shù)內部的值時,函數(shù)外部的變量不受影響。

用簡單的語言,我們可以理解為,在按值傳遞中,函數(shù)接收變量的副本,該副本獨立于最初傳遞的變量。

JavaScript 中的按值傳遞需要更多空間,因為函數(shù)會獲取實際內容的副本,因此會在內存中創(chuàng)建一個新變量。

在這個概念中,= 運算符起著很大的作用。當我們創(chuàng)建一個變量時,= 運算符會注意到你是為該變量分配原始值還是非原始值,然后相應地工作。

注意:當我們使用 = 運算符時,有一個函數(shù)調用(在幕后)按值(或引用)完成傳遞。

當我們?yōu)樽兞糠峙湓贾禃r,= 運算符會在內存中(例如在地址2001處)設置一個空間(位置/地址),以將該變量(num1)的數(shù)據(jù)存儲到該地址。當我們創(chuàng)建一個新變量num2(比如地址2002)并為其分配前一個變量num1的值時,= 運算符在內存中創(chuàng)建新空間,它獨立于地址為2001的前一個變量num1。因此,這復制了原始變量num1的值到內存中的兩個單獨的位置(地址為2001和2002)。

let num1 = 70
let num2 = num1

console.log(num1) // 70
console.log(num2) // 70

num1 = 40

console.log(num1) // 40
console.log(num2) // 70

在這里,我們?yōu)?num1 分配了一個值70。這會在內存中創(chuàng)建一個名為num1的空間,地址為2001(假設)。當我們創(chuàng)建一個變量num2并為其分配num1的值時, = 運算符注意到我們正在處理一個原始值,因此它在內存中創(chuàng)建一個地址為2002的新空間并為其分配一個num1值的副本,即70?,F(xiàn)在我們可以看到這兩個變量在內存中都有不同的空間,并且都具有70的值。

現(xiàn)在,如果我們更改num1的值,那么num2將不起作用,因為它在內存中有自己的獨立空間,現(xiàn)在它與num2的值無關,因為它們在內存中都有不同的空間(地址)。

讓我們通過另一個例子更好地理解這一點: 

function multiplication(tmp) {
    tmp = tmp * 50;
    return tmp;
}
var num = 30;
var result = multiplication(num);
console.log(num); // 30
console.log(result); // 1500

從上面的代碼中,我們可以看到函數(shù)乘法接受一個參數(shù)并改變它的值。

然后我們聲明了一個變量 num,其值為 30。

 

之后,我們將變量 num 傳遞給乘法函數(shù)。Javascript 自動將變量 num 的值復制到變量 tmp。所以,這里的 tmp 是一個新變量,它在內存中分配了一個新的空間,并且與 num 無關。

 

現(xiàn)在函數(shù)乘法所做的所有更改都直接對變量 tmp 進行;因此 num 的值不受影響。

這是因為在名為 tmp 的內存中創(chuàng)建了變量 num 的單獨副本,初始值為 30,計算后變?yōu)?1500。

 

tmp和num彼此沒有聯(lián)系,即它們彼此獨立。

在 JavaScript 中通過引用傳遞

與 JavaScript 中的值傳遞不同,JavaScript 中的引用傳遞不會在內存中創(chuàng)建新空間,而是傳遞實際參數(shù)的引用/地址,這意味著函數(shù)可以訪問變量的原始值。因此,如果我們改變函數(shù)內部變量的值,那么原始值也會改變。

它不會創(chuàng)建副本,而是對原始變量起作用,因此函數(shù)內部所做的所有更改也會影響原始變量。

 

與 JavaScript 中的按值傳遞不同,這里當?shù)忍栠\算符識別出變量obj1被設置為等于一個對象時,它會創(chuàng)建一個新的內存空間并將obj1指向3005(地址假設)?,F(xiàn)在,當我們創(chuàng)建一個新變量obj2并將其分配給obj1的值時,等號運算符識別出我們正在處理非原始數(shù)據(jù)類型,因此它指向obj1指向的相同地址。因此我們可以看到沒有創(chuàng)建新的內存空間,兩個變量都指向obj1指向的同一個地址。

let obj1 = {website: "webkaka.com"}
let obj2 = obj1;

console.log(obj1)     // {website: "webkaka.com"}
console.log(obj2)     // {website: "webkaka.com"}

obj1.website = "webkaka tutorial"

console.log(obj1)     // {website: "webkaka tutorial"}
console.log(obj2)     // {website: "webkaka tutorial"}

在上面的示例中,我們創(chuàng)建了一個變量obj1并將其設置為一個對象,然后我們將另一個變量obj2的值設置為obj1。由于等號運算符表明我們正在處理非原始數(shù)據(jù)類型,因此它不會創(chuàng)建新的內存空間,而是將obj2指向obj1所指向的相同內存空間。因此,當我們改變obj1的值時,obj2的值也會改變,因為obj2也指向與obj1相同的內存空間。

對象中的引用傳遞(帶函數(shù))

let originalObj = {
name: "webkaka.com",
rating: 4.5,
topic: "JavaScript"
};

function demo(tmpObj) { 
  tmpObj.rating = 5; 
  console.log(tmpObj.rating); 


console.log(originalObj.rating);    // 4.5
demo(originalObj);             // 5
console.log(originalObj.rating);    //5

從上面的例子中,我們可以看到改變 tmpObj 的值時originalObj的值也會改變。這樣做的原因是,當我們調用demo并傳遞對象時,originalObj是通過其引用傳遞的,因此本地參數(shù)tempObj將指向我們定義的同一個對象,即originalObj

 

因此,在這種情況下,我們不是在處理兩個獨立的副本,而是有指向同一個對象的變量,因此對該對象所做的任何更改都將對另一個變量可見。

在數(shù)組中通過引用傳遞(帶函數(shù))

let originalArr = ["卡卡網", "WebKaka","is", "the"];

function pushArray(tmpArr) { 
  tmpArr.push('best')
  console.log(tmpArr); 


console.log(originalArr);    // ["卡卡網", "WebKaka", "is", "the"]
pushArray(originalArr);      // ["卡卡網", "WebKaka", "is", "the", "best"]
console.log(originalArr);    // ["卡卡網", "WebKaka", "is", "the", "best"]

在這里,當我們嘗試向存儲在tempArr的數(shù)組中添加新項目時,它也會影響 originalArr 數(shù)組。發(fā)生這種情況是因為一個數(shù)組沒有兩個單獨的副本,我們只處理一個數(shù)組。變量tempArr引用了在變量originalArr中初始化的同一個數(shù)組。

這個例子表明,像對象一樣,在數(shù)組中改變tempArr的值時originalArr的值也會自動改變。

因此,我們可以得出結論,所有非原始數(shù)據(jù)類型都通過引用進行交互,因此當我們將它們的值設置為彼此相等或將它們傳遞給函數(shù)時,它們都指向相同的內存空間(地址),所以每當我們改變值之一,然后所有值都會更改。

 

何時使用按值傳遞或按指引傳遞?

在 JavaScript 中的按值傳遞,會創(chuàng)建變量的新副本,并且對新變量所做的任何更改都與原始變量無關,因此當我們想要跟蹤初始變量而不想失去它的值時,就使用按值傳遞。

當我們傳遞大的參數(shù)時,在 JavaScript 中最好使用按引用傳遞,因為在被調用函數(shù)中沒有單獨的副本,因此不會浪費內存,因此程序效率更高。

總結

  • 在 JavaScript 中,我們有值類型(也稱為基元)和引用類型(非基元)。
  • 基元是數(shù)字(number)、字符串(string)、布爾值(boolean)、未定義(undefined)和空值(null),而非基元是對象、函數(shù)和數(shù)組。
  • 在 JavaScript 中按值傳遞時,會創(chuàng)建原始變量的副本,因此對復制的變量所做的任何更改都不會影響原始變量。
  • 在 JavaScript 中按引用傳遞時,我們傳遞實際參數(shù)的引用,不會在內存中創(chuàng)建副本。

您可能對以下文章也感興趣

標簽: 值傳遞  引用傳遞  
相關文章
    x
    • 站長推薦
    /* 左側顯示文章內容目錄 */