JavaScript 的參數傳遞
- 每個宣告的變數的值(value)都會儲存在不同的記憶體空間
- 使用容量較小,原始型別的資料儲存在 Stack 內
- 複製變數時,會完全複製一份新的「值 (value)」
let a = 6let b = aa = 9console.log(b) // 6
宣告的變數 | 儲存的值 | 記憶體空間 |
---|
a | 6 | 空間 A |
b | 6 | 空間 B |
let a = 6let b = aconsole.log(a == b) //trueconsole.log(a === b) //true
- 函式接收到的參數值是原始值的一個複製
- 當函式修改參數的值時,原始值不會被改變
let age = 18function increase(age) { age++}increase(age)console.log(age) // 18;
- 儲存資料值所在的記憶體位置的地址 (address)
- 物件類別的資料通常較為複雜,佔用記憶體容量較大,會儲存在 Heap 內
- 複製變數時,是複製一份新的「地址 (address)」,並非複製值 (value)
- 注意重點: 原物件是否會被修改
//因為是儲存記憶體位置,所以用const宣告的陣列可以被修改const arr = [55, 66]arr.push(77)//值會被變更console.log(arr) // [55,66,77]
// object example//x有一個記憶體位址 0x01 指向儲存的值 {value:10}let x = { value: 10 }//y有同一個記憶體位址 0x01 指向相同的值let y = xx.value = 20console.log(x) // {value: 20}console.log(y) // {value: 20}//array examplelet animals = ['zebra', 'bear', 'deer']let animals2 = animalsanimals2.push('lion')console.log(animals) // ['zebra','bear','deer','lion']
宣告的變數 | 儲存的值 |
---|
x | 記憶體位址 0x01 |
y | 記憶體位址 0x01 |
animals | 記憶體位址 0x02 |
animals2 | 記憶體位址 0x02 |
記憶體位址 | 值 |
---|
0x01 | { value: 20} |
0x02 | 'zebra' ,'bear', 'deer', 'lion' |
- 使用 == , === 比較時,是比較記憶體位址是否相同
//記憶體位址不同console.log([] == []) //falseconsole.log([10] == [10]) //falseconsole.log(['hello', 'bye'] === ['hello', 'bye']) // false//有相同的值和記憶體位址let arr1 = [1, 2, 3] //0x01let arr2 = arr1 //0x01console.log(arr1 == arr2) //trueconsole.log(arr1 === arr2) //true//有相同的值,但記憶體位址不同let arr3 = [4, 5, 6] //0x01let arr4 = [4, 5, 6] //0x02console.log(arr3 == arr4) //falseconsole.log(arr3 === arr4) //false
- 因為物件類別有 call by reference 特性,如果不想修改原物件,可以使用一些方法來複製一份
- 例如想要新增物件屬性,但希望保留原本的物件資料時
- 只複製物件第一層屬性 (primitive type)
- 修改物件第一層屬性的值不會影響原物件
- 若有第二層以上結構時 (reference type),位址仍然相同,指向同樣的值
- 修改物件第二層屬性的值會影響原物件
- 方法 1:展開運算子 (Spread Operator)
//範例:複製物件const jacket = { brand: 'UNIQLO', color: 'navy' }const Jacket2 = { ...jacket }console.log(newJacket) // { brand: 'UNIQLO', color: 'navy' }//範例:複製陣列const dinner = ['pizza', 'hamburger', 'sandwich']const dinner2 = [...dinner]console.log(dinner2) // ['pizza','hamburger','sandwich']
- 方法 2: Object.assign()
//語法Object.assign(target, ...sources)//範例:複製物件const obj = { a: 1 }const copy = Object.assign({}, obj)console.log(copy) // { a: 1 }
- 方法 3: Array.prototype.slice()
- 會回傳一個新陣列物件
- 為原陣列選擇之 begin 至 end(不含 end)部分的淺拷貝(shallow copy)
- 原本的陣列不會被修改
//語法arr.slice([begin[, end]])const drinks = ['tea','coke','milk']//寫法1: 不給參數,會全部複製一份const mydrinks = drinks.slice()console.log(mydrinks) // ['tea', 'coke', 'milk']//寫法2:小括號內放0,結果同上const mydrinks = drinks.slice(0)console.log(mydrinks) // ['tea', 'coke', 'milk']
- 範例
- 如果物件內有 reference type(Object,Array...),是複製其位址,仍指向相同的值
//example1//改變第一層屬性的值,不影響原物件const car1 = { brand: 'tesla', color: 'white', detail: { model: 'ModelX', year: '2023' }}const car2 = Object.assign({}, car1)car2.color = 'black'console.log(car1)/*{ brand: 'tesla', color: 'white', detail: { model: 'ModelX', year: '2023' }}*/console.log(car2)/*{ brand: 'tesla', color: 'black', detail: { model: 'ModelX', year: '2023' }}*///example2//改變第二層物件的值,連原物件也一起改變let car2 = Object.assign({}, car1)car2.detail.model = 'Model3'//因為car1和car2內的detail共享同個位址//當改變car2的detail內model的值時,car1也會同樣被改變console.log(car1)/*{ brand: 'tesla', color: 'white', detail: { model: 'Model3', year: '2023' }}*/console.log(car2)/*{ brand: 'tesla', color: 'white', detail: { model: 'Model3', year: '2023' }}*/
- 深度複製指定物件,操作新物件不影響原物件
- 兩者指向不同記憶體位址
方法 1: 使用JSON.stringify/parse
//深拷貝方法 JSON.stringify/parseconst myBand = { name: 'The Strokes', members: { vocal: 'Julian', guitarist1: 'Albert', guitarist2: 'Nick', bassist: 'Nikolai', drummer: 'Fabrizio' }}//使用JSON.stringify將myBand轉成字串//再透過JSON.parse將字串轉回物件const newBand = JSON.parse(JSON.stringify(myBand))console.log(myBand === newBand) // falsenewBand.members.vocal = 'Kurt'// myBand不會被改變console.log(myBand)/*{ name: 'The Strokes', members: { vocal: 'Julian', guitarist1: 'Albert', guitarist2: 'Nick', bassist: 'Nikolai', drummer: 'Fabrizio' }}*/console.log(newBand)/*{ name: 'The Strokes', members: { vocal: 'Kurt', guitarist1: 'Albert', guitarist2: 'Nick', bassist: 'Nikolai', drummer: 'Fabrizio' }}*/
方法 2: 使用structuredClone
const myBand = { name: 'The Strokes', members: { vocal: 'Julian', guitarist1: 'Albert', guitarist2: 'Nick', bassist: 'Nikolai', drummer: 'Fabrizio' }}const yourBand = structuredClone(myBand)console.log(yourBand === myBand) // falseyourBand.members.drummer = 'Ringo'console.log(myBand)/*{ name: 'The Strokes', members: { vocal: 'Julian', guitarist1: 'Albert', guitarist2: 'Nick', bassist: 'Nikolai', drummer: 'Fabrizio' }}*/console.log(yourBand)// {// "name": "The Strokes",// "members": {// "vocal": "Julian",// "guitarist1": "Albert",// "guitarist2": "Nick",// "bassist": "Nikolai",// "drummer": "Ringo"// }// }