|
|
|
|
|
在 JavaScript 中,當調用函數(shù)時,參數(shù)可以通過兩種方式傳遞,按值傳遞或按引用傳遞(地址)。 原始數(shù)據(jù)類型(如string、number、null、undefined、boolean)是按值傳遞的,而非原始數(shù)據(jù)類型(如對象、數(shù)組和函數(shù))在 Javascript 中是按引用傳遞的。本文將了解兩者之間的區(qū)別以及何時使用哪種方法。
在理解 JavaScript 中的值傳遞和引用傳遞之前,我們先來了解一下什么是原始數(shù)據(jù)類型和非原始數(shù)據(jù)類型。因此,在 JavaScript 中,數(shù)據(jù)類型分為兩大類:
string、number、null、undefined、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 中的按值傳遞意味著實際參數(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 中的引用傳遞不會在內存中創(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
相同的內存空間。
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
。
因此,在這種情況下,我們不是在處理兩個獨立的副本,而是有指向同一個對象的變量,因此對該對象所做的任何更改都將對另一個變量可見。
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ù)中沒有單獨的副本,因此不會浪費內存,因此程序效率更高。
您可能對以下文章也感興趣