|
|
|
|
|
在 JavaScript 中,當(dāng)調(diào)用函數(shù)時(shí),參數(shù)可以通過(guò)兩種方式傳遞,按值傳遞或按引用傳遞(地址)。 原始數(shù)據(jù)類型(如string、number、null、undefined、boolean)是按值傳遞的,而非原始數(shù)據(jù)類型(如對(duì)象、數(shù)組和函數(shù))在 Javascript 中是按引用傳遞的。本文將了解兩者之間的區(qū)別以及何時(shí)使用哪種方法。
在理解 JavaScript 中的值傳遞和引用傳遞之前,我們先來(lái)了解一下什么是原始數(shù)據(jù)類型和非原始數(shù)據(jù)類型。因此,在 JavaScript 中,數(shù)據(jù)類型分為兩大類:
string、number、null、undefined、boolean等數(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 中的按值傳遞意味著實(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 中的值傳遞不同,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)存空間。
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è)變量可見。
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ì)更改。
在 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)存,因此程序效率更高。
您可能對(duì)以下文章也感興趣