[TOC]
## 1.2 了解瀏覽器

- Document Object Model(DOM)文件物件模型:客戶端Web應用程式使用者界面的結構化表示
- 事件(Event):JS大多數都是事件驅動,例如網路事件、使用者產生事件,例如點擊、滑鼠移動、按下鍵盤等
- 瀏覽器API:存取設備資訊、本地端除存資料或遠端伺服器通訊
## 1.3 Using current best practices使用當前最佳實踐
- Debugging skills(程式除錯技巧)
- Testing (測試)
- Performance analysis (效能分析)
## 2.1 The lifecycle 生命週期

1. 輸入網址(或點擊超連結)
2. 瀏覽器發出請求送到伺服器
3. 伺服器處理請求
4. 產生HTML CSS JS所組成的回應頁面
5. 進入一個等待事件發生的迴圈
6. 使用者關閉或離開網頁,結束應用程式的生命週期
## 2.2 The page-building phase 頁面建立階段
瀏覽器接收到頁面代碼開始,會有兩個步驟
- step1:解析HTML代碼並建立DOM結構(如圖2.4)
- step2:執行JS程式碼
```!
- 執行step1解析HTML時,當遇到含有<script>節點時,瀏覽器會暫時停止使用HTML代碼建立DOM,而會開始執行step2
- 瀏覽器根據需要會在這兩個step切換多次
- 執行step2到最後一行程式碼時,瀏覽器會離開JS執行模式,繼續處理剩餘的HTML代碼,建立其他DOM節點
```
`script`包含的JS程式碼都是由瀏覽器的JS引擎執行,例如
- Firefox的Spidermonkey
- Chrome和Opera的V8
- Edge的Chakra

```!
* 瀏覽器一次解析HTML,一次處理一個HTML元素,並建立DOM
* 每個HTML元素代表一個節點(node)
```

JS主要提供頁面的動態行為,瀏覽器藉由全域物件(window物件)提供了一組[API](https://developer.mozilla.org/en-US/docs/Web/API),任意改變所在頁面DOM結構,例如修改刪除現有元素、更改文字內容、修改屬性項、動態建立和新增新的子元素等
```!
window物件:
- window物件是一個特殊的全域物件,藉由他來存取所有其他全域物件、全域變數及瀏覽器API
- 擁有最重要的屬性"document"
```
```!
補充:API是什麼
Application Programming Interface,中文翻譯"應用程式介面",作為兩個程式的橋樑
```
## 2.3 Event handling 事件處理

**Event handling 事件類型**:
- 瀏覽器事件,例如網頁處於已載入或未載入的狀態
- 網路事件,例如伺服器的回應(Ajax事件、伺服器端事件)
- 使用者事件,例如點擊滑鼠、按下鍵盤等
- 計時器事件,例如等候逾時或間隔性的觸發條件
**event-handler registration 註冊事件處理器**:讓瀏覽器知道對特定事件發生時執行的函式,此兩種方式可用來註冊事件:
- 將函式指定給特殊屬性(不建議,因對某個特定事件只能指派一個事件處理器)
- 使用內建的addEventListener方法
## 3.1 使用函式與否的差異為何
### 3.1.1 Functions as first-class objects 函式作為頭等物件
所有物件可以做的事情,函式都能作到,函式是物件,卻是一個可被呼叫的物件
```
函式型程式設計(function programming):
- 程式風格是透過撰寫函式來解決問題
- 易於測試、擴展和模組化的程式碼
```
### 3.1.2 Callback functions 回呼函式
將函式作為參數傳遞給另一個函式,並透過傳遞參數呼叫該函式
```javascript!
<script>
var text = 'Domo arigato!';
report("Before defining functions");
function useless(ninjaCallback) {
report("In useless function");
return ninjaCallback();
}
function getText() {
report("In getText function");
return text;
}
report("Before making all the calls");
assert(useless(getText) === text,
"The useless function works! " + text);
report("After the calls have been made");
</script>
```
補充:[Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
## 3.2 函式作為物件的有趣之處(可跳過)
```javascript!
var ninja = {};
ninja.name = "hitsuke"; //新增一屬性name並給值
console.log(ninja); // {"name": "hitsuke"}
//函式也可以擁有屬性
var wieldSword = function () {};
wieldSword.swordType = "katana";
console.log(Object.entries(wieldSword)); //[["swordType","katana"]]
//在函式屬性中儲存函式,作為之後引用和呼叫
//使用函式屬性建立暫存memoization,避免不必要的計算
```
- Storing functions(儲存函式)是一個集合,可以管理互有關聯的函式,例如一系列事件的回呼函式==不懂?==
- memoization(暫存)允許記得之前計算得到的值,進而提升後續呼叫的執行效能==不懂?==
### 3.2.1 Storing functions 儲存函式
第九章會使用ES6新增的集合表(set)來處理元素必須為唯一值的集合
```javascript!
var store = {
nextId: 1,
cache: {},
add: function(fn) {
if (!fn.id) {
fn.id = this.nextId++; //注意++的位置,如果是++this.nextId結果又會不同
this.cache[fn.id] = fn;
return true;
} }
};
function ninja(){}
console.log(store.add(ninja), "Function was safely added.");
console.log(!store.add(ninja), "But it was only added once.");
//用console.dir將物件展開會看到更完整的資訊
//作者寫的assert與console.assert有稍微不同,
//assert(condition, describe);true或false都會印出
//內建console.assert(condition, describe)則第一個是false才印出describe內容,true就沒反應
const a = 1;
console.assert(a === 1, "a等於1");//順利通過不會報錯
const a = 2;
console.assert(a === 1, "a不等於1");//會報錯並終止程式
```
==++在後面印出的結果==
說明:因為fn.id先賦值為當下的this.nextId,也就是1,nextId再++變成2,所以印出時,`cache{1:ninja}`,而`nextId:2`

==++在前面的結果==
說明:因為this.nextId先++變成2,再賦值給`fn.id`,也就是fn.id=2,所以印出時,`cache{2:ninja}`,而`nextId:2`

### 3.2.2 Self-memoizing functions 自我記憶函式
指建立一個函式,使其能記住之前的計算值函式計算其結果時,將結果與函式參數一起儲存起來,當使用同一組參數進行另一次呼叫時,可以回傳先前儲存的結果
==程式碼看不懂?==
```javascript!
function isPrime(value) {
//檢查是否建立用來暫存的answers屬性
if (!isPrime.answers){
isPrime.answers = {};
}
//檢查該值的計算結果是否已暫存在answers中
if (isPrime.answers[value] !== undefined) {
return isPrime.answers[value];
}
var prime = value !== 0 && value !== 1; // 1不是質數
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
return isPrime.answers[value] = prime;
}
}
console.log(isPrime(5), "5 is prime!" );
console.log(isPrime.answers[5], "The answer was cached!");
```
## 3.3 定義函式
定義函式分為四類:
- 函式宣告(declaration)和函式表達式(expression)
- 箭頭函式(通常稱為lambda函式)
```javascript!
myArg => myArg*2
```
- 函式建構式(constructor):
```javascript!
new Function('a','b','return a+b')
```
- 生成器(generator):可以在應用程式執行時退出和重新進入它們,同時在這些不同的進入點之間保持他們的變數值
==(不懂???)==
```javascript!
function*myGen(){ yield 1; }
```
### 3.3.1函式宣告和函式表達式
**函式宣告**
```javascript!
//函式名稱是必要的
function myFunctionName(myFirstArg,mySecondArg){
myStatement1;
myStatement2;
}
```
**函式表達式**
作為指派表達式的右值,或是另一個函式的參數,稱為函式表達式
```javascript!
//變數指派的右側
var myFunc = function(){}
//或是另一個函式呼叫的參數,或作為函式的回傳值
myFunc(function(){
return function(){};
});
//函式表達式的函式名稱可有可無
```
```javascript!
//其他方式呼叫
//1.函式表達式指派給一個變數,使用該變數呼叫函式
var doNothing = function(){};
doNothing();
//2.為另一個函式的參數,藉由參數名稱在函式中呼叫
function doSomething(action) {
action();
}
```
**立即函式IIFE(immediately invoked function expression)**
立即呼叫這個新建立的函式,這種作法稱為「立即呼叫的函式表達式」,簡稱「立即函式」
```javascript!
(function(){})(3);
```
```!
補充:
把函式表達式放在括號內,通知JS解析器他是一個表達式,而不是一個敘述句
Chris補充:IIFE主要是模組隔離
```
使用一元運算子告訴JS引擎這是一個表達式而不是函式宣告,無須在函式表達式使用括號,一元運算子的結果不會儲存在任何地方
```javascript!
+function(){}();
-function(){}();
!function(){}();
~function(){}();
```
### 3.3.2 箭頭函式(arrow function)

- (param1,param2):0個或多個參數,括號不可省略;只有1個參數,括號可有可無
- `=>`:箭頭運算子
- expression:簡單的函式,可省略花括號`{}`和return
如後面接一段程式區塊,沒有return敘述句,結果為undefined
```javascript!
//接expression
var greet = name => "Greetings " + name;
//接程式區塊
var greet = name => {
var helloString = 'Greetings ';
return helloString + name;
};
```
> Chris補充:其他語言叫做[匿名函式或是Lambda](https://learn.microsoft.com/zh-tw/dotnet/csharp/language-reference/operators/lambda-expressions),只有JS叫做箭頭函式
## 3.4 Arguments and function parameters 引數與函式參數
- 參數(parameter):函式定義中所列出的變數
- 引數(argument):呼叫函式時傳遞給它的值
```javascript=
function skulk(ninja) { //ninja為參數
return performAction(ninja, "skulking"); //ninja和 "skulking"為引數
}
var performAction = function (person, action) { //person, action為參數
return person + "-" + action;
};
var rule = daimyo => performAction(daimyo, "ruling");
skulk("Hattori"); //"Hattori"為引數
rule("oda nobunaga"); //"oda nobunaga"為引數
```
如引數的數量多於參數,多的引數不會被指派給任何參數;
如引數的數量少於參數,參數因沒有相對應的引數,參數會被設為undefined
```javascript!
function practice(ninja, weapon, technique) {
return `${ninja} + ${weapon} + ${technique}`;
}
practice("a", "b", "c", "d"); //"a + b + c"
practice("e"); //"e + undefined + undefined"
```
### 3.4.1 Rest parameters 不定參數
- 函式最後一個參數才可以是不定參數,如果不是放在參數最後,則會報錯
- 以`...`為開頭
- 不定參數會以==陣列==呈現,可以用array methods
```javascript!
function multiMax(first, ...remainingNumbers) {
//console.log(remainingNumbers); //[1,2,3]
var sorted = remainingNumbers.sort((a, b) => b - a); //b-a是decscending降冪
//console.log(sorted); //[3,2,1]
return first * sorted[0];
}
multiMax(3, 1, 2, 3); //9
//multiMax函式第一個引數3指派給first
//其餘的引數1,2,3則包裝成陣列,並放在remainingNumbers
```
### 3.4.2 Default parameters 預設參數
在ES6,可以指定給某一個參數預設值,如函式有指定一個值,此預設值就會被覆蓋
```javascript!
function performAction(ninja, action = "skulking") {
return ninja + " " + action;
}
performAction("Fuma"); //"Fuma skulking"
performAction1("Fuma","sneaking"); //"Fuma sneaking" 指定值覆蓋了預設值
//也可以這樣寫但可讀性差,應避免
function performAction1(
ninja,
action = "skulking",
message = ninja + " " + action
) {
return message;
}
```
補充flag旗標:指的是0和1這個例子,當作設定configure用
作閉包
特殊用的的布林值
## 4.1 Using implicit function parameters 使用函式隱含引數
呼叫函式時,會傳遞兩個隱含參數**this**和**arguments**,它們可以在函式內存取到
### 4.1.1 arguments 參數
- arguments是傳遞給函式的所有參數集合
- 不定參數的出現,使arguments參數的需求減少,但在處理舊程式碼仍會遇到
- 為類陣列,可以使用length和陣列符號,但無法使用陣列的其他方法
```javascript!
function whatever(a, b, c) {
console.log(a === 1); //true
console.log(b === 2); //true
console.log(c === 3); //true
//可使用length屬性檢查引數數量
console.log(arguments.length); //5
//可使用陣列符號存取各個引數
console.log(arguments[0] === a); //true
console.log(arguments[1] === b); //true
console.log(arguments[2] === c); //true
console.log(arguments[3] === 4); //true
console.log(arguments[4] === 5); //true
}
whatever(1, 2, 3, 4, 5);
```
```javascript!
//沒有任何參數的函式
function sum() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i]; //透過arguments存取所有函式引數
}
return sum; //6
}
sum(1, 2, 3);
//改成不定參數的寫法
function sum(...a) {
var sum = 0;
for (var i = 0; i < a.length; i++) {
sum += a[i];
}
return sum; //6
}
sum(1, 2, 3);
```
arguments可以作為函式參數的別名,例如對arguments[0]設置新值,第一個參數的值也會改變
```javascript!
function infiltrate(person) {
console.log(person === "gardener"); //true
console.log(arguments[0] === "gardener"); //true
//更改arguments會影響原本參數值
arguments[0] = "ninja";
console.log(person); //"ninja"
}
infiltrate("gardener");
```
```!
* 這樣的作法會有造成混淆的風險,因此JS提供「嚴格模式strict mode」來禁用
```
arguments在**嚴格模式**下無法作為別名
```javascript!
//使用嚴格模式
//程式第一行寫上"use strict"
"use strict"
function infiltrate(person) {
console.log(person === "gardener"); //true
console.log(arguments[0] === "gardener"); //true
//修改第一個引數,person參數值沒有改變,仍為"gardener"
arguments[0] = "ninja";
console.log(person); //"gardener"
}
infiltrate("gardener");
```
### 4.1.2 this參數:介紹函式背景空間(context)
- 當函式被呼叫,除了代表呼叫時引數值的參數外,還會將
還會將this隱含式參數傳遞給函式
- **this通常被稱為函式背景空間(function context)**
- 呼叫函式的方式,會決定this的值
## 4.2 Invoking functions 呼叫函式
使用四種方式呼叫函式
- **作為函式**:一般呼叫形式,例如:skulk()
- **作為一個方法**:呼叫綁定到一個物件上,以提供物件導向的程式設計方式,例如:ninja.skulk()
- **作為一個建構器函式**:創建新的物件,例如:new Ninja()
- **透過函式的apply或call方法**,例如:skulk.call(ninja)或skulk.apply(ninja)
### 4.2.1 Invocation as a function 作為函式來呼叫
- 一個函式被「作為一個函式」呼叫,與其他的呼叫機制(例如:方法、建構器函式、apple/call)區別開來
- 使用()運算子呼叫函式,便會發生此類型的呼叫
```javascript!
//一.作為函式來呼叫
//在普通模式下,this是window物件
function ninja() {
return this;
}
ninja();
```
在chrome瀏覽器,普通模式下this是window

```javascript!
function samurai() {
"use strict";
return this;
}
samurai();
```
在chrome瀏覽器,嚴格模式下this是undefined

嚴格模式比普通模式更直覺,一般來說嚴格模式避免了許多JS的細微怪癖
### 4.2.2 Invocation as a method 作為方法呼叫
- 函式被指派給物件的屬性,且使用該屬性來呼叫函式,就是作為物件的方法來呼叫
- 在方法所屬的物件,該方法的主體中是可以用this來取得
- 當把一個函式作為物件的方法呼叫時,該物件會成為函式所在的背景空間,並可以透過this參數存取
```javascript!
//二.作為方法呼叫
var ninja = {};
ninja.skulk = function () {
return 1;
};
ninja.skulk();
```
```javascript!
//函式呼叫與方法呼叫之間的差異
//1.當作函式呼叫
function whatsMyContext() {
return this;
}
whatsMyContext(); //window
//宣告一個變數getMyThis來取得指向whatsMyContext函式的參照
//透過變數呼叫,並不會建立第二個函式實例,僅僅建立相同函式參照
var getMyThis = whatsMyContext;
getMyThis(); //window
//2.建立物件,有一個getMyThis屬性,是參照到whatsMyContext函式
var ninja1 = {
getMyThis: whatsMyContext
};
//透過物件的getMyThis方法來呼叫函式,此時函式背景空間為ninja1物件
ninja1.getMyThis(); //{getMyThis: ƒ}
var ninja2 = {
getMyThis: whatsMyContext
};
ninja2.getMyThis(); //{getMyThis: ƒ}
```
使用相同的函式whatsMyContext,但回傳的函式背景空間卻是**取決whatsMyContext如何被呼叫**。ninja1和ninja2物件能夠使用同一個函式,執行時存取並操作其所屬的個別物件,**不需要為了不同物件建立不同的函式複本**,**這是物件導向程式設計的一項根本原則。**
### 4.2.3 Invocation as a constructor 作為建構器來呼叫
建構器函式的宣告就像任何函式一樣,要作為建構器函式來呼叫函式,須在前面加上關鍵字`new`
- 函式建構器:用動態建立的字串來建立函式
```javascript!
//自己補充的範例
let a = new Function(`return 1 + 2`)();
console.log(a); //3
```
- 建構器函式:用於建立和初始化物件實例(object literal)的函式
一般建構器被呼叫時,會觸發以下步驟:
1. 用new關鍵字呼叫一個函式,會建立一個新的空物件
2. 新的空物件會被當成this參數傳遞給建構器,,成為建構器的函式背景空間(以新的空物件為函式背景空間,也就是this參數所代表的物件)
3. new運算子會回傳新建立的物件(也有例外,後面第七章會提到)
```javascript!
//三.使用建構器函式來建立共用的物件
function Ninja(name) {
this.name = name;
this.skulk = function () {
return this;
};
}
//new關鍵字來呼叫函式,回傳新建立物件
let ninja1 = new Ninja("ninja1");
let ninja2 = new Ninja("ninja2");
ninja1.skulk(); // Ninja {name: 'ninja1', skulk: ƒ},this會呼叫自己
ninja2.skulk(); // Ninja {name: 'ninja2', skulk: ƒ},this會呼叫自己
```
#### 建構器的回傳值
* 回傳基礎型別(primitive value)的建構器函式
* 以特定物件作為回傳值的建構器函式
## 9.1 陣列
### 9.1.1 建立陣列
==260==
建立陣列有兩種基本方式:
1. 內建的Array建構器函式
2. 陣列實值`[]`
```javascript!
const ninjas = ["Kuma", "Hattori", "Yagyu"];
const samurai = new Array("Oda", "Tomoe");
```

- 陣列的 length 屬性查詢陣列的大小
```javascript!
ninjas.length === 3; // true
samurai.length === 2; // true
```
- 陣列用索引值存取陣列資料項,第一筆資料項的索引值是`0`,最後一筆是 `array.length - 1`
```javascript!
ninjas[0] === "Kuma"; // true
samurai[samurai.length - 1] === "Tomoe"; // true
```
- 如果讀取超過陣列的邊界資料項,得到 `undefined`,代表並沒有東西存在
```javascript!
ninjas[4] === undefined; // true
```
- 寫入邊界以外的位置,陣列會被擴展
```javascript!
ninjas[4] = "Ishi";
```
現在陣列長這樣:
```javascript!
["Kuma","Hattori","Yagyu",undefined,"Ishi"]
```
length 的值回傳為 `5`
==262==
陣列其實就是物件,所以如存取一個不存在的物件屬性,也會得到`undefined`
- 陣列的length有奇特技能,其值可以手動更改
```javascript!
const array1 = ["a", "b", "c"];
array1.length = 2;
console.log(array1); // ["a","b"]
```
### 9.1.2 在陣列兩端新增或移除資料項
- `push`:陣列最尾端**新增**一筆資料項
- `unshift`:陣列最前端**新增**一筆資料項
- `pop`:陣列最尾端**刪除**一筆資料項
- `shift`:陣列最前端**刪除**一筆資料項
```javascript!
const ninjas = [];
// push 從最尾端新增一筆資料項
ninjas.push("Kuma");
console.log(ninjas[0]); // ["Kuma"]
console.log(ninjas.length); // 1
// push 從最尾端新增一筆資料項
ninjas.push("Hattori");
console.log(ninjas[0]); // "Kuma"
console.log(ninjas[1]); // "Hattori"
console.log(ninjas.length); // 2
// unshift 從最前端新增一筆資料項
ninjas.unshift("Yagyu");
console.log(ninjas); // ["Yagyu","Kuma","Hattori"]
console.log(ninjas.length); // 3
// pop 從最尾端移除一筆資料項
const lastNinja = ninjas.pop();
console.log(lastNinja); // "Hattori"
console.log(ninjas); // ["Yagyu","Kuma"]
console.log(ninjas.length); // 2
// shift 從最前端移除第一筆資料項
const firstNinja = ninjas.shift();
console.log(firstNinja); // "Yagyu"
console.log(ninjas); // ["Kuma"]
console.log(ninjas.length); // 1
```

效能考量:shift / unshift方法會修改陣列第一筆資料項,代表其他資料項的索引值也會一併調整。所以push和pop執行速度明顯比shift / unshift快很多。
### 9.1.3 在陣列的任何位置新增與移除資料項
```javascript!
const ninjas = ["Yagyu", "Kuma", "Hattori", "Fuma"];
delete ninjas[1];
console.log(ninjas); // ["Yagyu",undefined,"Hattori","Fuma"]
console.log(ninjas.length); // 4
```
用 `delete` 方式雖然刪除了一筆資料項,但陣列仍然為四筆資料項,讓刪除的資料變成 `undefined` 。
---
**[splice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)**
可以在指定的索引值開始,移除與插入資料項
```
splice(start, deleteCount, item1, item2, /* …, */ itemN)
```
```javascript!
const ninjas = ["Yagyu", "Kuma", "Hattori", "Fuma"];
var removedItems = ninjas.splice(1, 1); // 從索引值1開始,移除1筆資料
console.log(removedItems); // ["Kuma"]
console.log(ninjas); // ["Yagyu","Hattori","Fuma"]
// 從索引值1開始,移除2筆資料項,再加入3筆新的資料項
removedItems = ninjas.splice(1, 2, "Mochizuki", "Yoshi", "Momochi");
console.log(removedItems); // ["Hattori","Fuma"]
console.log(ninjas); // ["Yagyu","Mochizuki","Yoshi","Momochi"]
```
- splice方法會回傳被移除的資料項的陣列
- 原陣列的資料項會自動調整位置
- 可以在指定位置上新增資料項
### 9.1.4 常見的陣列操作
1. 用for迴圈檢查陣列每一筆資料項
```javascript!
const ninjas = ["Yagyu", "Kuma", "Hattori"];
for (let i = 0; i < ninjas.length; i++) {
console.log(ninjas[i]);
}
// "Yagyu"
// "Kuma"
// "Hattori"
```
2. 使用陣列內建 forEach 方法
```javascript!
const ninjas = ["Yagyu", "Kuma", "Hattori"];
ninjas.forEach((ninja) => console.log(ninja));
// "Yagyu"
// "Kuma"
// "Hattori"
```
### 陣列對應
把陣列裡的每一筆資料項對應到新陣列的資料項
- 取出武器陣列的笨方法:
建立一個新陣列,用forEach對ninjas陣列做迭代取每個忍者的武器
```javascript!
const ninjas = [
{ name: "Yagyu", weapon: "shuriken" },
{ name: "Yoshi", weapon: "Katana" },
{ name: "Kuma", weapon: "wakizashi" }
];
const weapons = [];
ninjas.forEach((ninja) => weapons.push(ninja.weapon));
console.log(weapons); // ["shuriken","Katana","wakizashi"]
```
- 陣列對應 - map方法
```javascript!
const weapons = ninjas.map((ninja) => ninja.weapon);
console.log(weapons); // ["shuriken","Katana","wakizashi"]
```

map函式為陣列裡的每一筆資料項呼叫「回呼函式」一次
再利用回呼函式的回傳值建立一個新陣列
### 測試陣列資料項
了解全部或部份的資料項是否滿足某種條件。
- every()
```javascript!
const ninjas = [
{ name: "Yagyu", weapon: "shuriken" },
{ name: "Yoshi" },
{ name: "Kuma", weapon: "wakizashi" }
];
// in運算子用來檢查屬性名稱是否存在物件中
const allNinjasAreNamed = ninjas.every((ninja) => "name" in ninja);
console.log(allNinjasAreNamed); // true
const allNinjasAreArmed = ninjas.every((ninja) => "weapon" in ninja);
console.log(allNinjasAreArmed); // false
```
every函式為陣列每一筆資料項呼叫回呼函式一次,每次呼叫都回傳true,則every方法回傳true,反之回傳false
只要有一項回傳false,後續資料項都不須再檢查
---
- some()
```javascript!
const someNinjasAreArmed = ninjas.some((ninja) => "weapon" in ninja);
console.log(someNinjasAreArmed); // true
```
some函式只要陣列裡有一筆資料項呼叫回呼函式時回傳true,就會回傳true
有一項回傳true,後續資料項都不須再檢查
---
- find():在陣列搜尋資料項
```javascript!
const ninjas = [
{ name: "Yagyu", weapon: "shuriken" },
{ name: "Yoshi" },
{ name: "Kuma", weapon: "wakizashi" }
];
const ninjaWithWakizashi = ninjas.find((ninja) => ninja.weapon === "wakizashi");
console.log(ninjaWithWakizashi);
// { "name": "Kuma","weapon": "wakizashi"}
```
會搜尋第一筆,讓回呼函式回傳true的資料項
```javascript!
const ninjaWithKatana = ninjas.find((ninja) => ninja.weapon === "Katana");
console.log(ninjaWithKatana); // undefined
```
如果找不到符合的,就會回傳 `undefined`
---
- filter():搜尋符合某個指定條件的多筆資料項
```javascript!
const armedNinjas = ninjas.filter((ninja) => "weapon" in ninja);
console.log(armedNinjas);
// [{"name": "Yagyu","weapon": "shuriken"},{"name": "Kuma","weapon": "wakizashi"}]
```
9.9圖中文版有錯,英文版對
---
搜尋陣列的索引值
- indexOf()
```javascript!
const ninjas = ["Yagyu", "Yoshi", "Kuma", "Yoshi"];
// indexOf:把要找出索引值得資料項傳入
ninjas.indexOf("Yoshi"); // 1
```
- lastIndexOf()
如有多筆相同的資料項,要知道最後一筆符合結果的索引值:
```javascript!
ninjas.lastIndexOf("Yoshi"); // 3
```
- findIndex()
回傳第一筆資料項的索引值
```javascript!
const yoshiIndex = ninjas.findIndex((ninja) => ninja === "Yoshi");
console.log(yoshiIndex); // 1
```
---
### 對陣列做排序
語法
```
array.sort((a, b) => a - b);
```
JS引擎實作排序演算法,唯一須提供一個回呼函式讓排序演算法知道兩筆資料項之間的關係
- 回呼函式回傳一個小於0的值,資料項a在資料項b前面
- 回呼函式回傳0,資料項a和資料項b相等
- 回呼函式回傳一個大於0的值,資料項a在資料項b後面
依照字母順序對陣列做排序:
```javascript!
const ninjas = [{ name: "Yoshi" }, { name: "Yagyu" }, { name: "Kuma" }];
ninjas.sort((ninja1, ninja2) => {
if (ninja1.name < ninja2.name) {
return -1;
}
if (ninja1.anme > ninja2.name) {
return 1;
}
return 0;
});
console.log(ninjas);
/*
{
"name": "Kuma"
},
{
"name": "Yagyu"
},
{
"name": "Yoshi"
}]
*/
```
回傳-1,代表ninja1在ninja2之前
回傳1,代表ninja1在ninja2後面
如果兩者相等,則會回傳0
### 對陣列進行彙整計算
```javascript!
const numbers = [1, 2, 3, 4];
let sum = 0;
numbers.forEach((number) => (sum += number));
console.log(sum); // 10
```
- reduce()
語法
```
array.reduce(callback, initialValue)
```
```javascript!
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((aggregated, number) => aggregated + number, 0);
console.log(sum); // 10
```
reduce方法會先取得初始值0,並對陣列每一筆資料項呼叫回呼函式,呼叫時是以上次呼叫的結果,以及當前陣列資料項為引數,最後結果為單獨一個值
## 9.1.5 重複使用內建的陣列函式
摹擬陣列方法:把Array上的方法用在普通物件上
```javascript!
const elems = {
length: 0,
add: function (elem) {
Array.prototype.push.call(this, elem);
},
gather: function (id) {
this.add(document.getElementById(id));
},
find: function (callback) {
return Array.prototype.find.call(this, callback);
}
};
elems.gather("first");
console.log(elems.length); // 1
console.log(elems[0].nodeType); // 1,回傳1表示是element
console.log(elems.length === 1 && elems[0].nodeType,"verify that we have an element in our stash");
// 1 "verify that we have an element in our stash"
elems.gather("second");
console.log(elems.length); // 2
console.log(elems[1].nodeType); //1
console.log(elems.length === 2 && elems[1].nodeType,"Verify the other insertion");
// 1 "Verify the other insertion"
const findElement = elems.find((elem) => elem.id === "second");
console.log(findElement); // <input id="second">
```
`length`:用來計算陣列的資料項數目
`add`:用來將一筆資料項加到摹擬陣列的最後面
`gather`:根據id來找到元素,並儲存起來
`find`:在自訂物件elems搜尋任意的資料項
**執行`elems.gather("first")`並印出`elems`:**

**執行`elems.gather("second")`並印出`elems`:**

[nodeType](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType)
[push() - Calling push() on non-array objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push#calling_push_on_non-array_objects)