技術(shù)頻道導(dǎo)航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運(yùn)營(yíng)

贊助商

分類目錄

贊助商

最新文章

搜索

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

作者:admin    時(shí)間:2022-5-26 9:49:52    瀏覽:

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

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

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

  • 原始
  • 非原始

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

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

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

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

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

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

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

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

var myArray1 = [ '卡卡網(wǎng)', 'webkaka.com' ];
var myArray2 = [ '卡卡網(wǎng)', 'webkaka.com' ];
arr1 === arr2;  // false

只有當(dāng)兩個(gè)對(duì)象引用同一個(gè)對(duì)象時(shí),它們才是嚴(yán)格相等的。

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

非原始值有時(shí)也稱為引用類型,因?yàn)樗鼈儾皇侵?,而是通過(guò)引用進(jìn)行比較。

在 JavaScript 中按值傳遞

JavaScript 中的按值傳遞意味著實(shí)際參數(shù)值的副本在內(nèi)存中進(jìn)行,即完成了新的內(nèi)存分配,并且所有更改都在該新值中進(jìn)行(即復(fù)制的值)。原始值和復(fù)制值彼此獨(dú)立,因?yàn)樗鼈冊(cè)趦?nèi)存中的空間不同,即在更改函數(shù)內(nèi)部的值時(shí),函數(shù)外部的變量不受影響。

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

JavaScript 中的按值傳遞需要更多空間,因?yàn)楹瘮?shù)會(huì)獲取實(shí)際內(nèi)容的副本,因此會(huì)在內(nèi)存中創(chuàng)建一個(gè)新變量。

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

注意:當(dāng)我們使用 = 運(yùn)算符時(shí),有一個(gè)函數(shù)調(diào)用(在幕后)按值(或引用)完成傳遞。

當(dāng)我們?yōu)樽兞糠峙湓贾禃r(shí),= 運(yùn)算符會(huì)在內(nèi)存中(例如在地址2001處)設(shè)置一個(gè)空間(位置/地址),以將該變量(num1)的數(shù)據(jù)存儲(chǔ)到該地址。當(dāng)我們創(chuàng)建一個(gè)新變量num2(比如地址2002)并為其分配前一個(gè)變量num1的值時(shí),= 運(yùn)算符在內(nèi)存中創(chuàng)建新空間,它獨(dú)立于地址為2001的前一個(gè)變量num1。因此,這復(fù)制了原始變量num1的值到內(nèi)存中的兩個(gè)單獨(dú)的位置(地址為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 分配了一個(gè)值70。這會(huì)在內(nèi)存中創(chuàng)建一個(gè)名為num1的空間,地址為2001(假設(shè))。當(dāng)我們創(chuàng)建一個(gè)變量num2并為其分配num1的值時(shí), = 運(yùn)算符注意到我們正在處理一個(gè)原始值,因此它在內(nèi)存中創(chuàng)建一個(gè)地址為2002的新空間并為其分配一個(gè)num1值的副本,即70?,F(xiàn)在我們可以看到這兩個(gè)變量在內(nèi)存中都有不同的空間,并且都具有70的值。

現(xiàn)在,如果我們更改num1的值,那么num2將不起作用,因?yàn)樗趦?nèi)存中有自己的獨(dú)立空間,現(xiàn)在它與num2的值無(wú)關(guān),因?yàn)樗鼈冊(cè)趦?nèi)存中都有不同的空間(地址)。

讓我們通過(guò)另一個(gè)例子更好地理解這一點(diǎn): 

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

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

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

 

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

 

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

這是因?yàn)樵诿麨?tmp 的內(nèi)存中創(chuàng)建了變量 num 的單獨(dú)副本,初始值為 30,計(jì)算后變?yōu)?1500。

 

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

在 JavaScript 中通過(guò)引用傳遞

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

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

 

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

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)建了一個(gè)變量obj1并將其設(shè)置為一個(gè)對(duì)象,然后我們將另一個(gè)變量obj2的值設(shè)置為obj1。由于等號(hào)運(yùn)算符表明我們正在處理非原始數(shù)據(jù)類型,因此它不會(huì)創(chuàng)建新的內(nèi)存空間,而是將obj2指向obj1所指向的相同內(nèi)存空間。因此,當(dāng)我們改變obj1的值時(shí),obj2的值也會(huì)改變,因?yàn)?code>obj2也指向與obj1相同的內(nèi)存空間。

對(duì)象中的引用傳遞(帶函數(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 的值時(shí)originalObj的值也會(huì)改變。這樣做的原因是,當(dāng)我們調(diào)用demo并傳遞對(duì)象時(shí),originalObj是通過(guò)其引用傳遞的,因此本地參數(shù)tempObj將指向我們定義的同一個(gè)對(duì)象,即originalObj。

 

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

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

let originalArr = ["卡卡網(wǎng)", "WebKaka","is", "the"];

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


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

在這里,當(dāng)我們嘗試向存儲(chǔ)在tempArr的數(shù)組中添加新項(xiàng)目時(shí),它也會(huì)影響 originalArr 數(shù)組。發(fā)生這種情況是因?yàn)橐粋€(gè)數(shù)組沒(méi)有兩個(gè)單獨(dú)的副本,我們只處理一個(gè)數(shù)組。變量tempArr引用了在變量originalArr中初始化的同一個(gè)數(shù)組。

這個(gè)例子表明,像對(duì)象一樣,在數(shù)組中改變tempArr的值時(shí)originalArr的值也會(huì)自動(dòng)改變。

因此,我們可以得出結(jié)論,所有非原始數(shù)據(jù)類型都通過(guò)引用進(jìn)行交互,因此當(dāng)我們將它們的值設(shè)置為彼此相等或?qū)⑺鼈儌鬟f給函數(shù)時(shí),它們都指向相同的內(nèi)存空間(地址),所以每當(dāng)我們改變值之一,然后所有值都會(huì)更改。

 

何時(shí)使用按值傳遞或按指引傳遞?

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

當(dāng)我們傳遞大的參數(shù)時(shí),在 JavaScript 中最好使用按引用傳遞,因?yàn)樵诒徽{(diào)用函數(shù)中沒(méi)有單獨(dú)的副本,因此不會(huì)浪費(fèi)內(nèi)存,因此程序效率更高。

總結(jié)

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

您可能對(duì)以下文章也感興趣

標(biāo)簽: 值傳遞  引用傳遞  
相關(guān)文章
    x
    • 站長(zhǎng)推薦
    /* 左側(cè)顯示文章內(nèi)容目錄 */