├── README.md ├── basic.md └── object-oriented.md /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript 筆記 2 | 3 | * [基礎介紹](basic.md) 4 | * [物件導向基礎](object-oriented.md) -------------------------------------------------------------------------------- /basic.md: -------------------------------------------------------------------------------- 1 | # JavaScript 基礎 2 | 3 | ## 如何在瀏覽器中執行 JavaScript 4 | 5 | 1. 放在 `` 之間執行 6 | 2. 放在外部的 .js 檔案中,再以 script 標籤的 `src` 載入執行 7 | 8 | ## script 放在哪裡? 9 | 10 | * 放在最後 (`` 之前) 。 11 | * 避免程式執行緩慢而阻斷了頁面的顯示。 12 | 13 | ## alert() 與 console.log 14 | 15 | * alert 會停下來等使用者回應,就是阻斷的一種。 16 | * 改用 console.log 可以避免阻斷程式執行,結果將顯示在 console 中。 17 | * Console 可以在各家瀏覽器的開發者工具上找到。 18 | 19 | ## 變數 20 | 21 | * 用 `var` 來宣告變數,同時給變數初始值。 22 | 23 | var myVar1; 24 | var myVar2 = 123; 25 | 26 | * 多個變數可以用逗號 (`,`) 同時宣告。 27 | 28 | var myVar1, myVar2 = 123; 29 | 30 | ## 類型 31 | 32 | ### 數字 (Number) 33 | 34 | * 有「整數」「浮點數」 35 | 36 | ### 字串 (String) 37 | 38 | * 用單引號或雙引號包起來的稱為字串 39 | * 字串可以用加號 (+) 將多個字串組合起來。 40 | 41 | ### 常數 42 | 43 | * 存放不能被改變的值 44 | * 名稱通常是用全大寫 45 | * 以往是用變數來模擬,在 ECMAScript 5 之後可以用 `const` 關鍵字來定義 46 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const 47 | * http://kangax.github.io/es5-compat-table/es6/#const 48 | 49 | ## 操作 HTML 元素 50 | 51 | * `document.getElementById()` 52 | * `document.querySelector()` 53 | * `document.querySelectorAll()` 54 | * `innerHTML` 55 | * `onclick` 56 | * `element.addEventListener()` 57 | 58 | ### `document.getElementById()` 59 | 60 | 將變數與到 HTML 上有 id 屬性的元素綁在一起。 (注意大小寫) 61 | 62 |
HTML內容
63 | 64 | var element = document.getElementById("id-name"); 65 | 66 | ### `document.querySelector()` 67 | 68 | 利用 CSS 規則語法來取得單一個 HTML 元素。 69 | 70 |
HTML內容
71 | 72 | var element = document.querySelector("#id-name"); // # 開頭表示 CSS 的 id 73 | 74 | ### `document.querySelectorAll()` 75 | 76 | 利用 CSS 規則語法來取得一組 HTML 元素群。 77 | 78 |
HTML內容
79 |
HTML內容
80 |
HTML內容
81 | 82 | var elements = document.querySelectorAll(".class-name"); // . 開頭表示 CSS 的 class 83 | var element1 = elements[0]; // 可以用陣列的形式來取得元素群的單一元素 84 | 85 | ### `innerHTML` 86 | 87 | * 取出 HTML 元素包含的內容 88 | * 也可以指定 HTML 內容給 HTML 元素。 89 | 90 | 範例: 91 | 92 | var html = element1.innerHTML; 93 | element2.innerHTML = html; 94 | 95 | ### `onclick` 96 | 97 | * 表示 HTML 元素的按下事件 98 | * 可以接受一個匿名函式當做動作。 99 | * 當按下按鍵時,就會觸發這個動作。 100 | * 重複指定匿名函式的話,新的會蓋掉舊的,所以只會執行最新的。 101 | 102 | `onclick` 裡的匿名函式中的 `this` 代表 `onclick` 所屬的 HTML 元素。 103 | 104 | 範例: 105 | 106 | HTML: 107 | 108 | Test 109 | 110 | JavaScript: 111 | 112 | var link = document.getElementById('link'); 113 | link.onclick = function () { 114 | console.log(this.href); // 顯示 "/path/to/resource" 115 | } 116 | 117 | ### `element.addEventListener()` 118 | 119 | * 可以對事件新增一個監聽的處理函式 120 | * 跟 `onXXXX` 不同的地方在於可以對同一事件註冊多個處理函式 121 | 122 | 範例: 123 | 124 | HTML: 125 | 126 | Test 127 | 128 | JavaScript: 129 | 130 | var link = document.querySelector('#link'); 131 | link.addEventListener('click', function () { 132 | console.log(this.href); // 顯示 "/path/to/resource" 133 | }); 134 | 135 | ## 句點操作符 136 | 137 | 句點 (`.`) 後面接的是某個東西的屬性或方法 138 | 139 | 例: 140 | 141 | * `innerHTML` 是某 HTML 元素的屬性 142 | * `getElementById` 是 `document` 的方法 143 | 144 | ## 函式 145 | 146 | * 函式分為指名函式及匿名函式 147 | * 函式的宣告方式有兩種: 148 | 149 | 1. 直接對函式命名。 150 | 2. 將匿名函式指定給一個變數。 151 | 152 | ## 函式呼叫時機 153 | 154 | 函式依照宣告方式的不同,會影響呼叫的時機。 155 | 156 | ### 指名函式 157 | 158 | 範例: 159 | 160 | myFunc(); // 可以在宣告前呼叫 161 | 162 | function myFunc1() { 163 | // do something 164 | } 165 | 166 | myFunc(); // 也可以在宣告後呼叫 167 | 168 | ### 匿名函式指定給變數 169 | 170 | 範例: 171 | 172 | myFunc(); // 不可在宣告前呼叫,會發生函式未定義的錯誤 173 | 174 | var myFunc = function () { 175 | // do something 176 | } 177 | 178 | myFunc(); // 可以在宣告後呼叫 179 | 180 | ## 陣列 181 | 182 | * 陣列用方括號 (`[]`) 宣告及初始化 183 | * 陣列索引從 0 開始。 184 | 185 | 範例: 186 | 187 | var myArray = ['John', 'Joe', 'Kevin']; 188 | console.log(myArray[0]); // "John" 189 | console.log(myArray[1]); // "Joe" 190 | console.log(myArray[2]); // "Kevin" 191 | 192 | ## for 敘述 193 | 194 | `for` 有兩種形式,第一種是: 195 | 196 | for (初始化變數; 終止條件判斷; 步進式) { 197 | // do something 198 | } 199 | 200 | 範例: 201 | 202 | var i, myArray = [2, 56, 68, 1, 44, 92, 15, 34, 24, 78]; 203 | 204 | for (i = 0; i < myArray.length; i ++) { 205 | console.log(myArray[i]); 206 | } 207 | 208 | 這裡 `for` 會多執行一次來判斷是否要進行迴圈。 209 | 210 | 第二種是: 211 | 212 | for (索引 in 陣列) { 213 | // do something 214 | } 215 | 216 | 範例: 217 | 218 | var i, myArray = [2, 56, 68, 1, 44, 92, 15, 34, 24, 78]; 219 | 220 | for (i in myArray) { 221 | console.log(myArray[i]); 222 | } 223 | 224 | 注意 `myArray[i]` 才是陣列元素內容。 225 | 226 | 以上兩個方法都可以用來一一處理 HTML 元素群: 227 | 228 | var i, elements = document.querySelectorAll('.class-name'); 229 | 230 | for (i = 0; i < elements.length; i ++) { 231 | var el = elements[i]; 232 | // do something 233 | } 234 | 235 | for (i in elements) { 236 | var el = elements[i]; 237 | // do something 238 | } 239 | 240 | ## if 敘述 241 | 242 | if 用來判斷條件式是否成立,成立時就執行第一個大括號裡的程式碼,否則就執行 `else` 後的程式碼。 243 | 244 | if (condition) { 245 | // do this when condition is true 246 | } else { 247 | // do this when condition is false. 248 | } -------------------------------------------------------------------------------- /object-oriented.md: -------------------------------------------------------------------------------- 1 | # JavaScript 物件導向基礎 2 | 3 | ## 如何表示物件 4 | 5 | 範例: 6 | 7 | var p1 = { 8 | name: '小明', 9 | birthday: '1980/2/28', 10 | 'phone-number': '0912345678' // 注意最後沒有逗號 11 | }; 12 | 13 | * 用 `{ ... }` 包起來表示物件實體 (instance) 14 | * 物件實體表示一個實際存在的物件 15 | * `key: value` 表示屬性,每個屬性之間用逗號 (`.`) 區隔 16 | * 非標準變數名稱做為 key 時,要加引號 (`'...'` 或 `"..."`) 17 | 18 | ## 操作物件實體 19 | 20 | 範例: 21 | 22 | console.log(p1.name); 23 | console.log(p1.birthday); 24 | console.log(p1['phone-number']); 25 | 26 | p1.name = '小華'; 27 | p1['phone-number'] = '0921876543'; 28 | 29 | * 用 `.` 來取得屬性 30 | * 用 `[...]` 來取得非標準名稱的屬性 31 | 32 | ## 物件實體的別名 33 | 34 | 範例: 35 | 36 | var p1 = { 37 | name: '小明' 38 | }; 39 | 40 | console.log(p1.name); // "小明" 41 | 42 | var p2 = p1; 43 | p2.name = '小華'; 44 | console.log(p2.name); // "小華" 45 | console.log(p1.name); // "小華" 46 | 47 | * `p2` 跟 `p1` 可以視為是一個指標變數,都是指向同一個物件實體 48 | 49 | ## 用外部函式來操作物件實體 50 | 51 | 範例: 52 | 53 | var p1 = { /* ... */ }; 54 | 55 | function who(p) { 56 | 57 | var birthday = new Date(p.birthday); 58 | // 1000 * 60 * 60 * 24 * 365 = 31536000000 59 | var age = Math.floor((new Date() - birthday) / 31536000000); 60 | 61 | return 'I\'m ' + p.name + ', my age is ' + age; 62 | } 63 | 64 | who(p1); 65 | 66 | * 函式主要是將一個常用的邏輯包裝起來 67 | * 利用帶入參數的方式來更改操作的對象 (物件實體) 68 | 69 | ## 讓物件實體負責自己的行為 70 | 71 | 範例: 72 | 73 | var p1 = { 74 | name: '小明', 75 | birthday: '1980/2/28', 76 | who: function () { 77 | 78 | var birthday = new Date(p1.birthday); 79 | var age = Math.floor((new Date() - birthday) / 31536000000); 80 | 81 | return 'I\'m ' + p1.name + ', my age is ' + age; 82 | } 83 | }; 84 | 85 | console.log(p1.who()); 86 | 87 | * 如果函式應該是跟隨著物件實體,那麼可以把它改成為物件實體的屬性 88 | * 此時跟著物件實體的函式應該改稱為「方法」 89 | 90 | ## 第一次接觸 `this` 91 | 92 | 範例: 93 | 94 | var p1 = { 95 | name: '小明', 96 | birthday: '1980/2/28', 97 | who: function () { 98 | 99 | var birthday = new Date(this.birthday); 100 | var age = Math.floor((new Date() - birthday) / 31536000000); 101 | 102 | return 'I\'m ' + this.name + ', my age is ' + age; 103 | } 104 | }; 105 | 106 | * 一般狀況下,在方法中的 `this` 會指回物件實體自己 107 | * 在物件實體中需要透過 `this` 來存取自己的屬性 108 | 109 | ### 當方法中有函式時 110 | 111 | 範例: 112 | 113 | var p1 = { 114 | name: '小明', 115 | birthday: '1980/2/28', 116 | who: function () { 117 | 118 | // 用生日計算年齡 119 | function age() { 120 | console.log(this); 121 | var birthday = new Date(this.birthday); 122 | return Math.floor((new Date() - birthday) / 31536000000); 123 | } 124 | 125 | return 'I\'m ' + this.name + ', my age is ' + age(); 126 | } 127 | }; 128 | 129 | * 在方法裡可以再包含函式 130 | * 方法裡的函式用到的 `this` 就不再是指到原來的物件 131 | * 在方法裡的函式中, `this` 變成了 Global 的 `window` 物件 132 | 133 | ### 改法一:用參數傳遞 134 | 135 | * `age` 改為帶一個參數 `that` 的函式 136 | * `age` 中的 `this` 改成 `that` 137 | * 在 `who` 方法中把 `this` 傳給 `age` 138 | 139 | 範例: 140 | 141 | var p1 = { 142 | name: '小明', 143 | birthday: '1980/2/28', 144 | who: function () { 145 | 146 | function age(that) { 147 | var birthday = new Date(that.birthday); 148 | return Math.floor((new Date() - birthday) / (1000 * 60 * 60 * 24 * 365)); 149 | } 150 | 151 | return 'I\'m ' + this.name + ', my age is ' + age(this); 152 | } 153 | }; 154 | 155 | console.log(p1.who()); 156 | 157 | ### 改法二:利用變數的 scope 158 | 159 | * 利用 `that` 變數暫時記住 `this` 160 | * `age` 中的 `this` 改成 `that` 161 | 162 | 範例: 163 | 164 | var p1 = { 165 | name: '小明', 166 | birthday: '1980/2/28', 167 | who: function () { 168 | 169 | var that = this; 170 | 171 | function age() { 172 | var birthday = new Date(that.birthday); 173 | return Math.floor((new Date() - birthday) / (1000 * 60 * 60 * 24 * 365)); 174 | } 175 | 176 | return 'I\'m ' + this.name + ', my age is ' + age(); 177 | } 178 | }; 179 | 180 | console.log(p1.who()); 181 | 182 | ## 物件中方法呼叫方法 183 | 184 | * 把 `age()` 函式變成方法 185 | * 在 `who` 方法中改用 `this.age()` 來呼叫 186 | 187 | 範例: 188 | 189 | var p1 = { 190 | name: '小明', 191 | birthday: '1980/2/28', 192 | who: function () { 193 | return 'I\'m ' + this.name + ', my age is ' + this.age(); 194 | }, 195 | age: function () { 196 | var birthday = new Date(this.birthday); 197 | return Math.floor((new Date() - birthday) / (1000 * 60 * 60 * 24 * 365)); 198 | } 199 | }; 200 | 201 | console.log(p1.who()); 202 | 203 | * `age` 方法定義在 `who` 方法之後也沒關係 204 | 205 | ## 定義物件的類型 206 | 207 | * `{...}` 一次只能定義一個物件實體,很難重複利用 208 | * 用建構函式來定義一個共用類型 209 | 210 | 範例: 211 | 212 | var Programer = function (name, birthday, phoneNumber) { 213 | this.name = name; 214 | this.birthday = birthday; 215 | this['phone-number'] = phoneNumber; 216 | this.who = function () { /* ... */ }; 217 | this.age = function () { /* ... */ }; 218 | }; 219 | 220 | var p1 = new Programer('小明', '1990-06-21', '0912345678'); 221 | var p2 = new Programer('小華', '1991-11-03', '0922111555'); 222 | 223 | console.log(p1); 224 | console.log(p2); 225 | 226 | * 用 `new` 關鍵字來透過建構函式建立一個物件 227 | * 建構式可以帶參數 228 | * 要用 `this` 來指定物件成員 229 | * 透過 `new` 呼叫建構函式時, `this` 指向建立出來的物件 230 | * 沒有用 `new` 來呼叫建構式,這時的建構式就只是普通的函式, 231 | `this` 指向 Global 的 `windows` 物件。 232 | 233 | ## 再看看 `this` 234 | 235 | 範例: 236 | 237 | window['phone-number'] = '0999123456'; 238 | 239 | function getPhoneNumber() { 240 | return this['phone-number']; 241 | } 242 | 243 | console.log(getPhoneNumber()); 244 | 245 | * 函式中的 `this` 可以看成保留的空位 246 | * `this` 是程式執行時動態決定它要指向誰 247 | * 預設函式中的 `this` 是指向 `window` 248 | 249 | ## 用 `apply` 來決定誰是 `this` 250 | 251 | 範例: 252 | 253 | var p1 = new Programer('小明', '1990/06/21', '0912345678'); 254 | var p2 = new Programer('小華', '1991/11/03', '0922111555'); 255 | 256 | function getPhoneNumber() { 257 | return this['phone-number']; 258 | } 259 | 260 | console.log(getPhoneNumber.apply(p1)); 261 | console.log(getPhoneNumber.apply(p2)); 262 | 263 | * 函式變數會提供一個 `apply` 方法 264 | * `apply` 會將第一個參數連結到函式中的 `this` 265 | 266 | ## 有參數的函式使用 `apply` 267 | 268 | 範例: 269 | 270 | function profile(label1, label2, label3) { 271 | var s = ''; 272 | s += label1 + ': ' + this.name + '\n'; 273 | s += label2 + ': ' + this.birthday + '\n'; 274 | s += label3 + ': ' + this['phone-number'] + '\n'; 275 | return s; 276 | } 277 | 278 | console.log(profile.apply(p1, ['Name', 'Birthday', 'Phone Number'])); 279 | // 'Name' 對應 label1 280 | // 'Birthday' 對應 label2 281 | // 'Phone Number' 對應 label3 282 | 283 | * `apply` 第二個參數是接一個陣列,陣列值會依序傳給函式的參數 284 | 285 | ## Prototype 初探 286 | 287 | * JavaScript 是 Protoype-based 物件導向語言 288 | * 可以用 Prototype 來擴充類型的屬性與方法 289 | 290 | 範例: 291 | 292 | var Programer = function () { /* ... */ }; 293 | var p1 = new Programer(); 294 | 295 | console.log(p1.profile('Name', 'Birthday', 'Phone Number')); 296 | // 錯誤, profile 還沒有宣告 297 | 298 | // 讓 profile 函式跟著 Programer 這個類型 299 | Programer.prototype.profile = function (label1, label2, label3) { 300 | /* ... */ 301 | }; 302 | 303 | console.log(p1.profile('Name', 'Birthday', 'Phone Number')); 304 | // 正確, profile 可以被所有類型為 Programer 的物件實體使用 305 | 306 | * 建構函式的 `prototype` 屬性可以讓我們擴充類型的屬性與方法 307 | * 這時候 `profile` 中的 `this` 就會自動對應到我們透過 `new` 建立的物件 308 | * 修改 `prototype` 會影響所有透過 `new` 所建立的該類型物件 309 | * 只有類型會有 `prototype` 屬性,物件實體 310 | 311 | ## Protytpe 繼承 312 | 313 | * 類型可以透過 `prototype` 屬性來繼承另一個類型 314 | 315 | 範例: 316 | 317 | var Employee = function () { 318 | this.salary = 0; 319 | this.setSalary = function (salary) { 320 | this.salary = salary; 321 | }; 322 | this.getSalary = function () { 323 | return this.salary; 324 | }; 325 | }; 326 | 327 | var Programer = function (name, birthday, phoneNumber) { 328 | /* ... */ 329 | }; 330 | 331 | Programer.prototype = new Employee(); 332 | 333 | var p1 = new Programer('小明', '1980/2/28'); 334 | 335 | p1.setSalary(22000); 336 | console.log(p1.getSalary()); 337 | 338 | * 必須用 `new` 初始化一個要繼承的類型,然後放到 `prototype` 屬性中 339 | * 被繼承的類型中,裡面的 `this` 會指向透過 `new` 所建立的物件實體 --------------------------------------------------------------------------------