## 陣列、函式、變數
----
#### 課程聊天室:https://tlk.io/mrtjs

---
## 陣列
JS 並沒有明確的陣列資料類型。取而代之,提供了 Array 物件。與物件不同的是,陣列可以經由特定的 index 取得、改變陣列中的元素 (element)。
----
```javascript=1
var obj = {propertyA: 1}
var arr = [0,1,2];
```
|功能|物件|陣列|
|--|--|--|
|取得|`obj.propertyA`|`arr[0]`|
|更新|`obj.propertyA = 2`|`arr[0] = 2`|
|新增|`obj.propertyB = 'B'`|`arr[3] = 3`|
----
:boom:
```javascript=1
var arr = [0,1,2];
arr[5] = 5
// [0,1,2, empty, empty, 5]
arr[3] = undefined
```
----
Exercise
```javascript=1
var arr = ['20191225'];
var obj = {time1: '20191224'}
// console.log(arr, obj)
// ['20191224'], {time1: '20191224', time2: '20191225'}
```
----
#### 常用內建函式
from [oxxostudio](https://www.oxxostudio.tw/articles/201908/js-array.html)
|分類 |方法|
| -------- | -------- |
| 會改變原始陣列 | push()、pop()、shift()、unshift()、reverse()、splice()、sort()、copyWithin()、fill()
| 回傳陣列元素資訊或索引值 | length、indexOf()、lastIndexOf()、find()、findIndex()、filter()
| 針對每個元素處理 | forEach()|
----
|分類 |方法|
| -------- | -------- |
| 產生新的陣列或新的值 | join()、concat()、slice()、map()、reduce()、reduceRight()、flat()、flatMap()、Array.from()、Array.of()、toString()
| 判斷並回傳佈林值 | every()、some()、includes()、Array.isArray()
----
* 改變原始陣列 - push()
```javascript=1
var arr = [{name: 'a'}, {name: 'b'}];
arr.push({name: 'c'});
conole.log(arr)
// [ {name: "a"}, {name: "b"}, {name: "c"}]
```
----
* 得到陣列資訊 - length
```javascript=1
var arr = [{name: 'a'}, {name: 'b'}];
console.log(arr.length)
// 2
```
----
* 不回傳,針對陣列每一元素做處理 - forEach()
```javascript=1
var arr = [{name: 'a'}, {name: 'b'}];
arr.forEach(function(person) {
person.name = 'c'
})
console.log(arr);
// [ {name: "c"}, {name: "c"}]
```
----
* 產生新的陣列或新的值 - map()
```javascript=1
let arr = [{name: 'a'}, {name: 'b'}];
arr = arr.map(function(person) {
return person.name
});
console.log(arr);
// ['a', 'b']
```
----
* 判斷並回傳佈林值 - every()
```javascript=1
var arr = [{name: 'a'}, {name: 'b'}];
arr.every(function(person) {
return typeof(person.name) === 'string'
})
// true
```
----
Exercise
```javascript=101
// 產生只有 {id, title} 的 array
var newReleases = [
{
"id": 70111470,
"title": "Die Hard",
"rating": [4.0],
"bookmark": []
},
{
"id": 654356453,
"title": "Bad Boys",
"rating": [5.0],
"bookmark": [{ id: 432534, time: 65876586 }]
}
]
function generateKeyArr(value) {
let result;
...
return result;
}
console.log(generateKeyArr(newReleases))
// [
// {
// "id": 70111470,
// "title": "Die Hard"
// },
// {
// "id": 654356453,
// "title": "Bad Boys"
// }
// ]
```
---
## 函式 - 組成元素
* 函式的名稱。
* 包圍在括號()中,並由逗號區隔的一個函式參數列表。
* 包圍在大括號{}中,用於定義函式功能的一些JavaScript語句。
----
```javascript=1
function square(number) {
return number * number;
}
square(4)
// 16
```
----
Exercise
```javascript=1
function plus(number1, number2) {
...
}
console.log(plus(4, 2));
console.log(plus('4', 2));
console.log(plus(4, '2'));
```
----
Exercise
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
...
}
modifyObj(person);
console.log(person);
// {name: 'peter'}
```
----
Exercise
```javascript=1
var name = 'chad';
function modifyValue(name) {
...
}
modifyValue(name);
console.log(name);
// peter
```
----
:bomb:將變數傳進函式時,發生了什麼事?[Reference](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/)

----

----

----

----

----

----

----

----
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
obj.name = 'peter';
}
modifyObj(person);
console.log(person);
```
----
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
obj.name = 'peter';
}
modifyObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
----
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
obj.name = 'peter';
}
modifyObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
| person | 0x02 | 0x01 |
----
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
obj.name = 'peter';
}
modifyObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
| person | 0x02 | 0x01 |
| obj | 0x03 | 0x01 |
----
```javascript=1
var person = {name: 'chad'};
function modifyObj(obj) {
obj.name = 'peter';
}
modifyObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | {name: <del>'chad'</del> 'peter'} |
| person | 0x02 | 0x01 |
| obj | 0x03 | 0x01 |
----
```javascript=1
var person = {name: 'chad'};
function reassignObj(obj) {
obj = {
name: 'peter'
};
}
reassignObj(person);
console.log(person);
```
----
```javascript=1
var person = {name: 'chad'};
function reassignObj(obj) {
obj = {
name: 'peter'
};
}
reassignObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
| person | 0x02 | 0x01 |
----
```javascript=1
var person = {name: 'chad'};
function reassignObj(obj) {
obj = {
name: 'peter'
};
}
reassignObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
| person | 0x02 | 0x01 |
| obj | 0x03 | 0x01 |
----
```javascript=1
var person = {name: 'chad'};
function reassignObj(obj) {
obj = {
name: 'peter'
};
}
reassignObj(person);
console.log(person);
```
| 名稱 | 記憶體位置 | 儲存的值 |
| -------- | -------- | -------- |
| | 0x01 | `{name: 'chad'}` |
| person | 0x02 | 0x01 |
| obj | 0x03 | 0x04 |
| | 0x04 | `{name: 'peter'}` |
----
#### :bomb:
> 將變數傳進函式時,發生了什麼事?
Pass by value: 傳值,這個 <b>值</b> 有時候代表記憶體位置,有時候代表儲存的值。
---
## 變數
----
宣告方式
* var
* const
* let
----
var
* ES6 以前
* 可對已宣告變數再次宣告
```javascript=1
var a; // undefined
var a = 1; // 1, 可以重複宣告
a = 2; // 2;
```
----
var
* ES6 以前
* 可對已宣告變數再次宣告
* 可同時宣告多個變數
```javascript=1
var a = 0, b = 0;
```
----
Exercise
```javascript=1
var x = y, y = 'A';
console.log(x + y);
```
----
scope - 變數的有效範圍
### function scope
```javascript=1
function change() {
var a = 1;
console.log(a);
}
change();
```
----
scope - 變數的有效範圍
### function scope
```javascript=1
function change() {
var a = 1;
console.log(a);
}
function change2() {
var a = 2;
console.log(a);
}
change();
change2();
```
----
scope - 變數的有效範圍
### function scope
```javascript=1
var a = 1;
function change() {
console.log(a);
}
change();
```
----
scope - 變數的有效範圍
### function scope
```javascript=1
var a = 1;
function change() {
var a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
console.log(a)
```
----
scope - 變數的有效範圍
### function scope
```javascript=1
var a = 1;
function change() {
var a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
for(var a=0; a < 5; a++) {
}
console.log(a);
```
----
scope - 變數的有效範圍
### [立即執行函式 IIFE](https://developer.mozilla.org/zh-TW/docs/Glossary/IIFE)
```javascript=1
var a = 1;
function change() {
var a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
(function(){
// 刻意創造一個立刻執行的函式,創造 scope,避免迴圈的宣告行為影響全局的變數 var a = 1;
for(var a=0; a < 5; a++) {
}
})()
console.log(a);
```
----
### function scope
<br/>
###### `var` 在 `function` 內部建立 scope
###### 換句話說,如果在非 `function` 的狀況下使用 `var`
###### 有相當大的機會污染外部變數
----
var
* ES6 以前 -> <i>較舊的用法</i>
* 可對已宣告變數再次宣告
* 可同時宣告多個變數
* function scope -> <i>新的版本提出解決方案</i>
----
#### scope - 變數的有效範圍
### block scope
```javascript=1
let x = 'Hello from the new world';
const y = 'Hello from the new world';
```
----
### let
* 可重新 assign
```javascript=1
let a = 1;
a = 2;
a = {};
a = '3';
```
----
### let
* 可重新 assign
* 不可重複宣告
```javascript=1
let a = 1;
let a = 2;
// Uncaught SyntaxError: Identifier 'a' has already been declared
```
----
### let
* 可重新 assign
* 不可重複宣告
* block scope
```javascript=1
let a = 1;
function change() {
let a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
for(let a=0; a < 5; a++) {
}
console.log(a);
```
----
### const
* 不可重新 assign
```javascript=1
const a = 1;
a = 2;
// Uncaught TypeError: Assignment to constant variable.
```
----
### const
* 不可重新 assign
```javascript=1
const a = {name: 'chad'};
a.name = 'peter'
```
----
### const
* 不可重新 assign
```javascript=1
const a = {name: 'chad'};
a.name = 'peter'
// 更改物件中的屬性值並不會更改 a 指向的記憶體位置,所以不算重新 assign
```
|名稱|記憶體位置|儲存的值|
|--|--|--|
||0x01|{name: <del>'chad'</del> 'peter'}|
|a|0x02|0x01|
----
### const
* 不可重新 assign
```javascript=1
const a = {name: 'chad'};
a = { name: 'peter'};
// Uncaught TypeError: Assignment to constant variable.
```
|名稱|記憶體位置|儲存的值|
|--|--|--|
||0x01|`{name: 'chad'}`|
|a|0x02|<del>0x01</del> 0x03|
||0x03|`{name: 'peter'}`|
----
### const
* 不可重新 assign
* 不可重複宣告
```javascript=1
const a = 1;
const a = 2;
// Uncaught SyntaxError: Identifier 'a' has already been declared
```
----
### const
* 不可重新 assign
* 不可重複宣告
* block scope
```javascript=1
const a = 1;
function change() {
const a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
console.log(a); // 1
```
----
### const
* 不可重新 assign
* 不可重複宣告
* block scope
```javascript=1
const a = 1;
function change() {
const a = 2;
console.log(a, "我在 function 'change' 裡面")
}
change();
for(const a=0; a < 5; a++) {
// 這裡就會出錯了: Uncaught TypeError: Assignment to constant variable.
// 針對現代的瀏覽器, for 迴圈裡最好運用 let 宣告變數
}
console.log(a);
```
----
## 變數
|宣告|重新 assign|重複宣告|scope|同時宣告多變數
|-|-|-|-|-|
|`var`|可|可|function scope|可|
|`let`|可|不可|block scope|可|
|`const`|不可|不可|block scope|可|
----
### hoisting - 提升
###### 執行任何程式碼前,JavaScript 會把函式宣告及變數放進記憶體裡面
----
### hoisting - 提升
```javascript=1
console.log(a);
```
----
### hoisting - 提升
```javascript=1
console.log(a); // undefined
var a = 2;
```
----
### hoisting - 提升
```javascript=1
// var a;
console.log(a); // undefined
a = 2;
```
----
### hoisting - 提升
```javascript=1
function catName(name) {
console.log("My cat's name is " + name);
}
catName("Tigger");
```
----
### hoisting - 提升
```javascript=1
catName("Tigger");
function catName(name) {
console.log("My cat's name is " + name);
}
```
----
### hoisting - 提升
###### function declaration 函式陳述式
```javascript=1
catName("Tigger");
function catName(name) {
console.log("My cat's name is " + name);
}
```
----
### hoisting - 提升
###### function expression 函式表示式
```javascript=1
console.log(catName); // undefined
catName("Tigger"); // error
var catName = function(name) {
console.log("My cat's name is " + name);
}
```
----
### hoisting
###### const / let - temporal dead zone
```javascript=1
console.log(a); //TDZ
const a = 1;
```
```javascript=1
console.log(a); //TDZ
let a = 1;
```
----
### hoisting
###### const / let - temporal dead zone
```javascript=1
var a = 10
function test() {
console.log(a);
// 如果 const / let 僅只是沒有 hoisting, 上一行應該為 10
let a;
}
test()
```
----
### hoisting
###### const / let - temporal dead zone
[「提升之後」(在最前面宣告但不進行初始化)<br/>以及「賦值之前」這段「期間」](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
```javascript=1
function test() {
var a = 1; // c 的 TDZ 開始
var b = 2;
console.log(c) // 錯誤
if (a > 1) {
console.log(a)
}
let c = 10 // c 的 TDZ 結束
}
test()
```
----
### hoisting 的原因
* 先宣告變數才可以使用
* 先宣告函式才可以使用
* 達成 function 互相呼叫
<br/>
<br/>
##### 總結:盡量主動先宣告再使用
```javascript=1
function test() {
var a = 1;
let b = 2;
const c = 3;
console.log(a, b, c);
}
test()
```
----
# 陣列、函式、變數
----
Ref:
1. [JavaScript Array 陣列操作方法大全 ( 含 ES6 )](https://www.oxxostudio.tw/articles/201908/js-array.html)
2. [深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/)
3. [我知道你懂 hoisting,可是你了解到多深?](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
4. [MDN 提升(Hoisting)](https://developer.mozilla.org/zh-TW/docs/Glossary/Hoisting)
5. [MDN var](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/var)
6. [MDN const](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/const)
7. [MDN let](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/let)
----
[範例程式](https://hackmd.io/@ChadZ/JS-MRT-2-EXERCISE)
{"metaMigratedAt":"2023-06-15T02:43:20.046Z","metaMigratedFrom":"YAML","title":"陣列、函式、變數","breaks":true,"slideOptions":"{\"title\":\"JavaScript 讀書會\",\"theme\":\"night\",\"transition\":\"slide\",\"spotlight\":{\"enabled\":false},\"transitionSpeed\":\"fast\"}","contributors":"[{\"id\":\"7da0a1a8-6add-40a7-9acc-950070ff069c\",\"add\":15368,\"del\":2144}]"}