今天終於要進入Javascript的世界啦!
因為在還沒鐵人賽之前有寫了幾篇文章,所以會跳過有寫過文章的部分。
如果小夥伴們對於JS變數宣告
、型別
、範圍鍊
有興趣的小夥伴可以去看看我之前寫的文章。
今天要探討的主題是JavaScript存取資料的方法,資料如何在記憶體裏被存放?
有六種資料型別是基本型別:Boolean
、Null
、Undefined
、Number
、BigInt
、String
、Symbol
(於EC6加入)。
那我們先來看一下基本型別是怎麼存取資料的吧!
let a = 1; //把1指定給a變數
let b; //有一個b變數先不指定值
b = a; //把a指定給b
console.log(b); //1
b = 20; //改b的值
console.log(a); //1
console.log(b); //20
console.log(a === b); //false
上面的程式碼可以看的出來基本型別是傳值
(pass by value)。
當一個變數被賦予基本型別的值時,整個值就會存在記憶體裏。當我們要拷貝基本型別的值到另一個變數,我們只會拷貝它們的值,而該兩個變數並不會影響到對方。這個情況稱為傳值(pass by value)。
在JavaScript的世界中除了基本型別之外的都是物件型別。
接下來換看物件型別怎麼存取資料
let a = { age: 18 };
let b;
b = a;
console.log(b); //印出{ age: 18 }
b.age = 20; //用key的方式拿到value
console.log(a); //印出{ age: 20 }
console.log(b); //印出{ age: 20 }
console.log(a === b); // true
等等為什麼我明明改b卻會影響到a的值? 因為物件型別的存取方法不同。
當變數被賦予的是物件型別的資料,記憶體會存放該物件在記憶體中的地址,並引用該地址來指向該物件。所以,當我們要拷貝一個物件到另一個變數時,我們拷貝的是該物件的地址,換言之,如果物件有被修改,所有引用該物件地址的變數,它們的值都會被修改。
如果今天改成
let a = { age: 18 };
let b;
b = a;
console.log(b); //印出{ age: 18 }
b = { age: 20 };
console.log(a); //印出{ age: 18 }
console.log(b); //印出{ age: 20 }
console.log(a === b); // false
為什麼結果會不一樣,因為這次不是修改值而是直接賦予一個新的物件。
因為是賦予一個新的物件,它的物件地址也被修改所以才不會相等。
在電腦底層的世界,可以想像記憶體空間就像是一個一個空間,每一個空間都有他的位址,並可以空間內儲存值。
為了方便人類取用,才有了變數
的存在。
拿來連結(指向)這些記憶體位址,宣告變數賦值,就是向電腦要一個記憶體空間來存值。
let a = 1
其實是變數 a 指向電腦中某記憶體的位置(ex: 0x01) ,在這個記憶體位置中儲存1這個值。
如果我再宣告了一個 b
let b = a;
雖然 b 和 a 的值一樣都是 1 ,但其實變數 b 是指向了另一個不一樣的記憶體位置 (ex:0x02),把 a 的值 copy 過來存,a 和 b 是存在於兩個獨立不同的記憶體位置中。
怎麼說不可變異呢?如果我們改變了 a 的值,其實是改變 a 所指向的記憶體位置, 1這個值是永遠不會被改變的,只是 a 所指向的記憶體位置不同,這就是所謂的不可變異。
我在這邊先宣告一個陣列
let arr1 = [1,2,3]
表示 arr1
指向了一個新的記憶體位置(ex: 0x01)。
如果我再建立了第二個陣列並讓他等於 arr1
let arr2 = arr1
這時候 arr2
則會直接指向 arr1
的記憶體位置(0x01)。
所以不論 arr1
所儲存的值是多少 arr2
都會得到一樣的值。
不過回到上面講過的,如果今天是用修改值跟賦予新值得到的結果會不一樣。
arr2[0] = 4;
直接修改arr2
值的話會影響arr1
因為使用的記憶體位置一樣都是(0x01)。
但是如果今天是這樣修改
arr2 = [4, 2, 3]
這樣就不會影響到arr1
,因為這樣修改會給予arr2
新的記憶體位置(0x02)就不會影響到(0x01)
其實還有一個說法是Javascript比較像是passing by sharing
。
let a = { age: 18 };
function changeValue(obj) {
obj = { age: 20 };
}
changeValue(a);
console.log(a); //18
上面這段程式碼我們在函式中試著去改變物件的值,但結果卻發現沒有值改變。
但是如果今天不是重新賦予值,而是直接改變值。
let a = { age: 18 };
function changeValue(obj) {
obj.age = 20;
}
changeValue(a);
console.log(a); //20
這樣物件的值就發現成功被更改了!其實物件當作參數傳入函式時,應該是pass by sharing
。
上面有提到過基本型別是不可變異 (immutable)
,而物件型別是可變異 (mutable)
。
當物件更新值時會影響到引用物件的變數與其副本,修改的時候會影響到原本的參考。
但是如果直接賦予新的值會直接產生新的參考。
所以如果今天是在基本型別不可變異的情況下不管你怎麼去更新值都不會影響其副本。
所以也有一派說法其實JavaScript應該都是pass by sharing
那個各位小夥伴有什麼看法呢?
重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」?