# 重新認識Javascript --- tags: Javascript relate --- ###### tags: `Javascript` # JavaScript: Day 02 JavaScript 簡介 ## JavaScript 的誕生與設計目標 Netscape (網景公司,當時知名瀏覽器廠商) 開發一門在瀏覽器上執行的語言系統,專門用來處理簡單的驗證。 最初的版本 (netscape2.0 beta1) 並沒有 `<script>` 標籤的設定,而是在表單元素中插入 onclick 的形式: ```javascript= //現在依舊可以使用喔! <input type="button" onclick="alert('hello, world!')"> ``` ## JavaScript 的名字由來 為了行銷考量~ 早在命名叫「LiveScript」之前,由於當時 Netscape 與 Sun (昇陽) 合作密切,且新誕生的這門程式語言其實有不少特性是由當時很火紅的 Java 借鑑而來,基於行銷考量,當時的 Netscape 高層其實早已決定要把這門語言命名叫「JavaScript」,但卻不小心把「LiveScript」這個開發者內部所訂定的名字公開了。直到同年 12 月,Netscape 與 Sun 發布聲明,正式啟用了「JavaScript」這個名字,就此沿用至今。 ## 為何成為瀏覽器唯一指定內建程式語言? 由於 Netscape 在 1996 年對 JavaScript 提出了標準化,第一個標準化版本 ECMA-262 在 1997 年就此誕生,也因為 Java 名稱上具有商標問題,ECMA-262 採用了 ECMAScript 作為語言名稱,JavaScript 此後成為了 ECMA-262 標準的實作語言,也變成瀏覽器唯一指定內建程式語言。 ## ECMAScript 與 JavaScript 的關係 剛剛提到 ECMAScript 是 Javascript 的標準,換成通俗點的說法,ECMA-262 標準是規格書,而 JavaScript、JScript 這類語言,就是依循這份規格書所實作出來的產品了。 # JavaScript: Day 03 變數與資料型別 ## 變數 * 變數是用來儲存資料和進行運算的基本單位 * JavaScript 的語法是有區分大小寫的 * JavaScript 中的變數宣告有其一定的規則 * 你的變數名稱是可以用中文命名的 * ES6之前只有var ES6之後出現let const * 變數本身無需宣告型別 在 JavaScript 中的變數宣告有其一定的規則,變數的第一個字母必須為英文字母、底線 _ 或是錢字號 $ ,後面可以是英文字母、底線 _ 或是錢字號 $ 以及數字。 變數名稱不可以是保留字 (Reserved Words) 與關鍵字 (keyword) [MDN 關鍵字與保留字列表](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords) 由於 JavaScript 是個「弱型別」[註2] 的語言,變數本身無需宣告型別,型別的資訊只在值或物件本身,變數只用來作為取得值或物件的參考: ![](https://i.imgur.com/jQIYMXH.png) ![](https://i.imgur.com/TCwxElT.png) > 要記得一句話,所有沒有透過 var 宣告的變數都會自動變成全域變數。 * 強型別 程式所定義的變數型別等於變數在執行時期的型別 * 弱型別 可以不用定義型別不需要宣告變數 ## 變數的資料型別 (1) > 嚴格來說,變數本身其實不帶有資料型別的資訊,其中的「值」或「物件」才有。 * 基本型別 (Primitives) 基本型別又分成 string、number、boolean、null、undefined 幾種 * 物件型別 (Object) 其他都是歸來這裡 ![](https://i.imgur.com/lYosuvm.png) ![](https://i.imgur.com/F1XLjim.png) ## string 字串 > 字串會用 ' ' (單引號) 或 " " (雙引號) 包夾住,兩者不可混用,意即用單引號開頭的,就要用單引號收合,反過來說也是。 跳脫字元的用法: ![](https://i.imgur.com/sNvFiNF.png) 字串串接: ![](https://i.imgur.com/hKgbyfQ.png) 分行文字的寫法: ![](https://i.imgur.com/nghNPy7.png) ## number 數字 > JavaScript 只有一種數值的型別,就是 number,不管整數或帶有小數點的數字都是一樣是number 除了常見的整數與小數點一類的數字外,另外還有幾種特殊的數字: * Infinity (無限大) * -Infinity (負無限大) * NaN (不是數值,Not a Number) Infinity 與 -Infinity 分別用來表示數學上的無限大與負無限大,一個正數除以 0,結果會得到是 Infinity,而任何負數除以 0 會得到 -Infinity。 咦? 你問 0 / 0 嗎? 結果會得到 NaN。 甚至是 Infinity / Infinity 或 -Infinity / -Infinity 同樣地也會得到 NaN。 NaN 在 JavaScript 當中是個有趣的存在,就字面上來說,它不是個數字,但你若用 typeof 運算子來判斷型態,它又會告訴你這是個 number。 ## boolean 布林值 > 與其他類別相比,boolean 就顯得單純地多,boolean 的名字由發明的科學家 George Boole 命名,其中的值只有兩種:true 以及 false。 > 布林值通常用在判斷式,作為控制程式流程的用途。 ![](https://i.imgur.com/3TJ8jNl.png) ## null 與 undefined * undefined 代表的是「(此變數) 還沒有給值,所以不知道是什麼」 * null 代表的是「(此變數可能曾經有值,可能沒有值) 現在沒有值」 ![](https://i.imgur.com/TrUnD8k.png) # JavaScript: Day 04 物件、陣列以及型別判斷 ## 物件 Object > 所有基本型別 (Primitives) 以外的值都是物件。 從 ECMA 262 標準中來看物件的定義: 「An object is a collection of properties and has a single prototype object.」 一個物件可以是個零至多種屬性的集合,而屬性是鍵 (key) 與值 (value) 之間的關聯。 一個屬性的「值」可以是某個基本型別,也可以是另一個物件,甚至可以是一個函數。 物件可以是瀏覽器預先定義好的,當然也可以是由自己定義物件的屬性與內容。 ### 物件及屬性 物件實字 按照字面上的意思直接用大括號 { },即可建立起一個新的物件,並且在建立物件的同時直接指定屬性至該物件內。 * 同時也是 JSON 格式的核心語法 * 這種建立物件的方式稱為物件實字 ```javascript= var person = { name: 'Kuro', job: 'Front-end developer', sayName: function() { alert( this.name ); } }; ``` ### 屬性存取 物件的屬性可以透過 . 來進行存取: ```javascript= var person = { name: 'Kuro', job: 'Front-end developer', sayName: function() { alert( this.name ); } }; person.name; // 'Kuro' person.sayName(); // 'Kuro' ``` 或是可以透過 [ ] 來進行存取,如: ```javascript= var person = { name: 'Kuro', job: 'Front-end developer', sayName: function() { alert( this.name ); } }; person["name"]; // 'Kuro' person["sayName"](); // 'Kuro' ``` 後者的好處是,當物件的索引鍵剛好是不合法的 JavaScript 的識別字 (如帶有空白的字串或是數字) 時,執行就會出現錯誤: ```javascript= var obj = { "001": "Hello" } obj.001; // SyntaxError: Unexpected number obj["001"]; // "Hello" ``` ### 屬性新增 若想為物件新增屬性的話,直接用 = 指定就可以了: ```javascript= var obj = { }; obj.name = 'Object'; obj.name; // 'Object' ``` ### 屬性刪除 要是想刪除屬性,則是透過 delete 關鍵字來刪除: ```javascript= var obj = { }; obj.name = 'Object'; obj.name; // 'Object' delete obj.name; obj.name; // 刪除屬性後變成 undefined ``` ### 判斷屬性是否存在 當我們試著去存取物件中不存在的屬性,此時會回傳 undefined,所以若是我們想要判斷屬性是否存在,最簡單的方式就是檢查該屬性是不是 undefined。 ```javascript= var obj = {}; console.log( obj.name ); // undefined ``` 但這麼做會有個例外,就是當該屬性剛好就是 undefined 時,這招就沒用了。 除了檢查 undefined 之外,還有 in 運算子 與 hasOwnProperty() 方法, ```javascript= var obj = { name: 'Object' }; // 透過 in 檢查屬性 console.log( 'name' in obj ); // true console.log( 'value' in obj ); // false // 透過 hasOwnProperty() 方法檢查 obj.hasOwnProperty('name'); // true obj.hasOwnProperty('value'); // false ``` ## 陣列 Array JavaScript 的陣列可以看作是一種特別的「物件」,同樣是零至多個元素的集合,且並沒有規定它能放什麼東西進去。 陣列內可以是原始的資料類型、其他陣列、函式等等。 要注意的是,陣列是個有順序性的集合,且只能透過 [] 來存取。 ### 建立陣列同樣也可以透過 new 關鍵字來建立: ```javascript= var a = new Array(); a[0] = "apple"; a[1] = "boy"; a[2] = "cat"; a.length; // 3 ``` ### 陣列實字 (Array literal): 實務上常見 ```javascript= var a = []; a[0] = "apple"; a[1] = "boy"; a[2] = "cat"; a.length; // 3 ``` 而陣列的長度可以由 ARRAY.length 來取得,而且 length 這個屬性的值是可以被覆寫的。 ```javascript= var a = ["apple", "boy", "cat"]; a.length; // 3 a.length = 1; console.log(a); // ["apple"] 直接刪掉後面兩個 a.length = 3; console.log(a); // ["apple", undefined, undefined] ``` 像上面的例子中,陣列 a 原本的長度為 3,後來透過 a.length = 1; 設定成 1 之後,後面的元素就被移除了。 即使之後再度修改成 a.length = 3;,後面的兩個元素也只會被 undefined 所填補。 陣列的長度隨時可以增加或減少,就算指定索引元素時也不一定要連續指定: ```javascript= var array = ['a', 'b', 'c']; array.length; // 3 array[7] = 'z'; console.log(array); // ["a", "b", "c", undefined, undefined, undefined, undefined, "z"] ``` 另外要注意的是,陣列的索引是由 0 開始計算的,也就是說一個 array = ['a', 'b', 'c']; 陣列的第一個元素,實際上要用 array[0] 來取得。 ### ARRAY.push() 想要在陣列末端新增元素時 ```javascript= var array = ['a', 'b', 'c']; array.length; // 3 array.push('d'); console.log(array); // ['a', 'b', 'c', 'd'] ``` [其他Array方法](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array) ## typeof: 型別判斷 透過 typeof 運算子回傳的東西都是「字串」 ![](https://i.imgur.com/YKgFrDJ.png) **typeof function(){} 為什麼是 "function" 不是 "object" ?** 你可以把它想像成是一種可以被呼叫 (be invoked) 的特殊物件。 雖然 typeof function(){ } 回傳的是 "function",但實際上仍然是屬於「物件」的型別。 **typeof null 為什麼是 "object" ?** > 「其實這只是一個 Bug !」 > 因為修改這樣的問題,會影響到太多舊有的程式,因此最後還是被 reject 了。 ## 如何判別是否為陣列 Array 定義了 isArray() 方法 ![](https://i.imgur.com/YnD2gNi.png) # JavaScript: Day 05 JavaScript 是「傳值」或「傳址」? * 基本型別內的資料,會是以純值的形式存在 ( string、number、boolean、null、undefined ) * 物件型別指的是可能由零或多種不同型別 (包括純值與物件) 所組合成的物件。 ## 基本型別 基本型別判斷兩個變數是否相同看的是"值" ```javascript= var a = 10; var b = 10; // 在 JavaScript 判斷是否「相等」用 " === " console.log( a === b ); // true var a = 'Kuro'; var b = 'Kuro'; var c = 'Jack'; console.log( a === b ); // true console.log( a === c ); // false ``` ## 物件型別 ```javascript= var obj1 = { value: 10 }; var obj2 = { value: 10 }; obj1 ===obj2 // 會回傳false ``` 我在這邊的理解是兩邊的value代表不一樣的10 以十元來說好了可能是不同的貨幣的10元或是長的不同等等 ## 變數的更新與傳遞 ### 基本型別的更新與傳遞 我的理解是 var b = a,也就是 b = 10,所以 a之後做了重新的指派也不會影響b的指派內容 像這種情況,我們通常會稱作「**傳值**」 (pass by value)。 ```javascript= var a = 10; var b = a; console.log( a ); // 10 console.log( b ); // 10 a = 100; // 變數 b 依然是 10,而變數 a 變成了 100 console.log( a ); // 100 console.log( b ); // 10 ``` ### 物件型別的更新與傳遞 「物件」這類資料型態,在 JavaScript 中是透過「引用」的方式傳遞資料的。 像這種透過引用的方式來傳遞資料,接收的其實是引用的「參考」而不是值的副本時, 我們通常會稱作「**傳址**」 (pass by reference)。 我的理解是因為物件類型的傳遞資料是傳址,所以在指派階段的時候就已經安排它們的"地址"是一樣的了所以地址一樣想當然內容也是一樣的! ```javascript= var coin1 = { value: 10 }; var coin2 = coin1; console.log( coin1.value ); // 10 console.log( coin2.value ); // 10 //乍看之下與前面基本型別 (純值) 的情況沒什麼不同,但是: coin1.value = 100; console.log( coin1.value ); // 100 console.log( coin2.value ); // 100 ``` ## 「傳值」或「傳址」? 在大多數的情況下,基本型別是「傳值」,而物件型別會是「傳址」的方式,但凡事都有例外。 更準確一點來說,JavaScript 應該屬於透過 pass by sharing (還沒找到合適的中文翻譯) 來傳遞資料。 ### Pass by sharing 「Pass by sharing」的特點在於,當 function 的參數,如 function changeValue(obj){ ... } 中的 obj 被重新賦值的時候,外部變數的內容是不會被影響的。 ```javascript= var coin1 = { value: 10 }; function changeValue(obj) { obj = { value: 123 }; } changeValue(coin1); console.log(coin1); // 此時 coin1 仍是 { value: 10 }因為重新賦值發生在function內外部變數不會被影響 如果不是重新賦值的情況,則又會回到大家所熟悉的狀況: var coin1 = { value: 10 }; function changeValue(obj) { // 僅更新 obj.value,並未重新賦值 obj.value = 123; } changeValue(coin1); console.log(coin1); // 此時 coin1 則會變成 { value: 123 } ``` ## Pass by value、 Pass by reference 、Pass by sharing 所以 JavaScript 到底屬於何種策略? JavaScript 應該更屬於 Pass by sharing 的形式。 ```javascript= var a = 10; var b = a; a = 100; console.log(a); // 100 console.log(b); // 10 ``` 這個時候在基本型別的操作下,以 Pass by sharing 的行為來說,與 Pass by value 的結果是完全一樣的,修改時只能更新值而不是指派新的變數值。 # JavaScript: Day 06 運算式與運算子 ## 運算式 (Expression) 與 運算子(Operator) JavaScript 的語法基本上可以分為兩大類,「敘述句 (Statement)」 與 「運算式 (Expression)」。 敘述句 (Statement):做某個動作,像是變數的宣告、賦值,迴圈和 if 判斷式 ![](https://i.imgur.com/tL5A1HU.png) 運算式 (Expression):會產生值,例子function = 右側的部分都算是運算式因為會產生值指派給function ![](https://i.imgur.com/6FjxAx2.png) 上例 = 右側的 10 * 10 就是運算式。 ### 運算子 在運算式中會提供一些運算子來做運算 像是 加減乘除 比較直觀的一種 還有很多類別: * 算術運算子 (Arithmetic Operator) * 指派運算子 (Assignment Operator) * 位元運算子 (Bitwise Operator) * 比較運算子 (Comparison Operator) * 邏輯運算子 (Logical Operator) * 字串運算子 (String Operator) * 特殊運算子 (Special Operator) 更詳細的資訊: [ MDN: 運算式與運算子 ](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Expressions_and_Operators) ### 算術運算子 #### 加號 (+) ##### 「數字」 如果加號 + 前後雙方都是「數字」的話,會是最單純的狀況。 ![](https://i.imgur.com/rgWz1Rc.png) ##### 「特殊的數字」 * Infinity * -Infinity * NaN Infinity ![](https://i.imgur.com/Jd0zb0b.png) NaN只要有其中一個是 NaN,那麼結果就必定是 NaN ![](https://i.imgur.com/KMG60C1.png) ##### 「字串」 加號在字串作用的話只要加號的兩邊有一邊是字串就會把另一邊自動做轉型成字串 ![](https://i.imgur.com/q6Kif2F.png) 以 number 、boolean 、 object 的情況來說,轉型時會去呼叫它們的 .toString() 的 「原型方法」去取得對應的字串。 ![](https://i.imgur.com/dC5dYyg.png) 而 null 與 undefined 則是透過 JavaScript 的 String() 函數來將它們分別轉為 "null" 與 "undefined"。 ![](https://i.imgur.com/KgKKM0J.png) ##### 運算規則 > 運算式的計算是「由左而右」且「先乘除後加減」的模式來運算。 #### 減號 (-)跟加法差很多 ##### 「數字」 單純的數字運算 ![](https://i.imgur.com/Awu9Wu4.png) ##### 「特殊的數字」 其中一方是 NaN 的話,那麼結果必定是 NaN。 ![](https://i.imgur.com/v8GDBTn.png) ##### 當其中一方不是「數字」的情況下: * 基本型別 若其中一方屬於基本型別且不是數字的情況,那麼 JavaScript 會在先在背後透過 Number() 嘗試將數值轉為「數字」,再進行運算。 ![](https://i.imgur.com/U7LW4du.png) * 物件型別 如果是物件型別的情況下,則是會先透過物件的 valueOf() 方法 [註2] 先求得對應的數值,如果得到 NaN,那麼結果就是 NaN。 如果物件沒有 valueOf() 方法的話,則是透過 toString() 先轉成「字串」後,再以 Number() 嘗試將數值轉為「數字」後進行運算。 ![](https://i.imgur.com/kP4KzRC.png) #### 乘號 (*) 如果計算結果超出 JavaSCript 的數字範圍就看是正數還是負數決定是infinite正的或是負的 當然如果其中一個是 NaN的話,那麼結果必定也是 NaN。 Infinity * 0 的結果也是 `NaN` 一樣換轉換成數字 ![](https://i.imgur.com/BQmN5tV.png) #### 除號 (/) 跟乘法的規則相仿一樣會把不是數字的使用Number()做轉換 如果有其中一個是 NaN,則結果也會是 NaN 。 但是,在除數為 0 的情況下: * 被除數為正數,則結果為 Infinity * 被除數為負數,則結果為 -Infinity * 被除數為 0,則結果為 NaN #### 取餘數 (%) 跟乘法除法的規則相仿一樣會把不是數字的使用Number()做轉換 infinite -infinite的結果在這邊怎麼處理都是NaN ![](https://i.imgur.com/bd558zC.png) 被除數是一般數值,而除數為 Infinity 的情況下,則結果為被除數: ![](https://i.imgur.com/ViYYMkw.png) 被除數是一般數值,而除數為 0 的情況下,則結果也是 NaN。 ```javascript= 100 % 0 // NaN ``` # JavaScript: Day 07 「比較」與自動轉型的規則