摘要:數(shù)值類型引用類型有種通過(guò)復(fù)制數(shù)值傳值的數(shù)據(jù)類型。我們稱之為原始基本數(shù)據(jù)類型還有三種通過(guò)引用傳值的數(shù)據(jù)類型。當(dāng)?shù)仁竭\(yùn)算符和用于引用型變量時(shí),他們會(huì)檢查引用。這是中的地方在內(nèi)存中的映射包含了函數(shù)的引用,其他變量則包含基本數(shù)據(jù)類型的數(shù)據(jù)。
本文旨在了解如何復(fù)制對(duì)象、數(shù)組和函數(shù)以及如何將它們傳遞到函數(shù)中。知道引用類型復(fù)制的是什么。了解原始值是通過(guò)復(fù)制值來(lái)復(fù)制及傳遞的。
數(shù)值類型 & 引用類型JavaScript有5種通過(guò)復(fù)制數(shù)值傳值的數(shù)據(jù)類型:Boolean, null, undefined, String, and Number。我們稱之為原始/基本數(shù)據(jù)類型
JavaScript還有三種通過(guò)引用傳值的數(shù)據(jù)類型:Array, Function, and Object。從專業(yè)角度講,它們都是Objects, 故而統(tǒng)稱為對(duì)象。
若為一個(gè)基本數(shù)據(jù)類型的變量賦值,我們可以認(rèn)為變量包含了這個(gè)原始值。
var x = 10; var y = "abc"; var z = null;
這張圖形象的展示了變量在內(nèi)存中的存儲(chǔ)情況:
Variables | Values |
---|---|
x | 10 |
y | "abc" |
z | null |
當(dāng)我們用 = 將這些變量賦值給其他變量時(shí),我們把這些值拷貝給了這些新變量。他們通過(guò)值復(fù)制的。
var x = 10; var y = "abc"; var a = x; var b = y; console.log(x, y, a, b); // -> 10, "abc", 10, "abc"
a 和 x 現(xiàn)在的值都是10. b 和 y 都擁有值 "abc"。他們各自獨(dú)立,擁有相同的值,互不影響:
Variables | Values |
---|---|
x | 10 |
y | "abc" |
a | 10 |
b | "abc" |
改變其中一個(gè)值并不會(huì)影響另一個(gè)的值,彼此井水不犯河水,盡管后者曾經(jīng)復(fù)制與它:
var x = 10; var y = "abc"; var a = x; var b = y; x = 5; y= "def"; console.log(x, y, a, b); // -> 5 "def" 10 "abc"對(duì)象
非基本數(shù)據(jù)類型的變量會(huì)保存對(duì)值的引用(地址)。該引用指向內(nèi)存中對(duì)象的地址,變量實(shí)際不包含該實(shí)際值。
對(duì)象創(chuàng)建于計(jì)算機(jī)內(nèi)存中。當(dāng)我們寫(xiě)代碼 arr = [], 我們?cè)趦?nèi)存中創(chuàng)建了一個(gè)新數(shù)組, arr 中現(xiàn)在包含了新數(shù)組在內(nèi)存中的地址。
假設(shè)address(地址)是一種新的傳遞數(shù)據(jù)的數(shù)據(jù)類型,就像數(shù)字和字符串。address指向通過(guò)引用傳遞的值的內(nèi)存地址,就像字符串由"" 或 ""表示, address由 <> 表示。
當(dāng)我們賦值引用一個(gè)引用型變量時(shí),我們通常這樣書(shū)寫(xiě)代碼:
var arr = []; arr.push(1);
兩步的操作分別是:
1.
Variables | Values | Address | Objects | |
---|---|---|---|---|
arr | <#001> | #001 | [] |
2.
Variables | Values | Address | Objects | |
---|---|---|---|---|
arr | <#001> | #001 | [1] |
值,地址以及 變量 arr 的包含的值 是靜態(tài)不變的,僅僅是內(nèi)存中的數(shù)組改變了。當(dāng)我們對(duì)arr 進(jìn)行操作時(shí),例如添加新元素, JavaScript引擎會(huì)獲取 arr 在內(nèi)存中的地址 并操作該地址存儲(chǔ)的數(shù)據(jù)。
引用賦值當(dāng)一個(gè)引用型值即對(duì)象被用 = 賦值給另一個(gè)變量, 實(shí)際上復(fù)制過(guò)去的是那個(gè)引用型值的地址。對(duì)象通過(guò)引用賦值而不是直接傳值。對(duì)象本身是靜態(tài)不變的,唯一改變的 是對(duì)象的 引用 、地址。
var reference = [1]; var refCopy = reference;
內(nèi)存變化:
Variables | Values | Address | Objects | |
---|---|---|---|---|
reference | <#001> | #001 | [1] | |
refCopy | <#001> |
現(xiàn)在每個(gè)變量都包含了同一個(gè)數(shù)組的引用,它們地址相同,這意味著如果我們改變了這個(gè)引用即改變reference, refCopy 也會(huì)隨之改變,這一點(diǎn)與基本數(shù)據(jù)類型的值不一樣。
reference.push(2); console.log(reference, refCopy); // -> [1, 2], [1, 2]
Variables | Values | Address | Objects | |
---|---|---|---|---|
reference | <#001> | #001 | [1,2] | |
refCopy | <#001> | [1,2] |
重新復(fù)制會(huì)覆蓋舊值:
var obj = { first: "reference" };
內(nèi)存變化:
Variables | Values | Address | Objects | |
---|---|---|---|---|
obj | <#234> | #234 | { first: "reference" } |
重新賦值:
var obj = { first: "reference" }; obj = { second: "ref2" }
Address存儲(chǔ)了 obj 的變化 ,第一個(gè)對(duì)象仍在內(nèi)存,第二個(gè)對(duì)象也在:
Variables | Values | Address | Objects | |
---|---|---|---|---|
obj | <#678> | #234 | { first: "reference" } | |
#678 | { second: "ref2" } |
當(dāng)已經(jīng)存在的對(duì)象沒(méi)有被引用時(shí),如上邊的 #234 ,JavaScript會(huì)啟動(dòng)垃圾回收機(jī)制。這就意味著程序員失去了對(duì)該對(duì)象的所有引用,不能再使用這個(gè)對(duì)象,所以JavaScript可以安全地刪除它。 這時(shí),對(duì)象 { first: "reference" } 不能再被任何變量獲取,內(nèi)存會(huì)被回收。
== and ===當(dāng) 等式運(yùn)算符 == 和 === 用于引用型變量時(shí), 他們會(huì)檢查引用。 如果多個(gè)變量包含同一項(xiàng)目的引用時(shí), 結(jié)果會(huì)返回 true
var arrRef = ["Hi!"]; var arrRef2 = arrRef; console.log(arrRef === arrRef2); // -> true
如果他們是不同的對(duì)象,即使它們包含相同的內(nèi)容, 比較結(jié)果也會(huì)返回 false。
var arr1 = ["Hi!"]; var arr2 = ["Hi!"]; console.log(arr1 === arr2); // -> false對(duì)象的比較
如果想比較兩個(gè)對(duì)象的屬性是否一樣,比較運(yùn)算符會(huì)失去作用。我們必須編一個(gè)函數(shù)來(lái)檢查對(duì)象的每一條屬性和值是否相同。對(duì)于兩個(gè)數(shù)組,我們需要一個(gè)函數(shù)遍歷數(shù)組每項(xiàng)檢查是否相同。
函數(shù)傳參當(dāng)我們傳遞基本數(shù)據(jù)類型的值給一個(gè)函數(shù)時(shí),函數(shù)拷貝這個(gè)值作為自己的參數(shù)。效果和 = 相同:
var hundred = 100; var two = 2; function multiply(x, y) { // PAUSE return x * y; } var twoHundred = multiply(hundred, two);
上例中,我們將 hundred 賦值 100 。當(dāng)我們把他傳遞給 multiply, 變量x 獲得值 100 。如果用 = 賦值,值會(huì)被復(fù)制。 而且,hundred的值不會(huì)被影響。 這是multiply 中 //的地方在內(nèi)存中的映射:
Variables | Values | Address | Objects | |
---|---|---|---|---|
hundred | 100 | #333 | function(x, y) {… } | |
two | 2 | |||
multiply | <#333> | |||
x | 100 | |||
y | 2 | |||
twoHundred | undefined |
multiply包含了函數(shù)的引用,其他變量則包含基本數(shù)據(jù)類型的數(shù)據(jù)。
twoHundred 是 undefined 因?yàn)槲覀冞€沒(méi)有函數(shù)返回結(jié)果,在函數(shù)返回結(jié)果前,它等于
undefined。
純函數(shù)是指不影響外部作用域的函數(shù)。只要一個(gè)函數(shù)只接受基本數(shù)據(jù)類型的值作為參數(shù)并且不適用任何外部范圍的變量,他就是純函數(shù)不會(huì)污染外部作用域。所有純函數(shù)的變量在函數(shù)返回結(jié)果后會(huì)進(jìn)入JavaScript的垃圾回收機(jī)制。
然而,接受一個(gè)對(duì)象(作為參數(shù))的函數(shù)會(huì)改變他周圍作用域的狀態(tài)。如果函數(shù)接受一個(gè)數(shù)組的引用并改變了它指向的數(shù)組,可能是添加元素,引用這個(gè)數(shù)組的外部變量會(huì)見(jiàn)證這些變化。當(dāng)函數(shù)返回結(jié)果后,產(chǎn)生的改變會(huì)影響外部作用域。這會(huì)導(dǎo)致很難追蹤到的負(fù)面影響。
許多本地?cái)?shù)組函數(shù)包含Array.map 和 Array.filter,因此都以純函數(shù)編寫(xiě)。 它們接收一個(gè)數(shù)組作為參數(shù),在內(nèi)部 它們會(huì)復(fù)制該數(shù)組操作這個(gè)副本數(shù)組而不是原數(shù)組。這使得原數(shù)組不被接觸得到,從而外部作用域不受影響,返回一個(gè)新數(shù)組的引用。
對(duì)比一下純函數(shù) 和 非純函數(shù):
function changeAgeImpure(person) { person.age = 25; return person; } var alex = { name: "Alex", age: 30 }; var changedAlex = changeAgeImpure(alex); console.log(alex); // -> { name: "Alex", age: 25 } console.log(changedAlex); // -> { name: "Alex", age: 25 }
非純函數(shù)接受了對(duì)象,改變了object的 age屬性 為25,由于它對(duì)前面聲明的引用直接起作用,直接改變了alex 對(duì)象。注意當(dāng)返回person對(duì)象時(shí),它返回與傳遞的相同的對(duì)象。alex 和 alexChanged 包含了對(duì)同一個(gè)對(duì)象的引用,既返回了person 變量又返回了有相同引用的新變量。
純函數(shù):
function changeAgePure(person) { var newPersonObj = JSON.parse(JSON.stringify(person)); newPersonObj.age = 25; return newPersonObj; } var alex = { name: "Alex", age: 30 }; var alexChanged = changeAgePure(alex); console.log(alex); // -> { name: "Alex", age: 30 } console.log(alexChanged); // -> { name: "Alex", age: 25 }
在這個(gè)函數(shù)中,我們利用 JSON.stringify 將傳遞的對(duì)象轉(zhuǎn)化成字符串,然后用JSON.parse重新解析回一個(gè)對(duì)象。存儲(chǔ)新結(jié)果至一個(gè)新變量中,我們創(chuàng)建了一個(gè)新對(duì)象。新對(duì)象具有源對(duì)象一樣的屬性和值,唯一區(qū)別是內(nèi)存的地址的不同。
當(dāng)改變新對(duì)象的age時(shí),原對(duì)象并沒(méi)有受到影響。這個(gè)函數(shù)就是純潔純凈的。它沒(méi)有影響任何外部作用域的對(duì)象,甚至傳入函數(shù)的對(duì)象。新的對(duì)象需要被返回 并將其存儲(chǔ)在一個(gè)新變量中否則一旦函數(shù)執(zhí)行完畢就會(huì)被回收,該對(duì)象在作用于內(nèi)就再也找不到了。
以下幾個(gè)例子看看你是否理解了上述內(nèi)容:
function changeAgeAndReference(person) { person.age = 25; person = { name: "John", age: 50 }; return person; } var personObj1 = { name: "Alex", age: 30 }; var personObj2 = changeAgeAndReference(personObj1); console.log(personObj1); // -> { name: "Alex", age: 25 } console.log(personObj2); // -> { name: "John", age: 50 }
解析:
上述函數(shù) 等于:
var personObj1 = { name: "Alex", age: 30 }; var person = personObj1; person.age = 25; person = { name: "John", age: 50 }; var personObj2 = person; console.log(personObj1); // -> { name: "Alex", age: 25 } console.log(personObj2); // -> { name: "John", age: 50 }
唯一一點(diǎn)不同是前者person在函數(shù)結(jié)束后就被回收了。
再來(lái)幾道題:
// 1 var obj = { innerObj: { x: 9 } }; var z = obj.innerObj; z.x = 25; console.log(obj.innerObj.x); // 2 var obj = { arr: [{ x: 17 }] }; var z = obj.arr; z = [{ x: 25 }]; console.log(obj.arr[0].x); // 3 var obj = {}; var arr = []; obj.arr = arr; arr.push(9); obj.arr[0] = 17; console.log(obj.arr === [17]); // 4 function fn(item1, item2) { if (item2 === undefined) { item2 = []; } item2[0] = item1; return item2; } ? var w = {}; var x = [w]; var y = fn(w); var z = fn(w, x); ? console.log(x === y);
結(jié)果:
25
17
false
false
3和4 需要注意一點(diǎn): [5] === [5] ====> false
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/89701.html
摘要:判斷一個(gè)值是否是,只能用來(lái)判斷如果兩個(gè)都是字符串,每個(gè)位置的字符都一樣,那么相等否則不相等。如果一個(gè)是字符串,一個(gè)是數(shù)值,把字符串轉(zhuǎn)換成數(shù)值再進(jìn)行比較。對(duì)象轉(zhuǎn)換成基礎(chǔ)類型,利用它的或者方法。核心內(nèi)置類,會(huì)嘗試先于例外的是,利用的是轉(zhuǎn)換。 javascript-- == vs === 高級(jí)語(yǔ)言層出不窮, 各個(gè)語(yǔ)言雖說(shuō)思想一致,但仍有各自獨(dú)特的設(shè)計(jì)理念和語(yǔ)法, js有許多容易讓人迷惑的地方...
摘要:深拷貝淺拷貝本文主要對(duì)深拷貝淺拷貝的解釋及實(shí)現(xiàn)做一下簡(jiǎn)單記錄。之所以會(huì)有深拷貝與淺拷貝之分,是因?yàn)椴煌瑪?shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中的存儲(chǔ)區(qū)域不一樣。但注意,只能做一層屬性的淺拷貝。 深拷貝VS淺拷貝 本文主要對(duì)深拷貝&淺拷貝的解釋及實(shí)現(xiàn)做一下簡(jiǎn)單記錄。原文鏈接,歡迎star。 之所以會(huì)有深拷貝與淺拷貝之分,是因?yàn)椴煌瑪?shù)據(jù)類型的數(shù)據(jù)在內(nèi)存中的存儲(chǔ)區(qū)域不一樣。 堆和棧是計(jì)算機(jī)中劃分出來(lái)用來(lái)存儲(chǔ)的...
摘要:一數(shù)據(jù)類型基本類型引用類型類型判斷返回結(jié)果未定義布爾值字符串?dāng)?shù)值對(duì)象或者函數(shù)拓展堆棧兩種數(shù)據(jù)結(jié)構(gòu)堆隊(duì)列優(yōu)先,先進(jìn)先出由操作系統(tǒng)自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。 一、數(shù)據(jù)類型 基本類型:`Null Boolean String Undefined Number(NB SUN)` 引用類型:`Array Function Object` 類型判斷:typeof 返回結(jié)果...
摘要:字節(jié)碼驗(yàn)證于是就寫(xiě)了以下的類,用來(lái)驗(yàn)證然后,然后,看字節(jié)碼如下圖。以上,就是整個(gè)關(guān)于引用傳遞和值傳遞的理解,有說(shuō)的不對(duì)的,望指正。 寫(xiě)這個(gè)的原因主要是今天看到了知乎的一個(gè)問(wèn)題,發(fā)現(xiàn)自己有些地方有點(diǎn)懵逼,寫(xiě)下來(lái)記錄一下,知乎上排名第一的答案說(shuō)的很清楚,不過(guò)看了以后依舊有點(diǎn)迷迷糊糊,所以自己寫(xiě)了個(gè)幾行代碼測(cè)試。首先上一個(gè),感覺(jué)比較對(duì)的結(jié)論:**Horstmann的《java核心技術(shù)》(中文...
閱讀 3899·2021-09-22 15:28
閱讀 1440·2021-09-03 10:35
閱讀 990·2021-09-02 15:21
閱讀 3574·2019-08-30 15:53
閱讀 3564·2019-08-29 17:25
閱讀 647·2019-08-29 13:22
閱讀 1628·2019-08-28 18:15
閱讀 2461·2019-08-26 13:57