---
# System prepended metadata

title: 【尚硅谷】JavaScript基礎&實戰 筆記
tags: [javascript, coding]

---

# 【尚硅谷】JavaScript基礎&實戰 筆記
###### tags: `coding` `javascript`
## 一、JavaScript簡介
###  JavaScript的起源
在JS出現前是將資料傳送至服務器，再將結果回傳。但當時網速慢，導致使用者體驗不佳，為改進此問題而產生的。JavaScript出現主要是用於處理前端網頁驗證。例如：用戶名的長度、密碼的長度、郵箱的格式。
演變至今，
實際上，如今網速已經改進許多，JS誕生的初衷已不再重要。JS的運用也變得廣泛，包括：遊戲、後端…。
為了確保不同瀏覽器上運行的JavaScript標準一致，所以幾個公司共同訂了JS的標準命名為ECMAScript。
###  實現
ECMAScript是一個標準，而這個標準需要由各廠商去實現。不同瀏覽器廠商對該標準會有不同的實現。


| 瀏覽器  | JavaScript實現方式 |
| ------- | ------------------ |
| Firefox | SpiderMonkey       |
| Safari  | JavaScriptCore     |
| Chrome  | v8                 |

![](https://i.imgur.com/7aWgfbs.png)
###  JavaScript的特點
1. 解釋型語言
2. 類似於C和Java的語法結構
3. 動態語言
4. 基於原型的物件導向
## 二、JS基礎
###  三種基本輸出方式
程式執行順序是由上至下依序執行
``` =javascript
//1.向控制台輸出
  console.log('msg');
  
//2.向頁面輸出
document.write('this is my first page');

//3.彈跳提示窗
alert('warning');
```
###  JS編寫位置
``` =javascript
注意：引號外雙內單
html標籤(不建議耦合性過高的寫法)

1. onclick屬性
    <button onclick="alert('JS可寫在onclick屬性上');">click</button>
    
2. href屬性
   <a href="javascript:alert('JS可寫在onclick屬性上');">click</a>
   
   [補充]以下為避免點擊作用
   <a href="javascript:;">click</a>
   
JS代碼鑲嵌
3. script內部 
  <script type="text/javascript"></script>
  
4. 外部引用（內部不可再引用）
  <script src="path"></script>
```
### 基本語法
1.JS嚴格區分大小寫

2.每一條語句結尾應加上分號。
  如果不加，瀏覽器會自動加上。但有時會導致一些無預期結果
  （ASI自動插入分號）
  
3.JS終會自動忽略多個空格和換行，所以可以用來格式化
  （最常見的問題：究竟要空四格還是空二格好？）
###  字面量(literal)和變量(variable)
![](https://i.imgur.com/EjURrgn.png)
> [字面量表示如何表達這個值，一般除去表達式，給變量賦值時，等號右邊都可以認為是字面量。
字面量分為字符串字面量(string literal )、數組字面量(array literal)和對象字面量(object literal)，另外還有函數字面量(function literal)]

一般不會直接使用字面量，而是將字面量保存在變量中，使用變量。

字面量都是一些不可改變的量。比如：1、2、3、4、5
``` =javascript
//1.為變量賦值
a = 123;
a = 456;
a = 123546576587;

//2.聲明和賦值同時進行
var b = 789;
var c = 0;
```
###  標識符（identifier）
在JS中基本上可以由我們命名的就可以稱為標識符
比如：變量名、函式名、屬性名…。
以下為基本命名規範：
1. 標識符可以包含數字、英文字母、＿、＄
2. 標識符不可以數字開頭
3. 不可使用關鍵字（keyword）及保留字(reserve word)
4. 建議採用駝峰命名法

JS或底層是以**unicode**編碼的方式保存標識符
###  基本數據類型
字面量可以為以下幾種數據類型，當中又區分為**基本數據類型**和**引用數據類型**：
``` =javascript
數據類型檢查工具：
  1. typeof運算符  : 返回結果值 (不能判斷null，無法仔細區分object中的哪種類型)
  2. instanceof - 判斷object的具體類型(object, array, function)  
  3. === / ==  :返回布林值 (用來判斷是否為null, undefined)
  
  1.基本
   var a;
   console.log(a, typeof a); //undefined  'undefined'
   console.log(typeof typeof a); //string
   console.log(undefined === 'undefined'); //false
   console.log(a === undefined, typeof a==='undefined'); 
   var b = null;
   console.log(b, typeof b); //null, 'object'
   //判斷是否為null要用全等來判斷
   console.log(b === null); //true
   
   
  2.對象
  var b1 = {
    b2:[1,'abc',console.log],
    b3: function(){
      console.log("b3");
    }
}
console.log(b1 instanceof Object); //true
console.log(b1.b2 instanceof Array, b1.b2 instanceof Object); //true true
console.log(b1.b3 instanceof Function, b1.b3 instanceof Object);//true true
console.log(typeof b1.b3 === 'function'); //true
```
基本數據類型：
####  1.字串
``` =javascript
  1)一般字符
    //需注意引號使用
    var hello = '789'; //使用單引號
    var world = "123"; //使用雙引號
    var str = '我說:"今天天氣真不錯"'; //外單內雙
    var str1 = "我還是說:'今天天氣真不錯'"; //外雙內單
    var str2 = "我說:\"我堅持要用雙引號\""; //堅持嵌套則使用轉譯字符
    
  2)轉譯字符
    \" 表示 "
    \' 表示 '
    \n 表示換行（newline）
    \t 制表符  （tab）
    \\ 表示 \
    str = "我說:\"今天\t天氣不錯！\"";     //我說:"今天 天氣不錯！"
    str = "\\\\\\";                     // \\\
    
  3)樣板字串 Template Literial
  
  4)強制轉換為String （個別型別還有自己的包裹物件）
    方式一：toString()
      1.該方法不會影響到原變量，他會將轉換結果返回
      2.注意null和undefined沒有toString()方法，調用會報錯
      
    方式二：String()
      1.要被轉會的變數作為參數傳進String()函式
      2.Number和Boolean實際上是調用toString()方法，
        String()方法會直接將null和undefined轉換為"null"和"undefined"
```
#### 2.數字
``` =javascript
  數字包括整數和浮點數（小數）
  
  1)JS中可以表示的數字最大值
    Number.MAX_VALUE
    //如果使用Number表示的數字超過最大值，則會返回Infinity(表示正無窮)
    a = Number.MAX_VALUE * Number.MAX_VALUE;
    console.log(a);
  
  2)JS中可以表示的數字最小正值
    Number.MIN_VALUE
    a = Number.MIN_VALUE * Number.MIN_VALUE;
    console.log(a); //0
    
  3)NaN  
    //Not a number的意思
    
  4)浮點運算結果可能不精確
    var c = 0.1 + 0.2; 
    console.log(c); //0.3000000004  反正就是結果不精確
    //因為所有運算最終都要轉換成二進制，但二進制沒辦法準確表達十進位制
    //所以涉及錢的計算（精確度比較高的運算），都建議在服務器端做計算
    
  5)強制轉換為Number
  
    方法一：Number()，也是透過參數形式傳遞
    1.字串轉數字
      -純數字字串 --> 數字
      -非數字字串或數字與非數字混合之字串 --> NaN
      -空串或內容皆為空格的字串 --> 0
    2.布林轉數字
      -true --> 1
      -false --> 0
    3.null轉數字
      -null --> 0
    4.undefined轉數字
      -undefined --> NaN （超級重要，不要忘記）
      
    方法二：parseInt()和parseFloat() 
    //這種方式專門用來對付字串(數字與非數字混合字串特好用)
    1.字串
    //由傳入參數第一位開始比對，直至非數字字串為止
    var a = "123456px";
    a = parseInt(a);
    console.log(a); //123456
    a = "a123456px";
    a = parseInt(a);
    console.log(a); //NaN    
    a = "123.456px";
    a = parseInt(a);
    console.log(a); //123
    a = "123.456px";
    a = parseFloat(a);
    console.log(a); //123.456
    2.非字串
    a = true;
    a = parseInt(a);
    console.log(a); //NaN
    
  6)其他進制的數字
    1.十六進制 --> 0x開頭
    2.八進制 --> 0開頭  //小心不要和十進位混淆
    3.二進制 --> 0b開頭
 
    a = "070";
    a = parseInt(a,10); //第二參數是進位制，這樣就可讓所有瀏覽器顯示一致的結果
```
####  3.布林
``` =javascript
  1)布林值只有兩個，用來做邏輯判斷
    1.true
    2.false
    
  2)轉換為Boolean()     //  13 Truthy 與 Falsy
  
    1.數字轉布林 --> 除了0和NaN，其餘都是true 
      var a = 123;
      a = Boolean(a);
      console.log(a); //正值為true
      a = Infinity;
      a = Boolean(a);
      console.log(a); //true
      a = -123;
      a = Boolean(a);
      console.log(a); //負值也是true
      
     2.字串轉布林 -->除了空串，其餘都是true     
      var a = "hello";
      a = Boolean(a);
      console.log(a); //true
      a = "false";
      a = Boolean(a);
      console.log(a); //true
      a = ""; //沒空格
      a = Boolean(a);
      console.log(a); //false
      a = "   ";  //有空格
      a = Boolean(a);
      console.log(a); //true
      
    3.null&undefined轉布林 -->都是false
    
    4.物件轉布林 --> true
```
####  4.null(空值)
``` =javascript
  null類型值只有一個，就是null
  null這個值專門用來表示一個為空的物件（這句很重要！）
  var a = null;
  console.log(typeof a);  //'object'
```
####  5.undefined（未定義）
``` =javascript
  undefined類型值只有一個，就是undefined
  聲明變量卻未賦值時的預設值
```
####  6.symbol（es6）
####  7.bigInt（es6）
引用數據類型：

  物件（MDN將陣列包括在物件中）
###  算術運算符
運算符也叫操作符
通過運算符可以對一個或多個值進行運算，並“返回運算結果” （這概念很重要）
比如：typeof就是運算符，他會將該值的類型以字串形式返回
``` =javascript
var a = 123;
var result = typeof a;
console.log(typeof result); //string

算術運算符不會改變原值！！！！！（很重要）
+ 
  1)運算符左右沒字串 -->轉數字再行運算（任何值跟NaN相加都是NaN）
  2)運算符左右有字串 -->轉字串再行拼接
  3)c = c+ "";是種隱式的類型轉換，由瀏覽器自動完成，實際上它也是調用String()函數
  
- 、*、/   都轉數字再運算
```
####  加法運算符
``` =javascript
a + 1;
console.log(a); 123

result = a + 1; //將返回之運算結果賦值給變數
console.log(a); //123

a = a + 1; //替a重新賦值
console.log(a); //124

result = true + 1;
console.log(result); //2 把true轉為數字計算

result = true + false;
console.log(result); //1

result = 2 + null;
console.log(result); //2

result = 2 + NaN;
console.log(result); //NaN     //任何值跟NaN相加都是NaN

result = "123" + "456";
console.log(result); //"123456"  //字串相加結果為拼接字串

result = "123"+ 1;
console.log(result); //"1231"   //任何值和字串相加，都會先轉為字串再拼接

result = true + "hello";
console.log(result); // "truehello"

var c = 123;
c = c+ ""; //轉字串
console.log(c); //"123" 

//運算是由左至右
result = 1 + 2 + "3";
console.log("result = "+result); //"33"
result = "1" + 2 + 3;
console.log("result = "+result); //"12
```
####  減法運算符
``` =javascript
result = 100 - "1";
console.log(result); //99   //都轉數字再運算
```
####  乘法運算
``` =javascript
result = 2 * undefined; //NaN
result = 3 * null;
```
###  一元運算符
``` =javascript
一元運算符，只需要一個操作數
＋ 正號
-  負號 對數字取反
   
//對非Number類型的值，會先轉換成Number，然後再運算
  進行隱式數字轉型，與調用Number函式相同
var a = 123;
console.log(a); //123

a = true;
a = +a;
console.log(a); //1

a = "18";
a = +a;
console.log(a); //18

var result = 1 + "2" + 3;
console.log(result); //"123"

result = 1 + +"2" + 3; //將字串2轉型為數字2
console.log(result); //6
```
###  自增和自減

1.自增 ++
    通過自增可以使變量在自身基礎上增加1
    注意：算術運算符不會改變原值！！！！！（很重要）
         但是對一個變量自增以後，原變量的值會立即改變
    自增分兩種：後++(a++)和前++(++a)
        無論是哪種自增，都會使原變量立即自增
            不同的是a++和++a的**值**不同
        a++（表達式）的值等於原變量的值（自增前的值）
        ++a（表達式）的值等於變量的新值（自增後的值）
        
2.自減 - -
    道理相同
``` =javascript
var a = 1;
a++; //自增 原變量的值會立即改變(所以這句話很重要！！！！)
console.log(a); //2

//仔細區分變量與表達式的不同
a  變數
a++ 表達式

var a = 1;
console.log(a++); //1 值等於原變量的值（自增前的值）
console.log(a); //2

var a = 1;
console.log(++a); //2 值等於變量的值（自增後的值）
console.log(a); //2

var c = 10;
c++; //c++值為10，c的值為11
console.log(c++); //c++的值為11，c的值為12

var d =20;
console.log(++d); //++d的值為21，d的值為21
console.log(++d); //++d的值為22，d的值為22

var d = 20;
var result = d++ + ++d + d; 
console.log(result); //20+22+22

var d = 20;
d = d++; //d++值為20，然後賦值給d變量，所以變量d是20
console.log(d);
```
###  自增自減練習
``` =javascript
var n1 = 10,n2 = 20;
var n = n1++; //10
console.log(n); //10
console.log(n1);//11
n = ++n1; //12
console.log(n); //12
console.log(n1);//12
n = n2--; //20
console.log(n); //20
console.log(n2);//19
n = --n2; //18
console.log(n); //18
console.log(n2);//18
```
###  邏輯運算符
**!非**
   !!a -->如果對非布林值進行隱式的類型轉換，等同調用Boolean()
//對非布林值進行與或運算時，會先將其轉為布林值，再做運算，並且返回"""原值""""

**&&與**
   找假，若前者為真，則返回後者
   
**||或**
   找真，若前者為假，則返回後者
   ``` =javascript
   var a = true;
console.log(a);
a = !a;
console.log(a); //false

var b = 10;
b = !b; //先把10轉為布林就是true，然後取反，就是false
console.log(b); //false

var result = 1 && 2;
console.log(result);  //2

result = 0 && 2;
console.log(result);  //0

result = 2 && 0;
console.log(result);  //0

result = NaN && 0;
console.log(result);  //NaN

result = 1 || 2;
console.log(result);  //1

result = 2 || NaN;
console.log(result);  //2

result = 0 || 2;
console.log(result);  //2

result = "   "|| 0;
console.log(result);  // (一片空白) 

result = ""|| {};
console.log(result);  //{}
   ```
  ###  賦值運算符
  = += -= *= /= %=
  ```=javascript
可以將運算符右側值賦予給左側變量

var a = 10;
a += 5; //a = a + 5
console.log(a); //15
a -= 5; //a = a - 5
console.log(a); //10
  ```
  ###  關係運算符
  通過關係運算符比較兩個值之間的大小，並**返回布林值**
>、=、<、>=、<=

**運算符左右都是數字 -->直接運算
運算符左右至少一側是數字 --> 對於非數字，也會先轉換成數字，再行運算
運算符左右都是字串 --> 依unicode一位一位做比較
                    如果字串內為數字，則可能會得到不如預期的結果，要先對字串進行轉換**
``` =javascript
5 > 10 //false
5 > 4 //true

console.log(1 >= "0") //true
console.log(1 > true) //將true先轉為數字1，但是1>1不成立，所以返回結果為false
console.log(10 > null) //true
console.log(true > false) //true 因為1>0

//任何值和NaN做比較都是false
console.log(10 <= "hello"); //false
//首先，把"hello"轉為數字結果為NaN，然後任何值跟NaN比較都是false

console.log("a" < "b") //true
console.log("abc" < "bcd") //true
console.log("11" < "5"); //true
//如果字串內為數字，則可能會得到不如預期的結果，要先對字串進行轉換
console.log("11234" > +"5") //先將其中一邊轉為數字
```
### unicode編碼
1.在字串中使用unicode
   **\u四位編碼**
  ``` =javascript
console.log("\u2620");
```
2.在網頁中使用unicode編碼
  **&#編碼;**
  這裡的編碼需要的是10進制(需要進行轉換)
  ``` =html
<h1>&#1;</h1>
```
###  相等運算符
``` =javascript
用來比較兩個值是否相等，並返回布林值

1.寬鬆相等運算 ==
  當使用==比較時，如果類型不同，會先轉換成同類型再比較
2.寬鬆不相等 !=
3.嚴格相等運算(全等) ===
  不會轉換型態
4.嚴格不相等運算 ！==

var a = 10;
console.log(a == 4); //false

console.log("1" == 1); //true
console.log(true == "1"); //true //將字串和布林值轉為數字做比較
console.log(null == 0); //false //這裡沒有把null轉成數字0做比較

//undefined衍生自null，所以做判斷時，會返回true
console.log(undefined == null); //true

//NaN不和任何值相等，包括他自己
console.log(NaN == NaN); //false
//如果想判斷是否為NaN，該如何處置？
var b = NaN;
//此時可通過isNaN函式判斷，也是返回布林值
isNaN(b);  //true

console.log(10 != 5); //true
console.log("1" != 1); //false

console.log(null === undefined); //null跟undefined相等但不全等
```
###  條件運算符
條件運算符也叫三元運算符
  語法：
      條件表達式？語句1 : 語句2;
  執行的流程：
      條件運算符在執行時，首先對條件表達式進行求值
        如果該值為true，則執行語句1，並返回執行結果
        如果該值為false，則執行語句2，並返回執行結果
  如果條件表達式非布林值，則會自動轉換為布林值
  ``` =javascript
  true?alert("語句1"):alert("語句2");    //alert("語句1")
  
  var a = 10;
  var b = 20;
  a > b? alert("a大"):alert("b大");    //alert("b大")
  
  //獲取a和b中的最大值
  var max = a > b ? a : b;  //先對條件運算符進行運算，然後將結果賦值給變數max
  console.log(max); //20
  
  //獲取a b c 中的大值
  max = max > c? max : c;
  
//不建議使用，可讀性不高  
var max = (a > b) ? (a > c? a:c):(b > c?b:c);
  ```
  ###  運算符的優先級
  ``` =javascript
  var result = 1 || 2 && 3;
console.log(result); //1     //與的優先級高於或
  ```
  ###  包裝類
  JS中提供了**三個包裝類**，通過這三個包裝類可以將基本數據類型的數據轉換為物件（引用數據類型）
1.String() 2.Number() 3.Boolean()   ->首字母大寫，都是構造函數
但是注意，我們在**實際運用**中**不會使用**基本數據類型的對象
``` =javascript
//創建一個Number類型的"對象"
var num = new Number(3);
var num2 = new Number(3);
console.log(num); //3
console.log(num2); //3
console.log(num == num2);  //false 比較兩個地址
console.log(typeof num); //object
//向num中添加一個屬性
num.hello = "abcdefg";
c.f:向基本數據類型的a變量（number類型）添加屬性會返回undefined
var a = 3;
console.log(a.hello); //undefined

var str = new String("hello");
console.log(str); //"hello"
console.log(typeof str); //object

var bool = new Boolean(true);
var bool2 = true;
console.log(bool); //true
console.log(typeof bool); //object
console.log(bool == bool2); //false

var b = new Boolean(false);
if(b){
   alert("我運行了");
}
//仍然可以運行，因為b是一個物件
```
<用途>如果上述的包裝類不建議使用，其用途何在？
方法和屬性只能添加給對象，不能添加給基本數據類型
當我們對一些基本數據類型的值去調用屬性和方法時，
   瀏覽器會臨時使用包裝類將其轉換為對象，再調用對象的屬性和方法
   調用完以後，再將其轉會為基本數據類型
   ``` =javascript
var s = 123;
s = s.toString();
s.hello = "你好";
console.log(s.hello); //這裡的s和上面的s不同
console.log(s); //"123"
console.log(typeof s); //string
   ```
   ###  字串的方法
   以下方法都不會影響到原字串
``` =javascript
在底層字串是以字符陣列的方式保存
//創建一個字串
var str = "hello atguigu";

//在底層字串是以字符陣列的形式保存
["h","e","l"];
console.log(str[2]); //"l"
console.log(str.length);

1. charAt(index) 
   - 可以返回字串中指定位置的字符
   - 根據索引獲取指定的字符
str = "hello atguigu";
var result = str.charAt(0);
console.log(result); //"h"

2. charCodeAt(index) - 獲取指定位置字符的字符編碼(unicode編碼)
result = str.charCodeAt(1);
console.log(result); //101       e的unicode是101

3. String.formCharCode(unicode)
  - 可以根據字符編碼去獲取字符
result = String.fromCharCode(74);
console.log(result); //"J"

4. concat() 
      - 可以用來連接兩個或多個字串
      - 跟+法效果一樣
result = str.concat("你好","再見");
console.log(result); //"hello atguigu你好再見"

5. indexOf() 
      - 該方法可以檢索一個字串中是否含有指定內容
      - 如果字串中含有該內容，則會返回其""第一次""出現的""索引""
      - 可以指定一個第二個參數，指定開始查找的位置
str = "hello atguigu";
result = str.indexOf("h");
console.log(result); //"0"
result = str.indexOf("g",6);

6.lastIndexOf()
      - 該方法的用法和indexOf()一樣，
        不同的是indexOf是從前往後找，而lastIndexOf是從後往前找
str = "hello atguigu";
result = str.lastIndexOf("g");
console.log(result); //"11"

7. slice()
     - 可以從字串中擷取指定的內容
     -不會影響原字串
str = "abcdefghijk";
str.slice(0,2);
console.log(str); //"ab"

8. substring()
     - 可以用來擷取一個字串，與slice()類似
       不同的是:  
         1.這個方法不能接受負值作為參數，如果傳遞了一個負值，則默認使用0
         2.而且它還自動調整參數位置，如果第二個參數小於第一個，則自動交換
         
str = "abcdefghijk";
result = str.substring(0,2);
console.log(result); //"ab"
result = str.substring(1,-10000); //"b"

9. substr()
   - 用來擷取字串
   - 參數：
       1.擷取開始位置的索引   2.擷取的長度
str = "abcdefg";
result = str.substr(1,2); //"bc"     從索引1開始長度為2的字串  

10.split() 
    - 可以將一個字串拆分為一個"""陣列"""
    - 參數:需要一個字串作為參數，將會根據該字串去拆分陣列
str = "abc, bcd, efg, hij";
result = str.split(",");
console.log(result);  //"abc, bcd, efg, hij"
console.log(result[0]);  //"abc"
console.log(typeof result); //object
console.log(Array.isArray(result)); //true

str = "abc, bcd, efg, hij";
result = str.split("d");
console.log(result);  //"abc, bc,,efg, hij"
console.log(result[0]);  //"abc, bc"

//如果傳遞一個空串作為參數，則會將每個字符都拆分為陣列中的一個元素
str = "abcdefghij";
result = str.split("")
console.log(result); //"a,b,c,d,e,f,g,h,i,j"

11.toUpperCase()
str = "abcdefg";
result = str.toUpperCase();  //"ABCDEFG"

12.toLowerCase()
str = "ABCDEFG";
result = str.toLowerCase();  //"abcdefg"
```
##  三、JS循環流程
###  代碼塊
1. 在JS中替語句分組，沒有其他作用
2. {}內的語句稱為一個代碼塊
3. 在代碼塊後面不加分號
###  流程控制概念
通過流程控制語句可以控制程序執行流程，使程序可以根據一定的條件來選擇執行
語句分類：
  1. 條件判斷語句
  2. 條件分支語句
  3. 循環語句
 ####  1.條件判斷語句
 ``` =javascript
 if語句
 
       語句一: if(條件表達式){語句}
       //if語句在執行時，會先對條件表達式進行求值判斷
       var a = 10;
       if(a > 10)
         console.log("a比10大");
         console.log("誰也管不了我");  //發現他會自動跳出，換言之，需要用{}來分組
         
       語句二:if...else...語句
       var age = 50; 
       if(age >= 60){
         alert("你已經退休了");
       }
       
       語句三:if...else if...else語句
       若值為true，則執行當前語句;
       若值為false，則繼續向下執行;
       如果所有條件都不滿足，則執行最後一個else後的語句
      //注意：應該思考條件表達式的順序，否則會產生死代碼
      //如果堅持要用升冪，那就要把條件表達式的範圍標示清楚
      age = 18;
      if(age > 17 && age <= 30){
         alert("你已經成年了");
       }else if(age > 30){
         alert("你已經中年了");
       }else if(age > 60){
          alert("你已經退休了");
       }else{
           alert("你歲數大了")
       }
```
####  條件判斷語句練習
從鍵盤輸入小明的期末成績：
當成績為100時，"獎勵一輛BMW"
當成績為[80-99]時，"獎勵一台iphone15s"
當成績為[60-80]時，"獎勵一本參考書"
其他時，什麼獎勵也沒有
```=javascript
//升冪寫法
var score = prompt("請輸入小明的期末成績：");
if(score >= 60 && score <80){
   alert("獎勵一本參考書");
}else if(score >= 80 && score <= 99){ 
   alert("獎勵一台iphone15s");
}else if(score == 100){
   alert("獎勵一輛BMW");
}

//降冪寫法(比較常用)
var score = prompt("請輸入小明的期末成績：");
if(score > 100 || score <0 || isNaN(score)){
   alert("請重新輸入0-100之間的數字");
}else{
   if(score == 100){
      alert("獎勵一輛BMW");
   }else if(score >= 80){ 
      alert("獎勵一台iphone15s");
   }else if(score >=60){
      alert("獎勵一本參考書");
   }
}
```
編寫程序，由鍵盤輸入三個整數分別存入變量num1、num2、num3
對他們進行排序，並且**從小到大輸出**
``` =javascript
var num1 = prompt("請輸入第一個數字:");
var num2 = prompt("請輸入第二個數字:");
var num3 = prompt("請輸入第三個數字:");

if(num1 > num2){
   if(num3 > num1){
     console.log(`${num2} , ${num1} , ${num3}`);
   }else{
     if(num3 > num2){
       console.log(`${num2} , ${num3} , ${num1}`);
     }else{
       console.log(`${num3} , ${num2} , ${num1}`);  
     }
   }
}else{ 
   if(num3 > num2){
     console.log(`${num1} , ${num2} , ${num3}`);
   }else{ 
     if(num3 > num1){ 
        console.log(`${num1} , ${num3} , ${num2}`);
     }else{ 
        console.log(`${num3} , ${num1} , ${num2}`);
     }
   }
}

```
大家都知道，男大當婚，女大當嫁，那麼女方家長要嫁女兒，當然要提出一定的條件：
高:180cm以上;富:1000萬以上;帥:500以上
如果三個條件同時滿足，則:'我一定要嫁給他'
如果三個條件有為真的滿足，則:'嫁吧，比上不足，比下有餘'
如果三個條件都不滿足，則:'不嫁！'
``` =javascript
var height = prompt("請輸入你的身高(CM):");
var money = prompt("請輸入你的資產總額(萬):");
var face = prompt("請輸入你的顏值(PX):");

if(height>180 && money>1000 && face>500){
   alert("我一定要嫁給他");
}else if(height>180 || money>1000 || face>500){
   alert("嫁吧，比上不足，比下有餘");
}else{
   alert("不嫁！");
}
```
####  2.條件分支語句
``` =javascript
條件分支語句也叫switch語句

//根據num的值，輸出對應的中文
var num = 1;
//switch與if(條件判斷語句)等價，兩者功能重複
if(num == 1){
    console.log("壹");
}else if(num ==2){
    console.log("貳");
}else if(num ==3){
    console.log("參");
}

語法：
   switch(條件表達式){
       case 表達式:
            語句...
            break;
       case 表達式:
            語句...
            break;   
       case default:
            語句...
            break;
   }
   
//在執行一次將case後的表達式值和switch後的條件表達式值進行全等比較
     如果比較結果為true，則從當前case處開始執行代碼
          當前case後的所有代碼都會執行，我們可以在case後面跟著break
          這樣可以確保只會執行當前case後的語句，而不會執行其他case
     如果比較結果為false，則繼續向下比較
          如果所有的比較結果都為false，則只執行default後的語句
          
   switch(num){
       case 1:
            console.log("壹");
            break;
       case 2:
            console.log("貳");
            break;   
       case 3:
            console.log("參");
            break;
       default:
            console.log("非法數字");
            break;   
   }
```
####  條件分支語句練習
對於成績大於60分的，輸出"合格"，低於60分的，輸出"不合格"
``` =javascript
//我的解法
var score = prompt("請輸入您的成績：");
switch(true){
  case score>=60:
    console.log("合格");
    break;
  case score<60:
    console.log("不合格");
    break;
  default:
    console.log("此為非法輸入！");
    break;
}

//老師的解法
var score = 55;
//比較十位數取整結果
switch(parseInt(score/10)){
  case 10:
  case 9:
  case 8:
  case 7:
  case 6:
     console.log("合格");
     break;
  default:
     console.log("不合格");
     break;
}
```
從鍵盤接收整數參數，如果該數為1-7，打印對應的星期，否則打印非法整數。
``` =javascript
var day = prompt("請輸入1-7之間的數字：");
day = parseInt(day);
switch(day){
  case 1:
    console.log("星期一");
    break;  
  case 2:
    console.log("星期二");
    break;  
  case 3:
    console.log("星期三");
    break;  
  case 4:
    console.log("星期四");
    break;  
  case 5:
    console.log("星期五");
    break;  
  case 6:
    console.log("星期六");
    break;  
  case 7:
    console.log("星期日");
    break;  
  default:
    console.log("非法整數");
    break;
}
```
####  3.循環語句
``` =javascript
向頁面中輸出連續的數字

第一階段：
document.write(1);
document.write(2);
//會發現數字連在一起，向頁面輸出結果為12

第二階段：
//如果希望能輸出完一個數字後換行。對於換行會聯想到字串內容中的轉譯字符\n(new line)
  但是因為現在是針對頁面輸出，所以要使用網頁標籤<br />，變成以下：
document.write(1+"<br />");
document.write(2+"<br />");
document.write(3+"<br />");
document.write(4+"<br />");

第三階段：
//觀察到數字遞增，所以聯想到自增用法，改寫為以下：
var n = 1;
document.write(n++ +"<br />");
document.write(n++ +"<br />");
document.write(n++ +"<br />");
document.write(n++ +"<br />");

第四階段：
//使用循環語句，達到反覆執行一段代碼多次的效果
 while循環
   語法:
     while(條件表達式){
         語句...
     }
     
   while語句在執行時，
      先對條件表達式進行求值判斷，
         如果值為true，則執行循環體，
            循環體執行完畢以後，繼續對表達式進行判斷
            如果為true，則繼續執行循環體，以此類推
         如果值為false，則終止循環
         
var n = 1;
//將這種將條件表達式寫死為true的循環，稱為死循環
//可以使用break，來終止循環
while(true){
  alert(n++);
  //判斷n是否是10
  if(n ==10){
    //退出循環
    break;
  }
}

//創建一個循環，往往需要三個步驟
// 1. 創初始化一個變量
var i = 1;
// 2. 在循環中設置一個條件表達式
while(i <= 10){
   alert(i);
   // 3. 定義一個更新表達式，每次更新初始化變量
   d
```
#### do...while循環
 語法:
     do{
       語句...
     }while(條件表達式)
``` =javascript  
可以將while中的範例改寫如下：

var i = 1;
do{
  document.write(i++ +"<br />");
}while(i<=10);
```
do...while語句在執行時，會先執行循環體，
     循環體執行完畢後，再對while後的條件表達式進行判斷
         如果結果為true，則繼續執行循環體，
         循環體執行完畢以後，繼續對表達式進行判斷，以此類推
         如果值為false，則終止循環
         
do...while無論成立不成立，都至少會執行一次
####  for語句
  for循環中提供了專門的位置用來方三個表達式：
      1.初始化表達式
      2.條件表達式
      3.更新表達式
  for循環的語法：
     for(初始化表達式;條件表達式;更新表達式){
           語句...
     }
#### while vs for
``` =javascript

while vs for
// 1. 創初始化一個變量
var i = 1;

// 2. 在循環中設置一個條件表達式
while(i <= 10){
   alert(i);
   
// 3. 定義一個更新表達式，每次更新初始化變量
   document.write(i++ +"<br />");
}


//把上面改寫成以下:
for(var i = 1;i<=10;i++){
   document.write(i +"<br />");
}

for循環的執行流程：
  1.執行初始化表達式，初始化變量(只會執行一次)
  2.執行條件表達式，判斷是否執行循環
        如果為true，則執行語句（3.）
        如果為false，終止循環
  4.執行更新表達式，更新表達式執行完畢繼續重複（2.）
1. --> 2. --> 3. --> 4. --> 2. --> 3.  --> 4.......

//注意：for循環中的三個部分都可以省略，也可以寫在外部（像寫while一樣）
var i = 0;
for(;i<10;){
    alert(i++);
}
//注意二：如果在for循環中不寫任何的表達式，只寫兩個;
         此時循環是一個死循環會一直執行下去
for(;;){
   alert("hello");
}
```
#### 循環語句練習
假如投資的年利率為5%，試求從1000塊漲到5000塊，需要花費多少年？
``` =javascript
var money = 1000, year = 0;
while(money < 5000){
  money *= 1.05;
  year++;
}
console.log(year);
console.log(money);
```
**條件判斷語句練習（修改:加上prompt的判斷，直到合法有效輸入才進行下一個程序）**
``` =javascript
while(true){ //無論如何都一定要執行
  var score = prompt("請輸入小明的期末成績：");
  if(score < 100 && score > 0){
     break;
  }
  alert("請輸入0-100之間的成績！");
}

if(score == 100){
      alert("獎勵一輛BMW");
   }else if(score >= 80){ 
      alert("獎勵一台iphone15s");
   }else if(score >=60){
      alert("獎勵一本參考書");
   }
```
打印1-100之間所有奇數之和
``` =javascript
//我的解法
let sum = 0;
for(var n =0;2*n+1<=100;n++){
  let odd = 2*n+1;
  sum += odd;
}
console.log(sum);

//老師的解法（不能被二整除的）
let sum = 0;
for(let i=1;i<=100;i++){
    if(i %2){
    sum+= i;
    }
}
console.log(sum);
```
打印1-100之間的數
``` =javascript
for(let i=1;i<=100;i++){
    console.log(i);
}
```
打印1-100之間所有7的倍數的個數及總和
``` =javascript
let sum = 0, count = 0;
for(let i=1;i<=100;i++){
    if(i %7 == 0){
    sum+= i;
    count++;
    }
}
console.log(sum);
console.log(count);
```
水仙花數是指一個3位數，他的每個位上的數字的3次冪之和等於他本身。
（例如：1^3+5^3+3^3 = 153），請打印所有的水仙花數。
``` =javascript
for(let i=100;i<=999;i++){
    a=parseInt(i/100);
    b=parseInt((i-a*100)/10);
    c=i/10;
    if((Math.pow(a,3)+Math.pow(b,3)+Math.pow(c,3)) == i){
       console.log(i);
    }
  }
```
在頁面中接收一個用戶輸入的數字，並判斷該數是否為質數。
  質數:只能被1和他自身整除的數，1不是質數也不是合數，質數必須是大於1的自然數。
  ``` =javascript
  var num = prompt("請輸入一個自然數:");
if(num <= 1){
   alert("該值不合法！");
}else{
   var flag = true;  //設置flag!!!!!
   for(let i=2;i<num; i++){
     if(num%i == 0){  // 被2到(自身-1)整除一定不是質數
       flag =false; //不可以用break，不然跳出的是整個迴圈
     }  
   }
   if(flag){
     alert(num+"是質數");
   }else{
     alert("這不是質數");
   }
}
  ```
打印2-100之間的所有質數
for嵌套寫法

**<補充>如何測試性能？
在程序執行前，開啟計時器
console.time("計時器名稱")
它需要一個字串作為參數，這個字串將會作為計時器的標示
他需要在外瀏覽器上使用**
``` =javascript
console.time("test");
for(let i=2;i<=100;i++){
   var flag = true;  //設置flag!!!!!
   for(let j=2;j<i; j++){
     if(i%j == 0){  // 被2到(自身-1)整除一定不是質數
       flag =false;
       break; //透過計時器可知break和continue可以大大提升性能
     }  
   }
   if(flag){
     console.log(i);
   }
 }
console.timeEnd("test"); //終止計時器
```
打印2-100之間的所有質數
for嵌套寫法(提升性能)  
**從因數角度分析**
試以36為例
1 36
2 18
3 12
4 9
6 6
僅需嘗試1,2,3,4,6，不需測試6,9,12,18,36
換言之，以開根號為基準，後面的數字都可以不用測試了。
``` =javascript
for(let i=2;i<=100;i++){
   var flag = true;  //設置flag!!!!!
   for(let j=2;j<=Math.sqrt(i); j++){
     if(i%j == 0){  // 被2到(自身-1)整除一定不是質數
       flag =false;
       break; //透過計時器可知break和continue可以大大提升性能
     }  
   }
   if(flag){
     console.log(i);
   }
 }
```
打印*三角形
``` =javascript
       行數  *個數
*       1    j<1  i=0
**      2    j<2  i=1
***     3    j<3  i=2
****    4    j<4  i=3
*****   5    j<5  i=4

for(let i=0;i<5;i++){  //圖高(行數)
  for(let j=0;j<i+1;j++){  //圖寬（＊個數）     document.write("*&nbsp;&nbsp;&nbsp;"); //空格還是用&nbsp;控制
  }
  document.write("<br />");
}
```
打印99乘法表
``` =javascript
                                行數(i)      j是寬度，相當於該行上變動的數
1*1=1                            1                 
1*2=2  2*2=4                     2      
1*3=3  2*3=6 3*3=9               3      
...
1*9=9  ...          9*9=81       9      


for(let i=1;i<10;i++){
   for(let j=1;j<=i ;j++){
      document.write(`${j}*${i}=${i*j}&nbsp;&nbsp;`);
   }
   document.write("<br />");
}
```
####  Break和Continue
``` =javascript
break關鍵字可用來退出循環語句或switch（但不包括if）

//if語句中不能使用break和continue
if(true){
  break;
  console.log("hello");
}

//在for循環中可以使用break
for(let i=0;i<5;i++){
  console.log(i);
  break;
}

//在for循環中的if裡面可以使用break;
//因為break是終止外層的循環語句
for(let i=0;i<5;i++){
  if(i ==2){
    break;
  }
}

//break只會對離自己最近的循環產生影響
for(let i=0;i<5;i++){
  console.log("@外層循環"+i);
  for(let j=0;j<5;j++){
    break;    //僅終止內層，不會影響到外層
    console.log("內層循環"+j);
  }
}

//想終結特定的循環，則
可以為循環語句創建一個label，來標示當前循環
label:循環語句
使用break語句時，可以在break後跟著一個label，
這樣break將會結束指定的循環，而不是最近的

outer:
for(let i=0;i<5;i++){
  console.log("@外層循環"+i);
  for(let j=0;j<5;j++){
   break outer;    //在內層指定終止外層循環
   console.log("內層循環:"+j);
  }
}

//continue關鍵字可以用來跳過當次循環，後續程序不執行，直接接續更新表達式
//continue也是默認只對最近循環起作用
//可以在continue後跟著一個label，
這樣continue將會結束指定的循環，而不是最近的
outer:
for(let i=0;i<5;i++){
  console.log("@外層循環"+i);
  for(let j=0;j<5;j++){
   //continue outer;    //效果就和break一樣
   if(j == 2){
     continue; //內層循環在等於2的時候跳過當次循環
   }
   console.log("內層循環:"+j);
  }
```
##  四、JS引用數據類型：物件
###  基本介紹
基本數據類型是單一的值，值和值之間沒有任何關聯。
在JS中如果以基本數據類型表達一個人的信息，呈現如下：
``` =javascript
var name = "孫悟空";
var gender = "男";
var age = 18;
//如果使用基本數據類型，我們創建的變量都是獨立，不能成為一體。
```
相對的，物件中屬於複合的數據類型，在物件中可以保存多個不同數據類型的屬性。
物件的分類：
- 內建物件
  - 由ES標準定義的對象，在任何ES(一般瀏覽器、NodeJS)的實現中都可以使用
  - 比如：Math, String, Number, Boolean,Function, Object…
- 宿主物件
  - 由JS瀏覽器提供的對象，目前來講主要指由瀏覽器提供的物件
  - 比如:DOM, BOM, console, document
- 自定義對象
  - 由開發人員自己創建的物件
###  創建物件
使用new關鍵字調用的函數，是構建函數constructor
 構造函數是專門用來創建物件的函數
使用typeof檢查一個物件時，會返回object
``` =javascript
var obj = new Object();  //調用一個Object方法
console.log(obj);  //{}
console.log(typeof obj);  //object
```
####  添加屬性
語法:物件.屬性名 = 屬性值
```=javascript
obj.name = "孫悟空";
obj.gender = "男";
obj.age = 18;
console.log(obj.gender);
```
####  讀取對象
語法：物件.屬性名
``` =javascript
console.log(obj.gender);
console.log(obj.hello);   //沒有的屬性，返回undefined
```
####  修改物件的屬性值
語法：物件.屬性名 = 新值
``` =javascript
obj.name = "tom";
```
####  刪除物件的屬性
語法:delete 物件.屬性名
``` =javascript
delete obj.name;
console.log(obj.age);
```
####  向物件中添加屬性
屬性名:
  - 物件的屬性名不強制要求遵守標識符的規範(可以使用關鍵字和保留字，但還是建議遵守規範)
  ``` =javascript
  obj.var = "hello";
  console.log(obj.var);
  ```
  - 如果要使用特殊的屬性名，不能採用.的方式來操作
    需要使用另一種方式:
       語法: 物件["屬性名"] = 屬性值
``` =javascript
  obj["123"] = 789;
  obj["#&)UPWOEURP@#"] = 555;
  console.log(obj["123"]);
```

使用[]這種形式去操作形式，更加的靈活，
在[]中可以直接傳遞一個變量，這樣變量值是多少就會讀取哪個屬性
``` =javascript
  var n = "nihao";
  console.log(obj[n]);
  ```
 ####  in 運算符
  通過該運算符可以檢查一個是否由指定的屬性
      如果有則返回true，沒有則返回false
  語法:
      "屬性名" in 物件
``` =javascript
  //檢查obj中是否含有test2屬性
  console.log("test2" in obj);
  ```
###  傳值跟傳址
####  傳值
JS的變量都是保存到棧內存中，基本數據類型的值直接在棧內存中存儲。
值與值之間是獨立存在的，修改一個變量不會影響其他變量的。
``` =javascript
var a = 123;
var b = a;
a ++; //a是123，a++是124
console.log("a = "+a);  //124
console.log("b = "+b);  //123
```
####  傳址
物件是保存到堆內存中，每創建一個新的物件，就會在堆內存中開闢出一個新的空間。
而變量保存的是物件的新地址（對象的引用），如果兩個變量保存的是同一個物件引用。
當一個通過一個變量修改屬性時，另一個也會受到影響。
``` =javascript
var obj = new Object();
obj.name = "孫悟空";
var obj2 = obj;

console.log(obj.name);
console.log(obj2.name); //孫悟空
```
當比較兩個基本數據類型的值時，就是比較值。
而比較兩個引用數據類型時，它是比較的對象的內存地址。
如果兩個物件是一模一樣的，但是地址不同，他也會返回false
``` =javascript
var obj3 = new Object();
var obj4 = new Object();
obj3.name = 'shs';
obj4.name = 'shs';
console.log(obj3 == obj4); //false
```
###  物件字面量
屬性名和屬性值是一組一組的名值對結構，
名和值之間使用:連接，多個名值對之間用,隔開
如果一個屬性之後沒有其他的屬性，就不要寫,
``` =javascript
var obj2 = {
   name:"豬八戒",
   age:28,
   gender:"男",
   test:{name:"沙和尚"}
}
console.log(obj2.test);
```
### 函式
1. 函數也是一個物件
2. 函數中可以封裝一些功能（代碼），在需要時可以執行這些功能（代碼）
3. 函數中可以保存一些代碼在需要時候調用
4. 使用typeof檢查一個函數物件時，會返回function
####  創建一個物件
可以將要封裝的代碼以字串形式傳遞給構造函數
``` =javascript
var fun = new Function("console.log('hello 這就是一個構造函數')");
```
封裝到函數中的代碼不會立即執行
函數中的代碼會在函數調用的時候執行
調用函數  語法:  函數對象()
當調用函數時，函數中封裝的代碼會按照順序執行
``` =javascript
fun();
```
使用函數聲明來創建一個函數
  語法:
      function 函數名（[形參1,形參2,形參3]）{
            語句...
      }
``` =javascript
//函數表達式
function fun2(){
   console.log("這是我的第二個函數");
 }
console.log(fun2);

//函數陳述式
var fun3 = function(){
  console.log("我是匿名函數中封裝的代碼");
}

fun3();
```
###  定義一個用來求兩個數的函數
可以在函數的()中指定一個或多個形參(形式參數)
多個形參之間使用,隔開，聲明形參就相當於**在函數內部聲明了對應的變量，但是並不賦值**。
``` =javascript
function sum(a, b){
   console.log(a+b);
}
```
在調用函數時，可以在()中指定時(實際參數)
實參將會賦值給函數中對應的形參
``` =javascript
sum(1,2);
```
算術運算符不會改變原值！！！！！（很重要） 
  - 運算符左右沒字串 -->轉數字再行運算（任何值跟NaN相加都是NaN）
  - 運算符左右有字串 -->轉字串再行拼接
調用函數時解析器不會檢查實參的類型，所以要注意，是否有可能會接收到非法的參數
``` =javascript
sum(123,"hello"); //"123hello"
sum(true, false); //1
```
調用函數時，解析器也不會檢查實參的數量
   多餘實參不會被賦值
如果實參數量少於形參，則沒有對應實參的形參將是undefined
``` =javascript
sum(123,456,"hello", true, null); //579
sum(123);  //NaN
```
###  Arguments
在調用函數時，瀏覽器每次都會傳遞這兩個隱含的參數:
- 函數的上下文對象this
- 封裝實參的對象arguments
    - arguments是一個**類陣列**對象，它也可以通過索引來操作數據，也可以獲取長度
    - 在調用函數時，我們所傳遞的實參都會在arguments中保存
    - arguments的長度就是我們的實參數量
    - 它裡邊有一個屬性叫做callee，
          這個屬性對應一個函數對象，就是當前正在指向的函數對象
``` =javascript
function fun(){
   console.log(arguments);
}

fun(); //[object Arguments]
```
####  檢查是否為陣列的方法
``` =javascript
1.方法一:instanceof
function fun(){
   console.log(arguments instanceof Array);
}
fun(); //false

2.方法二:isArray()
function fun(){
   console.log(Array.isArray(arguments));
}
fun(); //false
```
####  arguments的長度就是我們的實參數量
``` =javascript
function fun(){
   console.log(arguments.length);
}
fun();  //0


function fun(){
   console.log(arguments.length);
}
fun("hello",true); //2
```
####  如何取出arguments的值？
``` =javascript
function fun(){
   console.log(arguments[0]);
}
fun("hello",true); //"hello"


function fun(a, b){
   console.log(arguments.callee == fun); //就是當前正在指向的函數
}
fun(); //true
```
###  函數的返回值
``` =javascript
可以使用return來設置函數的返回值
   語法:
       return 值
   return後的值將會作為函數的執行結果返回
function sum(a, b, c){
   var d = a + b + c;
   return d;
   //在函數中return後不跟任何值就相當於回undefined
   return ;
}

調用函數
創建變量接收函數執行結果
函數返回結果及變量接收值
sum(4,7,8);

var result = alert("hello");
console.log("result = "+result);  //"result = undefined" 


//實參可以是任何值
1.定義一個函數，判斷一個數字是否是偶數，如果是返回true，否則返回false
function isOdd(num){
   return num%2 ==0; 
  寬鬆相等運算 ==
  當使用==比較時，如果類型不同，會先轉換成同類型再比較
}
var result = isOdd(3);
console.log("result ="+result);  //"result = false"

2.定義一個函數，可以根據半徑計算一個圓的面積（pi*r^2)，並返回計算結果
function area(r){
   return 3.14*r*r;
}
result = area(10);
console.log("result = "+result);   //"result = 314"
//實參可以是物件

3.創建一個函數，可以在控制台中輸出一個人的信息
  可以輸出人的name age gender address
function sayHello(name, age, gender, address){
    console.log("我是${name}，今年我${age}了，我是一個${gender}")
}
sayHello("豬八戒",28,"男","高老莊");

<問題>形參多了，就容易在輸入實參時順序出問題，該如何改善這個問題？
當我們的參數過多時，可以將參數封裝到一個物件中，然後通過參數傳達
var obj = {
  name:"孫悟空",
  age:18,
  gender:"男",
  address:"花果山"
};
function sayHello(o){
    console.log("我是${o.name}，今年我${o.age}了，我是一個${o.gender}");
}
sayHello(obj);

4.函數也可以作為一個實參
function fun(a){
  console.log("a = "+a);
}
fun(function(){alert("hello")}); //將匿名函式作為參數傳遞給另一個函式

5.函式名 vs 函式名()
函式名()
- 調用函數
- 相當於使用的函數的返回值
函式名
- 函數對象
- 相當於直接使用函數對象
```
###  立即執行函數
函數定義完，立即被調用，這種函數叫做立即執行函數
立即執行函數往往只會執行一次
``` =javascript
(function(){
   alert("我是一個匿名函數");
})();


(function(a, b){  //注意：匿名函數不可單獨存在於()外，會報錯
   console.log("a = "+a);
   console.log("b = "+b);
 })(123,456); //立即執行函數可以傳參
```
###  方法
``` =javascript
//創建一個物件
var obj = new Object();

//向物件添加屬性
obj.name = "孫悟空";
obj.age = 18;

//物件的屬性值可以是任何數據類型，包括函數
obj.sayName = function(){
   console.log(obj.name);  
}
function fun(){
   console.log(obj.name);  
}

console.log(obj.sayName);
//方法和函數只是名稱上的差別，本質上沒有差異
obj.sayName();  //調用方法
fun(); //調用函數

//物件中的函數屬性稱為方法
//同理，
console.log();  //調用console的方法
document.write();  //調用document的方法
```
###  枚舉物件中的屬性
在開發過稱中，常常會有拿到一個物件，但不知道內部有哪些屬性的時候？
一般會透過枚舉物件中屬性，得知物件長相
``` =javascript
var obj = {
  name:"孫悟空",
  age:18,
  gender:"男",
  address:"花果山"
}

如何枚舉？  使用for...in語句
語法：
    for(var 變量 in 物件){}


for(var n in obj){
    console.log("hello");  //打印四次，因為obj中有四個屬性
}

//for...in語句  物件中有幾個屬性，循環體就會執行幾次 （很重要）
  每次執行時，會將物件中的一個屬性名字賦值給變量
     換言之，以此為例，第一個n的值為name，第二個n的值為age，以此類推。
     
for(var n in obj){
    console.log(n);  //打印所有屬性名
    console.log(obj[n]);  //打印所有屬性值
}
```
###  全局作用域
作用域指一個變量的作用範圍
在JS中一共有兩種作用域:
  - 全局作用域
    - 在script標籤之間的代碼都是全局變數
    - 全局作用域在頁面打開時創建，在頁面關閉時消滅
    - 全局作用域創建時，自動生成一個全局物件window
    - 基本上全局變量是window的屬性，函式則為window的方法
    - 抬升(hoisting)分兩部分討論，變數和函式：
      - 變數
        - 變數使用var宣告時，會在所有程式被執行之前被聲明（但不會賦值）
          但是如果聲明變量不適用var關鍵字，則變量不會被提前聲明
    ``` =javascript
         console.log(a); //undefined
         var a = 10;  
         console.log(a); //10
         console.log(b); //b is not defined
         b = 15;   //b沒有宣吿，這裡b形同對window添加屬性（window.b）
         console.log(b); //15
    ```
      - 函式
        - 使用函數聲明形式創建的函數function函數(){}
          它會在所有代碼執行之前被創建
        ```=javascript
        fun1();  //I'm fun1!
        fun2();  //undefined
        
        function fun1(){  //會被提前創建
            console.log("I'm fun1!");
        }
        
        var fun2 = function(){  //不會被提前創建
            console.log("I'm fun2!");
        }
        ```
 - 函數作用域
   - 調用函數時創建函數作用域，函數執行完畢後，函數作用域銷毀
   - 每調用一次函數就會創建一次新的函數作用域，彼此之間獨立
   - 在函數作用域中可以訪問到全局作用域的變數（全局作用域中的變量都能被訪問到）
     但是全局作用域中無法訪問到函數作用域的變量
   - 當在函數作用域操作一個變量時，他會先在自身作用域尋找，如果有就使用，若無則向外查找。
   簡單來說，作用域的方向是由內向外可以不斷查找，但是無法由外向內
   - 函數作用域的抬生(hoisting)特性與全局作用域一樣
   ``` =javascript
   var a = "我是全局作用域的變數";
   function fun(){
     console.log(a);  //我是全局作用域的變數
   }
   fun();
   
   
   var a = "我是全局作用域的變數";
   function fun(){
     var a = "我是函數作用域的變數";
     console.log(a);  //我是函數作用域的變數
   }
   fun();
   
   
   var a = "我是全局作用域的變數";
    function fun(){
     var a = "我是函數作用域的變數";
     console.log(a);  //我是函數作用域的變數
     function fun2(){
        console.log(a);  //我是函數作用域的變數
     }
     fun2();
   }
   fun();
   
   
   <重要>
    var c = 33;
    //在函數中，不使用var聲明的變數都會變成全局變數
    function fun5(){
        console.log(c); //33         
        //c=10因為沒有使用var所以不會抬生，這時會向外查找
        c = 10;  //因為沒有使用var，所以這是個全局變數，所以它把var c = 33的值代換成10
    }
    fun5();
    console.log(c); //10
    
    
    var e = 10;
    function fun6(e){
       console.log(e);  //undefined
    }
    fun6();
    console.log(e); //10
   ```
###  作用域練習
說出以下代碼的執行結果
``` =javascript
var a = 123;
function fun(){
   alert(a);  //123
}
fun();



var a = 123;
function fun(){
   alert(a);  //undefined
   var a = 456;
}
fun();
alert(a);  //123


var a = 123;
function fun(){
   alert(a); //123
   a = 456;
}
fun();
alert(a); //456



var a = 123;
function fun(a){
   alert(a); //undefined
   a = 456; //賦值給形參
}
fun();
alert(a); //123



var a = 123;
function fun(a){
   alert(a); //123
   a = 456; 
}
fun(123);
alert(a); //123
```
###  執行上下文
- 全局執行上下文
    -  在執行全局代碼前將window確定為全局執行上下文
    -  對全局數據進行預處理
          - var定義的全局變量==>undefined，添加為window的屬性
          - function聲明的全局變數==>賦值(fun)，添加為window的方法
          - this ==>賦值(window)
          - 開始執行全局代碼
- 函數執行上下文
    - 在調用函數，準備執行函數體之前，創建對應的函數執行上下文對象
    - 對局部數據進行預處理
      - 形參變量==>賦值(實參)==>添加為執行上下文的屬性
      - arguments==>賦值(實參列表)，添加為執行上下文的屬性
      - var定義的局部變量==>undefined，添加為執行上下文的屬性
      - function聲明的函數==>賦值(fun)，添加為執行上下文的方法
      - this ==>賦值(調用函數的對象)
    - 開始執行函數體代碼
``` =javascript
function fn(a1){
   console.log(a1); //2
   console.log(a2); //undefined
   a3(); //a3()
   console.log(this); //window
   console.log(arguments); //[2,3]
   var a2 =3; 
   function a3(){
     console.log('a3()');
   }
}
fn(2,3);



var a = 10;
var bar = function(x){
   var b = 5;
   foo(x + b);
}
var foo = function(y){
   var c = 5;
   console.log(a + c + y);
}
bar(10);
// 
x=10 ==> foo(15) ==> y=15 ==> 10+5+15 =30



1.依次輸出什麼？
2.整個過程中產生了幾個執行上下文？ 5 // f1,f2,f3,f4,window
console.log("global begin:"+i); 
var i = 1;
foo(1); 
 
function foo(i){
  if(i == 4){
     return;
  }
  console.log("foo() begin:"+i);
  foo(i+1);
  console.log("foo() end:"+i);
}
console.log("global end:"+i);
// global begin:undefined
// foo() begin:1
// foo() begin:2
// foo() begin:3
// foo() end:3
// foo() end:2
// foo() end:1
// global end:1
```
###  this
每次調用函數時，都會向函數內部傳進一個隱含參數this
this指向一個物件，這個物件叫做函數執行的上下文物件
根據調用方式的不同，this所指向的對象也不同
``` =javascript
var name = "全局";
function fun(){
  console.log(this);
}
fun(); //[object Window]
obj.callName(); //[object Object]
```
``` =javascript
var name = "全局";
function fun(){
  console.log(this.name);  //this使用時機：當許多地方出現相同的變數名稱時
}

var obj = {
 name:"孫悟空",
 age:18,
 callName:fun
}

var obj2 = {
 name:"豬八戒",
 age:28,
 callName:fun
}

console.log(fun == obj.callName); //true
fun(); //"全局"
obj.callName(); //"孫悟空"
obj2.callName(); //"豬八戒"
```
###  使用工廠函數創建物件
創造大量屬性相同的物件，但是工廠函數無法區分類別
``` =javascript
//創建物件
var obj = {
   name:"孫悟空",
   gender:"男",
   age:18,
   sayName:function(){
      alert(this.name);
   }
}
```
<觀念>看見重複的代碼時，可以將其提取出來放入函式中
<問題>當要創建大量的物件時，該如何做？
     可以使用工廠方法創建物件（且更具擴充性）
``` =javascript
function createPerson(name, gender, age){
   var obj = new Object();
   obj.name = name;
   obj.gender = gender;
   obj.age = age;
   obj.sayName = function(){
      alert(this.name);
   };
   return obj;
}

function createDog(name, age){
 var obj = new Object();
 obj.name = name;
 obj.age = age;
 obj.bark = function(){
   console.log("旺旺");
  }
 return obj;
}

使用工廠方法創建的物件，使用構造函數都是Object
  所以創建的物件都是Object這個類型，
  就導致我們無法區分出多種不同類型的物件-->使用構造函數則可區分
console.log(createPerson("孫悟空","男",18));
console.log(createPerson("豬八戒","男",28));
console.log(createPerson("蜘蛛精","女",40));
var dog = createDog("旺財",3);
console.log(dog);
```
###  構造函數
創造大量屬性相同的物件，但是可區分類別
構造函數和普通函數創建方式相同，只不過函數名首字母大寫
構造函數和普通函數最大的不同在於調用方式的不同:
   普通函數是直接調用，而構造函數需要使用new關鍵字來調用(important!!!)
   構造函數的函數名作為創建物件的類別名（這就是構造函數和工廠函數的差異）
構造函數的執行流程:
   1. 立刻創建一個物件
   2. 將新建的物件設置為函數中的this，在構造函數中可以使用this來引用新建的對象
   3. 逐行執行函數中的代碼
   4. 將新建的物件作為返回值返回
``` =javascript
function Person(){

}
var per = new Person();
console.log(per); //[object Object]   以函式創建一個物件
```
``` =javascript
function Person(name, gender, age){
   this.name = name;
   this.gender = gender;
   this.age = age;
   this.sayName = function(){
      alert(this.name);
   };
}
function Dog(name, age){

}
var dog = new Dog();
var per = new Person("孫悟空","男",18);
console.log(per); //Person{name:"孫悟空",gender:"男",age:18,...}
console.log(per instanceof Person); //true 檢測該實例是否屬於該類
console.log(dog instanceof Person); //false
```
在上面例子的構造函數中，我們為每一個物件添加了一個方法，這是在構造函數內部創建的。
換句話說，每調用一次就會為實例創建一個新的方法，如果執行10000次就會創建10000個不同的方法
``` =javascript
function Person(name, gender, age){
   this.name = name;
   this.gender = gender;
   this.age = age;
   this.sayName = function(){
      alert(this.name);
   };
}
var per = new Person("孫悟空","男",18);
var per1 = new Person("豬八戒","男",28);
console.log(per.sayName == per1.sayName); //false
```
但其實所有實例調用的都是同一個方法，沒必要為每個實例分別創建。另一方面，性能也會因此下降，所以為了提升性能，較好的作法就是將方法提到構造函數外當作函數。
``` =javascript
function Person(name, gender, age){
   this.name = name;
   this.gender = gender;
   this.age = age;
   this.sayName = fun;
}
```
但是將函數提出來又產生了另一個問題:在全作用域下創建函數，污染了全局作用域的命名空間
另外在全局作用域中命名也沒有資安保障
因此接下來談原型
``` =javascript
function fun(){
      alert(this.name);
}

var per = new Person("孫悟空","男",18);
var per1 = new Person("豬八戒","男",28);
console.log(per.sayName == per1.sayName); //true
```
###  原型物件
構造函數中共有的內容就會設置到原型中，既可提升性能，又不會污染公共區域。

我們所創建的每一個函數，解析器都會向函數中添加一個屬性prototype
  這個屬性對應一個物件，這個物件就是原型物件
**-->我的理解：prototype是函數內部特有的(顯式原型)**

function Fn(){//內部語句：this.prototype = {}}
如果函數作為普通函數，prototype就沒有用
函數式作為構造函數，則其所創建實例都有一個隱含屬性，指向該構造函數的原型對象（很重要）
**-->我的理解：實例可以通過proto＿＿去訪問構造函數的原型對象（隱式原型）**

var fn = new Fn(); //內部語句：this.__proto__  = Fn.prototype;
原型就相當於一個公共的區域，所有同類的實例都可以訪問這個原型對象，
    我們可以將物件中共有的內容，統一設置到原型物件中。

當我們訪問物件的一個屬性或方法時，它會先在物件自身中尋找，如果有則直接使用，
    如果沒有則會去原型物件中尋找，如果找到則直接使用。
**-->我的理解：作用域方向是由內向外可以查找，這裡則是向上查找**
``` =javascript
function MyClass(){
 
}
MyClass.prototype.a = 123;
MyClass.prototype.sayHello = function(){
    alert("hello");
};
var mc = new MyClass(); //實例
console.log(mc.a); //123
```
``` =javascript
function Person(name, gender, age){
   this.name = name;
   this.gender = gender;
   this.age = age;
}

//改良成原型寫法
Person.prototype.sayName = function(){
      alert(this.name);
}
var per = new Person("孫悟空","男",18);
var per1 = new Person("豬八戒","男",28);

<補充>函數的prototype屬性:
  1.每個函數都有一個prototype屬性，他默認指向一個Object空對象（原型對象）
  2.原型對象中都有一個contructor屬性，他指向函數對象
  3.所有函數都是Function的實例（包含function）
    每個創建的函式都是一個Function(函式)構造函式的實例，
    所以每個我們定義的函式都有__proto__以及一個身為函式會有的prototype

console.log(Date.prototype, typeof Date.prototype);
function fun(){
}
fun.prototype.test = function(){
  console.log("test()");
}
console.log(fun.prototype);
```
###  hasOwnProperty()介紹
只希望查看該實例中存有的屬性(不包含其原型屬性)，使用hasOwnProperty()
``` =javascript
function MyClass(){
}

MyClass.prototype.name = "原型中的name";

var mc = new MyClass();
console.log(mc.name); //"原型中的name"   因為本身沒有，會向上查找

//使用in檢查物件中是否有該屬性，注意：如果該物件沒有但原型有，依然返回true
console.log("name" in mc); //true  因為源興中有這屬性而不是因為實例本身有

如果只希望查看該實例中存有的屬性(不包含其原型屬性)，就要使用hasOwnProperty()
console.log(mc.hasOwnProperty("name"));  //false
```
###  console.log()的本質
``` =javascript
function Person(name, age, gender){
   this.name = name;
   this.age = age;
   this.gender = gender;
}
var per = new Person("孫悟空",18,"男")

console.log(per); //[object Object]

<問題>為什麼返回的是[object Object]？
因為當我們直接在頁面中打印一個對象時，實際上是輸出的對象的toString()方法的返回值

var result = per.toString();
console.log(result);  //[object Object]

//查找toString方法在哪一層？
console.log(per.hasOwnProperty("toString"));  //false
console.log(per.__proto__.hasOwnProperty("toString")); //false
console.log(per.__proto__.__proto__.hasOwnProperty("toString"));  //true
//在Object中有toString方法

//如果不想打印出[object Object]，則為per實例添加屬性
per.toString = function(){
  return `Person[name=${this.name},age=${this.age},gender=${this.gender}]`;
}
//但是這樣的做法其他實例無法共有，若想大家共有則需要放在構建函數中。
Person.toString = function(){
  return `Person[name=${this.name},age=${this.age},gender=${this.gender}]`;
}
```
###  call和apply
call()和apply()
- 這兩個方法都是函數的方法，需要通過函數對象來調用
- 當對函數調用call()和apply()都會調用函數執行
- 在調用call()和apply()可以將一個對象指定為第一個參數
     此時這個對象將會成為函數執行時的this
- call()方法可以將實參在對象之後依次傳遞
- apply()方法需要將實參封裝到一個陣列中統一傳遞
- this的情況:
   1.以函數形式調用時，this永遠都是window
   2.以方法形式調用時，this是調用方法的對象
   3.以構造函數的形式調用時，this是新創建的實例
   4.使用call和apply調用時，this就是指定的那個對象
``` =javascript
function fun(){
   alert(this);
}
var obj = {};
fun(); //[object Window]
fun.call(obj); //[object Object]
fun.apply(obj); //[object Object]
```
``` =javascript
function fun(){
   alert(this.name);
}
var obj = {
  name:"obj"
};
var obj2 = {
  name:"obj2"
};
fun.call(obj); //"obj"  this變成obj
fun.call(obj2); //"obj2"  this變成obj2
fun.apply(obj);
```
``` =javascript
function fun(){
   alert(this.name);
}
var obj = {
  name:"obj", 
  sayName:function(){
    alert(this.name);
  }
};
var obj2 = {
  name:"obj2"
};
obj.sayName(); //"obj"
obj.sayName.apply(obj2); //"obj2" 指定this為obj2
```
####  call和apply的差別
``` =javascript
function fun(a, b){
    console.log("a = "+a);
    console.log("b = "+b);
}
fun.call(obj, 2, 3);  
// a = 2
   b = 3
fun.apply(obj,[2, 3]); //apply的參數要用陣列封裝（apply with array）
```
###  Date對象
在JS中使用Date對象來表示一個時間
``` =javascript
//創建一個Date對象
//如果直接使用構造函數創建一個Date對象，則會封裝為"""當前"""代碼執行的時間
var d = new Date(); //構造函數
console.log(d);

//創建一個指定的時間對象
//需要在構建函數中傳遞一個表示時間的"""字串"""作為參數
//日期的格式: 月份/日/年 時:分:秒
var d2 = new Date("12/03/2020 11:10:30"); //2020年12月3日 11點10分30秒
console.log(d2);

//利用時間戳來測試代碼的執行性能
//獲取當前的時間戳(單位是毫秒)
var time = Date.now();  
var start = Date.now();
for(let i=0;i<100;i++){
   console.log(i);
}
var end = Date.now();
console.log(`執行了，${end - start}毫秒`); //計算for循環執行的時間

<補充>時間函式的運用時機
1. json中時間判斷篩選資料
2.新增一筆資料時間紀錄
3.時鐘
 不同做法的時鐘（參考codepen、）
 電子鐘、復古鐘
 涉及setInterval、setTimeout…概念 →概念介紹
 運用：c.f 計時器、碼表
```
####  Date( )中常用的原型方法
![](https://i.imgur.com/xC7U8A1.png)
####  在Date( )中自定義方法
``` =javascript
Date.prototype.getFullDate = function(){
   let yyyy = this.getFullYear();
   let mm = String(this.getMonth() + 1);
   let dd = String(this.getDate())
   let nowString = yyyy + ' / '+ mm + ' / '+ dd;
   return nowString;
}
let now = new Date();
now.getFullDate(); '2020 / 11 / 10'
```
###  Math
Math和其他的物件不同，它不是一個構造函數，
    它屬於一個工具類不用創建對象，它裡邊封裝了數學運算相關的屬性和方法
``` =javascript
var m = new Math(); //[object Math]

//圓周率
console.log(Math.PI);

//絕對值abs = absolute
console.log(Math.abs(-1));  //1

//無條件進位 ceil
console.log(Math.ceil(1.5)); //2

//無條件捨棄 floor
console.log(Math.floor(1.1)); //1

//四捨五入  round
console.log(Math.round(1.4)); //1
console.log(Math.round(1.8)); //2

//亂數  random
//可以用來生成一個0-1之間(不含0和1)的隨機數
console.log(Math.random());
//生成一個0-10的隨機數
for(let i=0;i<100;i++){
   console.log(Math.random()*10);
}
```
``` =javascript
//如果想生成包含0和10的數，就透過四捨五入
for(let i=0;i<100;i++){
   console.log(Math.round(Math.random()*10));
}
//生成1-10
就是先生成0-9再加1，就變1-10
for(let i=0;i<100;i++){
 console.log(Math.round((Math.random()*9))+1);
}

//最大值 max
var max = Math.max(10,20,30);
console.log(max); //30

//最小值 min
var min = Math.min(10,45,30,100);
console.log(min); //10

//次方 pow
console.log(Math.pow(2,3));  //8

//開平方根  sqrt
console.log(Math.sqrt(9)); //3
```
###  垃圾回收
沒有參照的物件就稱為垃圾，所以JS的自動回收機制會將其銷毀
``` =javascript
var obj = new Object();
//各種使用後
obj = null;
//JS回收機制自動回收，提升內存空間
```
##  五、JS陣列
###  陣列簡介
回顧先前所提，物件分三種：1.內建對象2.宿主對象3.自定義對象。
只是自定義對象麻煩又困難，所以一般而言使用上還是以內建對象及宿主對象為主。

陣列就是內建對象中常見的對象。他也是用來存儲值，且存儲性能佳（最常用）。不同的是普通對象使用字串作為屬性名，陣列是使用數字作為索引值。陣列中的成員稱作元素
####  創建陣列
``` =javascript
方法一：使用構造函數創建時也可同時添加元素
var arr = new Array();
console.log(typeof arr);  //object
var arr3 = new Array(10,20,30); //這裡是元素

方法二：可以在創建同時添加元素
var arr = [];
var arr1 = [1,2,3,4,5,6];

<比較>
var arr3 = new Array(10,20,30); //這裡是創建並添加元素，返回[10,20,30]
var arr4 = new Array(10); //這裡是創建一個長度為10的陣列
var arr5 = [10]; //這裡是創建並添加元素，返回[10]
```
####  添加元素
語法:陣列[索引] = 值
``` =javascript
arr[0] = 10;
arr[1] = 33;
arr[2] = 22;
arr[10] = 8;
```
####  讀取元素
語法:陣列[索引]
``` =javascript
console.log(arr[0]); //10
console.log(arr[7]); //undefined  沒有該元素不會報錯，而是返回undefined
```
####  獲取陣列長度
注意：對於非連續陣列，使用length會獲取到陣列最大索引＋1
####  修改length
如果修改的length大於原長度，則多出部分會空出來
如果修改的length小於原長度，則多餘部分會刪除
``` =javascript
arr.length = 10
```
####  向陣列最後添加一元素
``` =javascript
arr[arr.length] = 70; //因為長度是最大索引值＋1
```
###  陣列的四個方法
####  1.push 
對陣列最後添加一個或多個元素，並返回新的陣列**長度**。
比起arr[arr.length]的方式更具彈性
``` =javascript
var arr = ["a","b","c"];
  arr.push("d"); //4
  arr.push("e","f","g");  //可以同時添加多個元素
  var newLen = arr.push("h"); //設置變數接收新長度
  console.log(newLen); //8
  console.log(arr);  //["a","b","c","d","e","f","g","h"]
```
####  2.pop 
刪除陣列最後一個元素，並返回刪除元素
``` =javascript
arr.pop();
```
####  3.unshift 
對陣列最前添加一個或多個元素，並返回新的陣列**長度**。
``` =javascript
arr.unshift("before a-1","before a-2");
```
####  4.shift 
刪除陣列第一個元素，並返回刪除元素
``` =javascript
arr.shift();
```
![](https://i.imgur.com/2Kl4mNj.png)
###  陣列的遍歷
####  1.使用for循環
``` =javascript
function Person(name, age){
  this.name = name;
  this.age = age;
}

var per = new Person("Al",8);
var per1 = new Person("Bill",17);
var per2 = new Person("Cody",18);
var per3 = new Person("Daniel",15);
var per4 = new Person("Eddie",58);

//創建一個陣列，將18歲以上的人提取出來放入新陣列返回
var perArr = [per, per1, per2, per3, per4];
function get18(array){   
  var newArr = [];
  for(let i=0; i<array.length; i ++){
      if(array[i].age>=18){
        newArr.push(array[i]);
      }
  }
  return newArr;
}
get18(perArr);
```
####  2.使用forEach
forEach()方法需要一個函數作為參數
  - 像這種函數，由我們創建但是不由我們調用，而是由瀏覽器調用，我們稱為回調函數
  - 陣列中有幾個元素就執行幾次，每次執行時，瀏覽器將會遍歷所有的元素以實參的形式傳遞進來，
    我們可以來定義形參，來讀取這些內容
  - 瀏覽器會在回調函數中傳遞三個參數，
       第一個參數，就是當前正在遍歷的元素
       第二個參數，就是當前正在遍歷的元素的索引
       第三個參數，就是當前正在遍歷的陣列
``` =javascript
perArr.forEach(function(element, index, array){
    console.log("hello");  //對每一個元素都執行一次
})
```
###  slice和splice
####  1.slice(start,end):從某個已有的陣列返回選定的元素（MDN定義）
- 可以用來從陣列提取指定元素
    - 該方法不會改變元素陣列，而是將擷取到的元素封裝到一個新陣列中返回
    - 參數:
        1. 擷取開始的位置的索引，包含開始索引
        2. 擷取結束的位置的索引，""不""包含結束索引(很重要！！！)        (optional)
               不設置結束索引時，預設選定到最後一個元素
        - 參數可以傳遞一個負值 -1代表最後一個元素的索引，-2到表倒數第二個元素的索引  以此類推
``` =javascript
var perArr = ["Alex", "Bill", "Cody", "Daniel", "Eddie","Flynn"];
var result = perArr.slice(1,4); // "Bill", "Cody", "Daniel", "Eddie"
perArr.slice(4); //"Eddie","Flynn"
perArr.slice(1,-1); // "Bill", "Cody", "Daniel", "Eddie","Flynn"
```
####  2.splice()  
- 刪除元素，並向陣列添加新元素
   - 會改變原陣列長相，會將指定元素從原陣列中刪除，並將被刪除的元素作為返回值返回
   - 參數:
        第一個，表示開始位置的索引
        第二個，表示刪除的數量
        第三個及以後，可以傳遞一些新的元素，
             這些元素將會自動插入到開始位置索引前
``` =javascript
var perArr = ["Alex", "Bill", "Cody", "Daniel", "Eddie","Flynn"];

//刪除該指定位置元素 arr.splice(index,1); [一個元素]
perArr.splice(1,2); //"Bill", "Cody"
console.log(perArr); //["Alex", "Daniel", "Eddie","Flynn"]

//替換該指定位置元素 arr.splice(index,1,"new element");
perArr.splice(1,1,"Bill","Bruce");

//在該指定位置新增元素 arr.splice(index,0,"new element2");
//對整個陣列而言的對應索引位置新增該元素(很重要！！！！！)
//在當前該索引位置前新增該元素
perArr.splice(1,0,"Brandson"); //[]  因為沒有刪除任何元素
console.log(perArr); //["Alex","Brandson", "Bill","Bruce", "Eddie","Flynn"]
perArr.splice(3,0,"before Bruce")
console.log(perArr);
```
###  陣列去重練習
創建一個有九個元素的陣列，當中有重複的數字，現在要把內部重複數字去除(使用for嵌套)
``` =javascript
var arr = [1,2,3,2,1,3,4,2,5];
//遍歷就陣列中每個元素
for(let i=0; i<arr.length; i++){  
   let base = arr[i];
   for(let j=i+1; j<arr.length;j++){
     if(base == arr[j]){
       arr.splice(j,1);
     }
   }
}
console.log(arr);
```
但是這麼做會產生一個問題，就是當陣列內重複兩次時，無法使splice過後的陣列保持不重複。
``` =javascript
var arr = [1,2,3,2,2,1,3,4,2,5];
for(let i=0; i<arr.length; i++){  
   let base = arr[i];
   for(let j=i+1; j<arr.length;j++){
     if(base == arr[j]){
       arr.splice(j,1);
       j--;
     }
   }
}
```
###  陣列的剩餘方法
目前會改變原陣列的有:四個方法、splice、reverse、sort
``` =javascript
var arr = ["孫悟空","豬八戒","沙和尚"];
var arr2 = ["白骨精","玉兔精","蜘蛛精"];
var arr3 = ["二郎神","太上老君","玉皇大帝"];
```
####  1.concat() 
- 連接兩個或多個的陣列，並返回結果(concat不會改變原陣列) 很重要！！
- 該方法不會對原陣列產生影響
``` =javascript
var result = arr.concat(arr2, arr3,"牛魔王","鐵扇公主"); //可連接多個陣列或元素
console.log(result);
```
####  2.join() 
  - 把陣列轉換成字串
  - 在join()中可以指定一個字串作為參數，這個字串將會成為陣列中元素的運算符（,是預設）
 ``` =javascript
 result = arr.join(); 
console.log(result);  //"孫悟空,豬八戒,沙和尚"
console.log(typeof result); //string
result = arr.join("hello");
console.log(result); //"孫悟空hello豬八戒hello沙和尚"
 ```
 ####  3.reverse() 
 - 陣列中元素的順序前後對換
 ``` =javascript
 arr.reverse(); //["沙和尚","豬八戒","孫悟空"]
 ```
 #### 4.sort() 
- 對陣列的元素進行排序
- 也會影響原陣列，默認會按照unicode編碼進行排序(所以對數字進行排序時可能會得到錯誤順序)
- 我們可以自己來指定排序的規則:（在sort裡面放一個回調）
     回調函數中需要定義兩個形參，
     瀏覽器將會分別使用陣列中的元素作為實參去調用回調函數
     使用哪個元素調用不確定，但是肯定的是在陣列中a一定在b前邊
- 瀏覽器會根據回調函數的返回值來決定元素的順序，
     如果返回一個大於0的值，則元素會交換位置
     如果返回一個小於0的值，則元素位置不變
     如果返回一個0，則認為兩個元素相等，也不交換位置
``` =javascript
arr = ["b","d","e","c","a"];
arr.sort(); //["a","b","c","d","e"]
arr = [3,4,11,2,5];
arr.sort(); //[11, 2, 3, 4, 5]  因為是用unicode排序
arr = [4,5];
arr.sort(function(a, b){
   console.log("a = "+a);
   console.log("b = "+b);
});
console.log(arr); 
//返回 a = 4
      b = 5
arr = [4,5,3];
arr.sort(function(a, b){
   console.log("a = "+a);
   console.log("b = "+b);
});
console.log(arr);
//返回 a = 4
      b = 3
      
      
//返回一個大於0的值(元素會交換位置)
arr = [5,4];
arr.sort(function(a, b){
  return 1;
})
//返回一個小於0的值(元素位置不變)
arr = [5,4];
arr.sort(function(a, b){
  return -1;
})
//返回一個等於0的值(元素位置不變)
arr = [5,4];
arr.sort(function(a, b){
  return 0;
})

arr = [5,4];
//希望升冪
arr.sort(function(a, b){
  if(a>b){
    return 1;
  }else if(a<b){
    return -1;
  }else{
    return 0;
  }
})
//改寫:升冪
arr = [5,4];
arr.sort(function(a, b){
  return a - b;
})
//改寫:降冪
arr = [5,4];
arr.sort(function(a, b){
  return b - a;
})
```
##  六、正則表達式
### 介紹
對**字串**的規則
計算機可以根據正則表達式正則表達式，來檢查一個字串是否符合規則
獲取將字串中符合規則的內容提取出來

創建正則表達式的物件
  語法：
      **var 變數 = new RegExp("正則表達式","匹配模式");** 
      前後兩個參數都要是**字串**
  在構造函數中可以傳遞一個匹配模式作為第二個參數，
  可以是
       i 忽略大小寫
       g 全局匹配模式  遇到只返回第一個匹配內容的方法時，可透過全局匹配**返回所有符合的值**
``` =javascript
var reg = new RegExp("a");    
console.log(reg); //   "/a/"
console.log(typeof reg); //object
```
``` =javascript
var reg = new RegExp("a");  //創建好規則:用來檢查字串裡是否有a
var str = "a";  //要檢查的字串
```
<問題>如何用正則表達式檢查字串？
test()
   - 使用這個方法可以用來檢查一個字串是否符合正則表達式的規則，
       如果符合則返回true，否則返回false
``` =javascript
var result = reg.test(str);
console.log(result); //true
console.log(reg.test("abc")); //true
console.log(reg.test("bcabc")); //true
console.log(reg.test("bcdef")); //false 因為沒有規則裡要包含的字母a
console.log(reg.test("Abc")); //false   嚴格區分大小寫
```
``` =javascript
var reg = new RegExp("a","i"); //忽略大小寫
console.log(reg.test("abc")); //true
console.log(reg.test("Abc")); //true
```
###  使用字面量創建正則表達式
語法:  var 變數 = /正則表達式/匹配模式   （注意：他們不是字串！！！！）[很重要]
``` =javascript
var reg = new RegExp("a","i");跟  var reg = /a/i;  等價
```
####  1.創建一個正則表達式，檢查一個字串中是否"""有a或b"""
``` =javascript
方法一:
var reg = /a|b/;   c.f:var reg = /ab/;(且的關係)

方法二:
var reg = /[ab]/;  []裡的內容也是或的關係
console.log(reg.test("abc")); //true
console.log(reg.test("cde")); //false
console.log(reg.test("ac")); //true
```
#### 2.創建一個正則表達式，檢查是否一個字串中是否有字母
``` =javascript
var reg = /[a-z]/;   //任意小寫字母
console.log(reg.test("cde")); //true
console.log(reg.test("1688888")); //false

var reg = /[A-Z]/;   //任意大寫字母
console.log(reg.test("A")); //true

var reg = /[A-z]/;   //任意字母，不區分大小寫
console.log(reg.test("A")); //true
console.log(reg.test("b")); //true
```
####  3.檢查一個字串中是否含有abc或adc或aec
``` =javascript
var reg = /a[bde]c/;
console.log(reg.test("adc")); //true
console.log(reg.test("azc")); //false
console.log(reg.test("aeec")); //false
```
####  4.[^ ] 除了 - 只要有ab以外的字符就返回true，即使仍有ab也無所謂
``` =javascript
var reg = /[^ab]/;
console.log(reg.test("c")); //true
console.log(reg.test("ab")); //false
console.log(reg.test("abc")); //true
```
####  5.創建一個正則表達式，檢查是否一個字串中是否有數字
``` =javascript
var reg = /[0-9]/;
console.log(reg.test("1688888")); //true
console.log(reg.test("q68a8f8")); //true
```
###  字串與正則表達式相關的方法
#### 1.search()
- 搜索字串中是否有指定內容
``` =javascript
var str = "hello abc hello abc";
var result = str.search("abc");
console.log(result); //6  是找到第一個指定內容的索引值
result = str.search("abcd");
console.log(result); //-1
result = str.search(/a[bef]c/);
console.log(result); //6
```
####  2.match()
   - 根據正則表達式，從一個字串中將符合條件的內容提取出來
   - 默認情況下match只會找到第一個內容，就停止以後的檢索
   - match()會將匹配到的內容封裝到一個陣列中返回，即使只有查詢到一個結果
``` =javascript
var str = "1a2b3c4d5e6f7";
result = str.match(/[A-z]/);
console.log(result); //"a"   默認情況下match只會找到第一個內容
result = str.match(/[A-z]/g);
console.log(result); //"a,b,c,d,e,f"
console.log(result[2]);  //"c"  因為返回的是陣列
```
####  3.replace() 
   - 只替換第一個
   - 可以將字串中指定內容替換為新內容
   - 參數：(都是字串)
       1.被替換的內容，可以接受一個正則表達式作為參數
       2.新內容
``` =javascript
result = str.replace("a","@_@"); 
console.log(result);  //"1@_@2b3c4d5e6f7"

str = "1a2a3aaa4d5e6f7";
console.log(result);  //"1@_@2a3aaa4d5e6f7"  只會替換第一個匹配部分

result = str.replace(/a/g,"@_@");
console.log(result);  //"1@_@2@_@3@_@@_@@_@4d5e6f7"
```
####  4.split()
   - 可以將一個字串拆分為一個陣列
   - 方法中可以傳遞一個正則表達式作為參數，這樣可根據正則表達式拆分字串
``` =javascript
var str = "1a2b3c4d5e6f7";
根據任意字母將字串拆分
var result = str.split(/[A-z]/);
console.log(result); //1,2,3,4,5,6,7
```
###  正則表達式語法：量詞
  - 通過量詞可以設置一個內容出現的次數
  - 量詞只對它前邊的一個內容起作用
  - {n}正好出現n次
  - {m,n} 出現m-n次
  - {m,} 出現m次以上
  - \+ 至少一個，相當於{1,}
  - \* 0或多個，相當於{0,} //有或沒有都沒差
  - ? 0或1個，相當於{0,1}
``` =javascript
var reg = /a{3}/;  //在{}內填入出現次數   a得連續出現3次

var reg = /ab{3}/; //abbb

var reg = /(ab){3}/; //ababab

var reg = /ab{3}c/; //abbbc

var reg = /b{3}/;  //bbbb  true

var reg = /ab{1,3}c/;  
//abc  true;  //abbc  true;  //abbbc true;  //abbbbc false

var reg = /ab{3, }c/;  //至少3個以上的b

var reg = /ab+c/;  //ac  false;    //abc  true;
```
####  創建一個正則表達式檢查一個字串是否含有aaa
``` =javascript
var reg = /a{3}/;  //必須有三個緊鄰的a字符
console.log(reg.test("abc")); //false
console.log(reg.test("aaabc")); //true
console.log(reg.test("aabac")); //false
```
####   以a字符開頭，然後立刻結束
``` =javascript
var reg = /^a$/;   
console.log(reg.test("aaa")); //false
```
####    以a字符開頭，或以a字符結尾
``` =javascript
var reg = /^a｜a$/;   
console.log(reg.test("aaa")); //true
```
###  正則表達式語法：加轉譯字符
``` =javascript
var reg = /\./;
reg = /\\/;  //本身就代表\\
console.log(reg.test("b."));  //true
console.log(reg.test("\\."));  //true
```
注意：使用構造函數時，由於它的參數是一個字串，而\是字串中轉譯字符，
     如果要使用\則需要使用\\來代替
reg = new RegExp("\.");   //如果要出現\則需要使用\\來代替

\w - 任意字母、數字、_   [A-z0-9_]
\W - 除了(大寫)任意字母、數字、_   [＾A-z0-9_]
\d - 任意數字   [0-9]
\D - 除了任意數字  [^0-9]
\s - 空格
\S - 除了空格
\b - 單詞邊界  //單詞child就是獨立存在，不與其他字母緊鄰（避免與"包含"概念混淆）
\B - 除了單詞邊界
####  創建一個正則表達式檢查一個字串中是否有單詞child
``` =javascript
reg = /child/;
console.log(reg.test("hello children")); //true

reg = /\bchild\b/;
console.log(reg.test("hello child ren")); //true
console.log(reg.test("hello children")); //false
```
####  接收一個用戶輸入的姓名
``` =javascript
var name = prompt("請輸入你的用戶名:");

//假如你在輸入前邊多打了空格
var name = "       username        ";

<問題>該如何去除掉字串中的空格？
就是用""(空串)去替換空格
name = name.replace(/\s/g,"");  //注意：不用全局，只會替換一個空格
//這樣會引發另一個問題，就是把包括輸入中間無誤的空格也一同去掉

<修改>如何只將開頭跟結尾的空格去掉？
1.去除開頭的空格
name = name.replace(/^\s*/,""); //發現全局匹配無效，因為只有開頭第一個空格才算開頭
2.去除結尾的空格
name = name.replace(/\s*＄/,""); //發現全局匹配無效，因為只有結尾最後一個個空格才算結尾
3.合併
name = name.replace(/^\s*|\s*＄/g,""); //一定全局匹配，否則去除第一個空格就將結果返回
console.log(name);
```
###  正則表達式練習
####  1.創建一個正則表達式，用來檢查一個字串是否是一個手機
09xx-xxx-xxx  (10位09開頭(後面8位)的全數字)
``` =javascript
var reg = /^09[0-9]{8}$/;  //加^與＄必須完全匹配
var str = "0938713200";
var result = reg.test(str);
console.log(result);
```
####  2.郵件的正則
<老師的規則>
任意數字字母下划線.任意數字字母下划線@任意字母數字.任意字母(2-5位).任意字母(2-5位)
``` =javascript
var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;
var email = "abc@abc.com.123";
console.log(emailReg.test(email));
```
##  七、宿主對象
###  什麼是DOM?
Dom（全稱Document Object Model文檔對象模型）
- 透過DOM對html進行操作
<問題>何謂文檔對象模型？
1.文檔:整個的html網頁文檔
2.對象:把網頁中的每一個部分都轉換為物件
3.模型:使用模型來表示物件間的關係，這樣方便我們獲取物件。
![](https://i.imgur.com/DQoVo7l.png)
###  節點(Node)
1. 節點是構成我們**網頁最基本的組成部分**，網頁中的每一個部分都可以稱為一個節點。比如:html標籤、屬性、文本、注釋、整個文檔等都是一個節點。
2. 雖然都是節點，但是實際上他們的具體類型是不同的。
3. 節點的類型不同，屬性和方法也不盡相同。
4. 常用節點分為四類：
   文檔節點：整個html文檔
   元素節點：html文檔中的html標籤
   屬性節點：元素的屬性
   文本節點：html標籤中的文本內容
###  DOM操作
####  DOM查詢
``` =javascript
1.getElementsByTagName()
   - 通過標籤名獲取多個元素節點對象
   
2.getElementsByName()
   - 通過name屬性獲取多個元素節點對象
     比如:form表單元素中radio,checkbox....的name屬性
     
3.獲取body
var body = document.body;  //document中有一個body屬性，它保存的是body的引用

4.獲取html根標籤
var html = document.documentElement;
console.log(html); //[object HTMLHtmlElement]

5.獲取document.all代表頁面中所有的元素
var all = document.all;
console.log(all.length);
for(let i=0;i<all.length; i++){
   console.log(all[i]);
}

6.獲取class為box1中的所有的div
  .box1 div
  document.querySelector(".box1 div"); //使用querySelector可以複合式選取
  
7.獲取文本節點
var fc = li.firstChild;
console.log(fc.nodeValue);   //取文本節點的值（文字）
```
####  DOM新增
``` =javascript
1. createElement() 
    - 用於創建一個"""元素"""節點對象，
      他需要一個標籤名作為參數，將會根據該標籤名創建元素節點對象，
      並將創建好的對象作為返回值返回
   var li = document.createElement("li");
   
2. createTextNode()
    - 創建"""文本"""節點對象，需要一個文本內容作為參數，將會根據該內容創建文本節點，
      並將新的文本節點返回
   var text = document.createTextNode("這裡是放文本內容");
   
//這裡的多個被查找元素都是以[html collection]類陣列形式返回
//innerHTML用於獲取元素"""內部"""html代碼，對於自結束標籤，這個屬性沒有意義
//如果需要讀取該元素節點屬性，直接使用元素.屬性名
     比如:元素.id, 元素.name, 元素.value
```
###  子節點操作
注意空白會被當作文本節點
####  查詢子節點
``` =javascript
1.childNodes 
  - 表示當前節點的所有子"""節點"""(會獲取包含文本節點)[很重要！！]
  - 根據DOM標籤間空白也會當成文本節點[很重要！！]
  c.f children屬性  
     - 可以獲取當前元素的所有子"""元素"""
     - 就不會包括空白！！！！（推薦使用）
     
2.firstChild - 表示當前節點的第一個子節點  
  c.f firstElementChild屬性  
     - 可以獲取當前元素的第一個子"""元素"""
     - 就不會包括空白！！！！（推薦使用）
     
3.lastChild - 表示當前節點的最後一個子節點
  c.f lastElementChild屬性  
     - 可以獲取當前元素的最後一個子"""元素"""
     - 就不會包括空白！！！！（推薦使用）
```
####  新增子節點
``` =javascript
1.父節點.appendChild(子節點) - 把新的子節點添加到指定節點

2.父節點.insertBefore(新子節點,舊子節點) - 在指定的子節點前面插入新的子節點
```
####  刪除子節點
``` =javascript
removeChild() - 刪除子節點
<運用> 子節點.parentNode.removeChild(子節點)
```
####  修改子節點
``` =javascript
父節點.replaceChild(新子節點,舊子節點) - 替換子節點
```
###  父節點和兄弟節點的操作
注意空白會被當作文本節點
####  查詢父節點和兄弟節點
``` =javascript
1.parentNode (c.f:子元素，這裡是沒有s，因為一次只有一個父元素)
  - 表示當前節點的所有子"""節點"""(會獲取包含文本節點)[很重要！！]
  - 根據DOM標籤間空白也會當成文本節點[很重要！！]
  c.f children屬性  
     - 可以獲取當前元素的所有子"""元素"""
     - 就不會包括空白！！！！（推薦使用）
     
2.previousSibling - 表示當前節點的前一個兄弟節點  
  c.f previousElementSibling屬性  
     - 可以獲取當前元素的前一個兄弟"""元素"""
     - 就不會包括空白！！！！（推薦使用）
     
3.nextSibling - 表示當前節點的後一個兄弟節點
  c.f nextElementSibling屬性  
     - 可以獲取當前元素的最後一個兄弟"""元素"""
     - 就不會包括空白！！！！（推薦使用）
```
###  操縱子節點CSS樣式
1.通過style屬性設置和讀取的都是內聯樣式，**無法**讀取樣式表中的樣式
- 設置元素的樣式
     語法: 元素.style.樣式名
- 獲取元素的當前顯示的樣式(唯讀)
     語法: getComputedStyle()  
  - 這個方法是window的方法，可以直接使用
  - 需要兩個參數
       第一個，要獲取樣式的元素
       第二個，可以傳遞一個偽元素，一般都傳null
  - 該方法返回一個物件，物件中封裝了當前元素對應的樣式
  - 如果獲取的樣式沒有設置，則會獲取到真實的值，而**不是默認值**
    比如：沒有width，它就不會獲取到auto，而是一個長度
``` =javascript
var obj = getComputedStyle(box1, null);
    console.log(obj.width);
   function getStyle(obj, name){
        return getComputedStyle(obj, null)[name]; //不可用.因為name是""變量""
   }
   alert(getStyle(box1, width));
```
###  什麼是事件？
1.事件，就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。
2.JS和html之間的交互是通過事件實現的。
3.代表性事件:點擊、移動鼠標、按下鍵盤鍵。（DOM event）
4.注意script標籤下的地方，必須在body內容加載完後加入，
   否則瀏覽器不認識該物件會報錯 （很重要！！）
5.常用事件介紹:
  - onclick
  - onload - 會在整個頁面加載完之後才觸發
   （根據文檔，image,layer跟window支持）
    為window綁定一個onload事件
``` =javascript
    window.onload = function(){
         alert("hello");
    }
```
onload也為了避免第4點想避免的錯誤，可以因應的做法。
但是以性能觀點而言，沒有必要。因為等於先跑完script內的內容，
    才執行body的內容，使頁面加載變慢（一般來說不會使用）
###  什麼是BOM?
瀏覽器對象模型
BOM可以使我們通過JS來操作瀏覽器   c.f DOM操作網頁，BOM操作瀏覽器
1.在BOM中為我們提供了一組物件，用來完成對瀏覽器的操作
2.BOM對象有:
   - Window
      - 代表的是整個瀏覽器的窗口，同時window也是網頁中的全局對象
   - Navigator
      - 代表的當前瀏覽器的信息，通過對象可以來識別**不同的瀏覽器**
      - appName (時代的產物，現在不太使用)
          - 返回瀏覽器名稱
           （比如:safari,Microsoft Internet Explorer(IE),
             netscape(火狐瀏覽器,chrome)…）
      - userAgent是一個字串，這個字串中包含有用來描述瀏覽器信息的內容，
        不同的瀏覽器會有不同的userAgent
        - 在IE11中已經將微軟和IE相關的標示去除了，
           所以我們基本已經不能通過UserAgent來識別一個瀏覽器是否是IE了
        - 如果通過UserAgent不能判斷，還可以通過一些瀏覽器中特有的對象，
          來判斷瀏覽器的信息(比如:ActiveXObject)  
   - History
        - 代表瀏覽器的**歷史紀錄**，可以通過該對象來操作瀏覽器的歷史紀錄
        - 由於隱私原因，該對象不能獲取到具體的歷史紀錄，
      只能操作瀏覽器**向前**或**向後**翻頁
        - 而且該操作只在**當次訪問有效**
   - Location
      - 代表當前瀏覽器的地址欄信息(url)，通過Location可以獲取地址欄信息，
        或者操作瀏覽器**跳轉**頁面
   - Screen
      - 代表用戶的屏幕的信息，通過該對象可以獲取到用戶的**顯示器**相關信息
      - 這些BOM對象在瀏覽器中都是作為window對象的屬性保存的
      - 可以通過window對象來使用，也可以直接使用
###  Navigator
``` =javascript
//方法一：appName過時
console.log(navigator);
console.log(navigator.appName);

// 方法二:ie沒有特殊識別，無法辨別
console.log(navigator.userAgent);
var ua = navigator.userAgent;
if(/firefox/i.test(ua)){
   alert("firefox");
}else if(/chrome/i.test(ua)){
   alert("chrome");
}else if(/msie/i.test(ua)){
    alert("IE");
}

// 方法三: <針對ie11>因為IE11被動過手腳，轉布林值會是false
if(window.ActiveXObject){
   alert("你是IE")
}else{
   alert("你不是IE");
}

//方法四:<針對ie11>用in檢查
alert("ActiveXObject" in window);
if("ActiveXObject" in window){
    alert("你是IE")
}else{
    alert("你不是IE");
}

//<總結>針對所有瀏覽器
var ua = navigator.userAgent;
if(/firefox/i.test(ua)){
   alert("firefox");
}else if(/chrome/i.test(ua)){
   alert("chrome");
}else if(/msie/i.test(ua)){
   alert("ie");
}else if("ActiveXObject" in window){
   alert("ie11");
}
```
###  History
history對象方法
  - back() - 加載history列表中的前一個url
  - forward() - 加載history列表中的後一個url
  - go()
     - 加載history列表中的某個具體頁面
     - 它需要一個整數作為參數
        1 : 表示向前跳轉一個頁面，等價 history.forward();
        2 : 表示向前跳轉兩個頁面
       -1 : 表示向後跳轉一個頁面，等價 history.back();
       -2 : 表示向後跳轉兩個頁面
  - length屬性，可以獲取到當成訪問的鏈接數量
     - 只會記錄當次的訪問鏈接數量
``` =javascript
var btn = document.getElementById("btn");
btn.onclick = function(){
  // alert(history.length); //1 當前頁面
  // history.back();
  // history.forward();
  history.go();
}
```
###  Location
``` =javascript
alert(location); //透過location獲取當前url
```
如果直接將location屬性修改為一個完整的路徑，或相對路徑;
  則我們將會自動跳轉到該路徑，並且生成相應的歷史紀錄
``` =javascript
location = "http://www.google.com"; //可指定url進行跳轉
location = "test01.html";
```
####  assign()
- 加載新內容
- 用來跳轉到其他頁面，作用和直接修改location一樣
``` =javascript
location.assign("http://www.google.com");
```
####  reload()
- 重新加載當前文檔內容
- 如果在方法中傳遞一個true作為參數，則會強制清空
``` =javascript
    location.reload();
    location.reload(true); //強制清空緩存刷新
```
####  replace()
- 用新的文檔替換當前文檔，調用完畢也會跳轉頁面
- 不會生成歷史紀錄，不能回到上一頁
``` =javascript
location.replace("test01.html")
```
###  圖片切換練習
1.創建一個裝圖片src的陣列
2.透過點擊事件自增和自減索引值
3.設界限:小於第一張和大過最後一張時要停止
4.再將新的索引值賦值給圖片src屬性值，以此切換圖片