JavaScript Crash Course For Beginners 學習大綱以及準備 What is Javascript 高級語言 編譯過的程式語言(不需要編譯器) 符合ECMAScript的規範 多重編程範式 是一種可以支援超過一種編程範式的程式語言。 執行於客戶/瀏覽器端同時也作用時伺服器端(Node.js) Why learn Javascript JS是瀏覽器語言 如果你要操作瀏覽器必學 建構互動性高的UI藉著React 建構客戶端的server以及全端的app 建構行動裝置的app(React Native, NativeScript,Lonic) 建構桌機端的app(Electron JS) What you will learn in this course 變數&數據型態(Variables & Data Type) 陣列(Arrays) 物件實字(object literals) 操作字串、陣列、物件的方法(Methods for string, arrays, objects,etc) 迴圈(Loops - for, while, for…of, forEach, map) 條件式(conditionals - if, ternary&switch 函式(function&arrow) 物件導向(OOP-prototype&classes) DOM選取器介紹(DOM Selection) DOM操作 事件(Events) 基本的驗證(basic validation) 建議可以使用的VScode 配件 可以即時的看做的改變而不用一直做重新整理:
幾種在HTML檔案使用JS的位置 放在body最底層直接撰寫JS程式碼
1 2 3 4 5 6 7 8 9 10 <body> <header> <h1>JS Crash Course</h1> </header> <script> alert('HEllo world' ); </script> </body>
引用到外面的檔案(推薦使用)
1 2 3 4 5 6 7 8 <body> <header> <h1>JS Crash Course</h1> </header> <script src="main.js" ></script> </body>
Console 印出結果跟debug可以使用的地方
推薦使用chrome瀏覽器的工具相容性高
一般我們常見的console.log()
的log的部分其實是方法(methods)
console常見的方法 - MDN
變數&數據型態(Variables & Data Type) var 最早版本的變數 是全域範圍的變數容易導致衝突 現在多已使用下面兩個變數居多 let let 可以重新被指派變數
1 2 3 const age = 30 ;age = 31 console .log(age);
const 作者建議多多使用const
除非你知道你會使用其他值來做重新指派那就使用let
const 變數被指派的值不能被改變並且一定要有起始值(initializer)不然會報錯:
1 2 3 const age = 30 (起始值);age = 31 console .log(age);
Primitive Data Type 1 2 3 4 5 6 7 8 9 const name = 'John' ; const age = 30 ;const rating = 4.5 ;const isCool = true ;const x = null const y = undefined ;let z; console .log(typeof name);
串接(Concatenation) 比較老的字串串接寫法: 當字串較長且變數很多的時候會很痛苦
1 console .log('my name is ' + name + ' and i am ' + age);
Template string: 主要使用(~)下面那個符號讓字串串接變的很容易
1 console .log(`my name is ${name} and i am ${age} ` );
甚至可以把它們指派給變數
1 2 3 const hello = `my name is ${name} and i am ${age} ` ;console .log(hello);
字串的methods 計算文字長度(會包含空格)
1 2 3 const s = 'hello world' ;console .log(s.length);
轉換至大寫: 這邊的方法因為是function所以需要加上()來做呼叫 (也有轉換到小寫toLowercase)
1 2 3 const s = 'hello world' ;console .log(s.toUpperCase());
擷取文字: ()內的參數代表(起始位置,結束位置)
1 2 3 const s = 'hello world' ;console .log(s.substring(0 , 5 ));
方法也可以做串接使用(.)
1 2 3 const s = 'hello world' ;console .log(s.substring(0 , 5 ).toUpperCase());
split: 在參數內是要用來隔開內容的比方說
這邊參數內寫入的是空白因此會得到全部用’’隔開的array
1 2 3 const s = 'hello world' ;console .log(s.split('' ));
如果要把用逗號串接的字串轉換成array的話:
1 2 3 const s = 'technology, computers, it, code' console .log(s.split(',' ));
陣列(Arrays) 擁有不只一個value的變數就是array
number的array:
1 2 3 const numbers = new Array (1 , 2 , 3 , 4 , 5 );console .log(numbers);
字串的array:
1 2 3 const fruits = ['apples' , 'oranges' , 'pears' ];console .log(fruits);
arrays是以0為基底所以這樣去使用的話會得到:
console.log(fruits[1]);
如果要多串一些物件上去可以這樣使用:
1 2 3 4 5 const fruits = ['apples' , 'oranges' , 'pears' ];fruits[3 ] = 'grapes' ; console .log(fruits);
下面是不行的狀況把const變數指派給新的array就會報錯搂
1 2 3 4 5 const fruits = ['apples' , 'oranges' , 'pears' ];const fruits = [];console .log(fruits);
然而大多數的情況下你不一定都會知道array裡面的物件數量,所以這時就可以使用
push
這個methods直接把新的物件加到最後一個位置不管總數為何
1 2 3 4 5 6 7 const fruits = ['apples' , 'oranges' , 'pears' ];fruits[3 ] = 'grapes' ; fruits.push('mangos' ); console .log(fruits);
如果要把物件加到開頭可以使用unshift
1 2 3 4 5 6 7 8 9 const fruits = ['apples' , 'oranges' , 'pears' ];fruits[3 ] = 'grapes' ; fruits.push('mangos' ); fruits.unshift('strawberries' ); console .log(fruits);
一些常見的方法: 1 2 3 4 5 6 7 8 9 fruits.push('mangos' ); fruits.unshift('strawberries' ); fruits.pop(); console .log(Array .isArray(fruits))console .log(fruits.indexOf('oranges' ))
物件實字(object literals) 優點是因為封裝的關係可以減少一些全域變數會造成的衝突(不知道是否過時這段說法)
物件實字的語法重點:
會用大括號表示。 裡面的屬性 (Properties) 用鍵值對 (name(key)-value pairs) 表示。ex.(title: ‘Book one’,) 多個屬性以逗號 (comma) 分隔。 宣告完後,還是可以再增加 Properties 進去。
1 2 3 4 5 6 7 8 9 10 11 12 const person = { firstName: 'John' , lastName: 'Doe' , age: 30 , hobbies: ['music' , 'movies' , 'sports' ], address: { street: '50 main st' , city: 'Boston' , state: 'MA' } }
取得hobbies底下的movies:
console.log(person.hobbies[1])
取的address底下的city:
console.log(person.address.city)
解構賦值(Destructuring assignment): ES6的東西
可以從array中取出值(value) 可以從object中取出property 並且指派給新的變數
1 2 3 4 5 6 7 8 9 10 const { firstName, lastName, address: { city } } = person; console .log(city);
加入新的property:
1 2 3 person.email = '1234@hotmail.com.tw' console .log(person)
從array中的物件取得value: 想要取得’Meeting with boss’這個字串的方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const todos = [{ id: 1 , text: 'take out trash' , isCompeleted: true }, { id: 2 , text: 'Meeting with boss' , isCompeleted: true }, { id: 3 , text: 'Dentist app' , isCompeleted: false } ] console .log(todos[1 ].text)
轉換成JSON格式 JSON是一種資料的格式,再傳送資料給server的時候會用JSON這個格式傳跟接收,跟上面物件實字的格式有點像差別在於沒有(‘’)都是(“”),並且所以的key的部分都要加上雙引號(“”),值的部分除了數字也都要上(“”)
簡單操作把array轉換成JSON格式使用JSON.stringify
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const todos = [{ id: 1 , text: 'take out trash' , isCompeleted: true }, { id: 2 , text: 'Meeting with boss' , isCompeleted: true }, { id: 3 , text: 'Dentist app' , isCompeleted: false } ] const todoJSON = JSON .stringify(todos);console .log(todoJSON);
迴圈(Loops - for, while, for…of, forEach, map) For Loop 第一個分號之前initializes整個loop,同時定義binding 第二個部分check整個loop要不要繼續跑 最後一個部分更新整個loop狀態在每一次迴圈結束之後 1 2 3 for (let i = 0 ; i < 10 ; i++) { console .log(i); }
while Loop 其實跟for很像只是比較囉嗦一點
1 2 3 4 5 6 let i = 0 ;while (i < 10 ) { console .log(i); i++; }
陣列套用迴圈(loop through arrays) 使用length這個方法就可以取得check要不要跑得部分得數字並使用todos[i].text
印出文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const todos = [{ id: 1 , text: 'take out trash' , isCompeleted: true }, { id: 2 , text: 'Meeting with boss' , isCompeleted: true }, { id: 3 , text: 'Dentist app' , isCompeleted: false } ] for (let i = 0 ; i < todos.length; i++) { console .log(todos[i].text); }
for…of Loop 印出物件console.log(todo)
印出文字console.log(todo.text)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const todos = [{ id: 1 , text: 'take out trash' , isCompeleted: true }, { id: 2 , text: 'Meeting with boss' , isCompeleted: true }, { id: 3 , text: 'Dentist app' , isCompeleted: false } ] for (let todo of todos) { console .log(todo); } for (let todo of todos) { console .log(todo.text); }
High order array methods(forEach, map, filter) 這邊的high order arrays method都會把一個function當作參數放進去作執行
forEach 印出每一個()內的參數以及它的property或是方法
下方的例子會得到每個todo的text
1 2 3 todos.forEach(function (todo ) { console .log(todo.text); })
map map會產出一個新的array,下面的範例只產出包含text的array
1 2 3 4 5 const todoText = todos.map(function (todo ) { return todo.text; }) console .log(todoText);
filter 會有條件做篩選
下方的條件是todo.isComplete是否為true是的話則印出新的array來包含被篩選後的內容
1 2 3 4 5 const todoComplete = todos.filter(function (todo ) { return todo.isCompeleted === true ; }) console .log(todoComplete);
綜合使用 下方的例子要使用filter中有true的內容後map出只有text的內容
1 2 3 4 5 6 7 const todoComplete = todos.filter(function (todo ) { return todo.isCompeleted === true ; }).map(function (todo ) { return todo.text; }) console .log(todoComplete);
條件式(conditionals - if, ternary&switch if, else if, else的用法: 符合if的條件跑他的{}不然就往下走跑else if,都不符合則跑else
1 2 3 4 5 6 7 8 9 const x = 1 ;if (x === 10 ) { console .log('x is 10' ); } else if (x > 10 ) { console .log('x is greater than 10' ) } else { console .log('x is less than 10' ); }
邏輯 OR 運算子 (||) 只要有一格成立就可以跑下面的程式
1 2 3 4 5 const x = 4 ;const y = 11 ;if (x > 5 || y > 10 ) { console .log('x is more 10 or y is more than 10' ); }
邏輯 AND 運算子(&&) 並須兩邊都符合,所以下面的程式碼不會跑
1 2 3 4 5 const x = 4 ;const y = 11 ;if (x > 5 && y > 10 ) { console .log('x is more 10 or y is more than 10' ); }
三元 ternary 運算子(? :) 問號前面是條件 問號代表then 分號左邊代表條件式成立,右邊是不成立 所以我們看下方的程式可以這樣解釋:
color = 如果X大於10那麼則是red,沒有大於的話則是blue
1 2 3 4 5 const x = 11 ;const color = x > 10 ? 'red' : 'blue' ;console .log(color);
switch 像開關一樣哪個條件符合則使用哪一段程式碼,記得要加上break跳出迴圈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const x = 21 ;const color = x > 10 ? 'red' : 'blue' ;switch (color) { case 'red' : console .log('color is red' ); break ; case 'blue' : console .log('color is blue' ); break ; default : console .loe('color is not red or blue' ) break ; }
函式(function&arrow) 建構階段(可以預設參數,然後預設參數是可以被呼叫階段的參數取代) 呼叫階段(如果有預設參數則不需要放參數進去這邊) 1 2 3 4 5 function addNums (num1, num2 ) { console .log(num1 + num2); } addNums(1 , 2 );
1 2 3 4 5 function addNums (num1=1 , num2=2 ) { console .log(num1 + num2); } addNums(5 ,5 );
這樣的寫法會比較常見: 使用return帶入並且在console.log()處呼叫函式
1 2 3 4 5 function addNums (num1 = 1 , num2 = 2 ) { return num1 + num2; } console .log(addNums(5 , 5 ));
箭頭函式(arrow function) 會用創建變數的方式取代function字樣並且在{}前面加入箭頭(=>)
1 2 3 4 5 const addNums = (num1 = 1 , num2 = 1 ) => { return num1 + num2; } console .log(addNums(5 , 5 ));
如果後方的{}裡面只有一個表達式,甚至可以省略到 reutrn以及 {}(這邊要注意如果要加回來return跟{}要一起加不然會報錯) :
1 2 3 4 const addNums = (num1 = 1 , num2 = 1 ) => num1 + num2;console .log(addNums(5 , 5 ));
甚至當只有一個參數的時候連前面的()都可以省略:
1 2 3 4 const addNums = num1 => num1 + 5 ;console .log(addNums(5 ));
物件導向(OOP-prototype&classes)簡介 物件的構成及基本操作: 屬性(property) 這就好比車子的廠牌 大小 人的姓名年齡等等各種資訊 方法(method) 就像是物件的運行方式,車子的發動、煞車,人的吃飯睡覺行走等等 建構子(constructor): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function Person (firstName, lastName, dob ) { this .firstName = firstName; this .lastName = lastName; this .dob = dob; } const person1 = new Person('John' , 'Doe' , '4-3-1980' );const person2 = new Person('Mary' , 'Smith' , '3-6-1970' );console .log(person1.firstName);
new Date()
物件來呈現時間設定方式如下,使用new Date()
的方式設定就可以使用他的方法瞜!
console.log(person2.dob);
因為下方dob被設定好了日期所以會呈現出這些完整的資訊
1 2 3 4 5 6 7 8 9 10 11 12 function Person (firstName, lastName, dob ) { this .firstName = firstName; this .lastName = lastName; this .dob = new Date (dob); } const person1 = new Person('John' , 'Doe' , '4-3-1980' );const person2 = new Person('Mary' , 'Smith' , '3-6-1970' );console .log(person2.dob);
物件內的function 使用 在物件中建立getBirthYear這個funciton,然後在外面呼叫這個函式console.log(person1.getBirthYear())
console.log(person1.getFullName());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function Person (firstName, lastName, dob ) { this .firstName = firstName; this .lastName = lastName; this .dob = new Date (dob); this .getBirthYear = function ( ) { return this .dob.getFullYear(); } this .getFullName = function ( ) { return `${this .firstName} ${this .lastName} ` } } const person1 = new Person('John' , 'Doe' , '4-3-1980' );const person2 = new Person('Mary' , 'Smith' , '3-6-1970' );console .log(person1.getBirthYear());console .log(person1.getFullName());
把物件的function放到prototype 上面有介紹到了建構function在物件裡面的方法,這邊這個方法比較泛用把function放到prototype裡面
這樣使用的原因是不是每個物件都要使用方法所以直接往外拉出來需要的再去抓取使用就好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function Person (firstName, lastName, dob ) { this .firstName = firstName; this .lastName = lastName; this .dob = new Date (dob); } Person.prototype.getFullName = function ( ) { return `${this .firstName} ${this .lastName} ` } Person.prototype.getBirthYear = function ( ) { return this .dob.getFullYear(); } const person1 = new Person('John' , 'Doe' , '4-3-1980' );const person2 = new Person('Mary' , 'Smith' , '3-6-1970' );console .log(person1.getBirthYear());console .log(person1.getFullName());
ES6 -class 得到的結果跟上方一模一樣只是更好閱讀不同語言的使用者也更看的懂
這就是語法糖(不一樣的包裝一樣的內容物)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person { constructor (firstName, lastName, dob ) { this .firstName = firstName; this .lastName = lastName; this .dob = new Date (dob); } getBirthYear ( ) { return this .dob.getFullYear(); } getFullName ( ) { return `${this .firstName} ${this .lastName} ` ; } } const person1 = new Person('John' , 'Doe' , '4-3-1980' );const person2 = new Person('Mary' , 'Smith' , '3-6-1970' );console .log(person1.getBirthYear());console .log(person1);
DOM選取器介紹 (DOM Selection) 操作選取的物件就是DOM在做的事情
document就是HTML檔案也通常是DOM作用的地方
取得單一的元素 getElementById
1 2 3 console .log(document .getElementById('my-form' ))
把抓取到的元素指派給變數
1 2 3 const form = document .getElementById('my-form' );console .log(form);
比較新的用法querySeletor
可以抓取任何物件、class、id、各種tag,但是他是單一的選取器所以只會取得第一個就算他是負數
1 2 console .log(document .querySelector('h1' ))
取得複數的元素 querySeletorAll
作者建議都使用這個因為其他方法比較舊
他抓取出來的物件很像array
1 2 console .log(document .querySelectorAll('.item' ))
也可以使用array的一些方法如forEach
但不是全部畢竟他不是array
1 2 3 const items = document .querySelectorAll('.item' );items.forEach((item ) => console .log(item));
DOM操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const ul = document .querySelector('.items' );ul.remove(); ul.lastElementChild.remove(); ul.firstElementChild.textContent = 'Hello' ul.children[1 ].innerHTML = 'Brad' ul.lastElementChild.innerHTML = '<h1>HELLO<h1>' ; const btn = document .querySelector('.btn' );btn.style.background = 'red' ;
事件監聽addEventListener
建立事件監聽的方法如下:
在addEventListener後面的參數(事件,事件發生時執行的函式)
印出點擊這個事件
1 2 3 4 5 6 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); console .log('click' ) })
印出所有事件的屬性(attribute)
1 2 3 4 5 6 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); console .log(e) })
console.log(e.target)
這個屬性可以印出e所taget地方的元素像這邊就是點擊哪邊就印出哪邊的元素
1 2 3 4 5 6 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); console .log(e.target) })
console.log(e.target.className)
可以繼續使用屬性像這邊就可以取得className
1 2 3 4 5 6 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); console .log(e.target.className) })
DOM實例 實作一個小小的提交表
點擊送出的時候會在下方產生區塊顯示剛剛submit的內容
HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <body> <header> <h1>JS For Beginners</h1> </header> <section class="container"> <form id="my-form"> <h1>Add User</h1> <div class="msg"></div> <div> <label for="name">Name:</label> <input type="text" id="name"> </div> <div> <label for="email">Email:</label> <input type="text" id="email"> </div> <input class="btn" type="submit" value="Submit"> </form> <ul id="users"></ul> <!-- <ul class="items"> <li class="item">Item 1</li> <li class="item">Item 2</li> <li class="item">Item 3</li> </ul> --> </section> <script src="main.js"></script> </body>
教學一些小技巧 點擊時產生效果 點擊前
點擊後
事件(Evemt) 1 2 3 4 5 6 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); document .querySelector('#my-form' ).style.background = '#ccc' })
使用classList.add()
裡面放入預先做好的class內容就可以在點擊的時候跳出這個被景色
1 2 3 4 5 6 7 8 const btn = document .querySelector('.btn' );btn.addEventListener('click' , (e ) => { e.preventDefault(); document .querySelector('#my-form' ).style.background = '#ccc' document .querySelector('body' ).classList.add('bg-dark' ) })
改變文字內容當點擊的時候
1 2 document .querySelector('.items' ).lastElementChild.innerHTML = '<h1>hello</h1>'
正式內容:
完成品
第一步把所有的DOM elements轉換成變數
1 2 3 4 5 6 7 8 const myForm = document .querySelector('#my-form' );const nameInput = document .querySelector('#name' );const emailInput = document .querySelector('#email' );const msg = document .querySelector('.msg' );const userList = document .querySelector('#users' );
加入事件監聽,並建立變數指派給onSubmit這個function並把其內容寫在下方
1 2 3 myForm.addEventListener('submit' , onSubmit);
這邊因為想要取得名字的值
可以使用 想要取得值的 變數.value
1 2 3 4 5 6 function onSubmit (e ) { e.preventDefault(); console .log(nameInput.value); }
事件的驗證(Event Validation) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function onSubmit (e ) { e.preventDefault(); if (nameInput.value === '' || emailInput.value === '' ) { msg.classList.add('error' ); msg.innerHTML = 'Please enter all fields' ; setTimeout (() => msg.remove(), 3000 ); } else { const li = document .createElement('li' ); li.appendChild(document .createTextNode(`${nameInput.value} : ${emailInput.value} ` )); userList.appendChild(li); nameInput.value = '' ; emailInput.value = '' ; } }