跳至主要内容

Frontend 2(JS)

RE:從 0 開始的 JS

Min


ECMAScript?


ECMAScript 是 JavaScript 的標準,目的是讓不同瀏覽器之間能根據 spec 來實作。當 ECMAScript 發布第三版 (即 ES3) 之後,成為當時所有瀏覽器支援的程式語言。


而各家瀏覽器就會依照 ECMAScript 的標準,去實作 JavaScript 的 runtime(執行環境)。


而 ECMAScript 會隨著時代演進而更新內容(例如新增語法、功能),所以可能會聽到 ES5、ES6 之類的,那指的就是 ECMAScript 的第幾個版本


NodeJS

是一個可以跑在伺服端的 Javascript 執行環境,可以讓 JS 直接在電腦上執行,不用透過瀏覽器。


來下載一下ㄅ

ubuntu

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

Windows 其他的可以看這裡


順便把 yarn 也裝一裝ㄅ

# ----ubuntu----
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get install yarn
# ----homebrew----
brew install yarn
# ----arch----
pacman -S yarn

Windows


那該怎麼用呢


直接 node index.js


可以先建一個空白的 index.js 然後在裡面輸入 console.log("Hello") 接著在終端機下 node index.js


基本語法篇


先從印出東西開始吧


console.log()


console.log("Hello World")

變數與資料型別


  • var 作用域是「函式(function)」
  • let ES6 新增的,作用域是「區塊(block)」
  • const 用來宣告常數的

先試試看ㄅ

var a = 123;
let b = "123"; //盡量用這個
const c = true; //宣告後,就不能再被改了

console.log(a);
console.log(b);
console.log(c);

變數名稱的限制:

  1. 第一個字元必須是字母 (大小寫皆可) 或是底線 (_),之後的字元,可以是數字、字母或底線。
  2. 區分大小寫、 var XYZ 並不等於 var xyz。
  3. 變數名稱不能用 JavaScript 的保留字。

資料型態


動態型別

let a = 123;
console.log(typeof a);
a = "123";
console.log(typeof a);

關係運算子



console.log("123" == 123) //true
console.log("123" === 123) //false
console.log(123 != "123") //false
console.log(123 !== "123") //true


if...else


語法

if (condition1)
statement1
else if (condition2)
statement2
else if (condition3)
statement3
...
else
statementN

三元運算子

condition ? exprIfTrue : exprIfFalse

範例

let a = true ? "apple" : "pineapple";
console.log(a); //apple

for and while


for 迴圈

for (let i = 0; i < 5; i++) {
console.log(i);
}

while 迴圈

let i = 0;
while (i < 5) {
console.log(i);
i++;
}

function


語法

// 第一種
function functionName(parameter1, parameter2, ...) {
// statements

return value;
}
// 第二種
let functionName = function(parameter1, parameter2, ...) {
// statements

return value;
}

// 箭頭函式
let functionName = (parameter1, parameter2, ...) => {
// statements

return value;
}
// 如果只有一個參數的話
// 括號可以省略
let functionName = x => {

}

延伸閱讀-JS 的 this


array


語法

let a = ["Apple", "Banana", "Pineapple"];
let b = [1, 2, 3];
let c = [4, "Berry", true];

想把東西丟進去?

let a = [1, 2, 3];
// 從尾端
a.push(4) // [1, 2, 3, 4]
// 從開頭
a.unshift(5); // [5, 1, 2, 3, 4]

那想拿出來呢

let a = [1, 2, 3, 4];
// 從尾端
a.pop() // 4
// 從開頭
a.shift(); // 1

object


語法

let obj = {
key1: value1,
key2: value2,
...
};

let person = {
name: "Min",
country: "Taiwan",
gender: "male",
};

// 取得物件裡的值
person.name
person["name"] // Min

// 修改物件裡的值
person.name = "FKT"
person["name"] = "FKT"
console.log(person.name) //FKT

// 在物件裡新增屬性
person.department = "資管系"
person["department"] = "資管系"
console.log(person)

// {
// name: "FKT",
// country: "Taiwan",
// gender: "male",
// department: "資管系",
// };

進階篇


高階函式


forEach

const fruits = ['Apple', 'Pineapple', 'Cherry'];
// forEach
fruits.forEach(function(item) {
console.log(item);
});

// 等於
for (let i = 0; i < fruits.length; i++) {
const item = fruits[i];
console.log(item);
}

map

let numList = [1, 2, 3, 4, 5];
// map會回傳一個陣列
let array = numList.map(function (item) {
return item + 1;
});
// [2, 3, 4, 5, 6]

// 等於
let array = [];
for (let i = 0; i < numList.length; i++) {
const item = numList[i];
array.push(item + 1);
}

filter

const words = ["spray", "limit", "elite", "exuberant", "destruction", "present", "happy"];
let longWords = words.filter(word => word.length > 6);
// ["exuberant", "destruction", "present"]

// 等於
let longWords = [];
for (let i = 0; i < words.length; i++){
const word = words[i];
if (word.length > 6){
longWords.push(word);
}
}

Spread Operator &
Rest Operator


Spread Operator

把一個陣列展開成個別的值的速寫語法

const params = [ "hello", true, 7 ]
const other = [ 1, 2, ...params ]
// [ 1, 2, "hello", true, 7 ]
function sum(a, b, c) {
return a + b + c
}
const args = [1, 2, 3]
sum(…args) // 6

Rest Operator

收集其餘的(剩餘的)這些值,轉變成一個陣列。它會用在函式定義的傳入參數識別名定義(其餘參數, Rest parameters),以及解構賦值時


function test(...numbers) {
console.log(numbers)
}

test(1) // [1]
test(1, 2, 3, 4, 5) // [1, 2, 3, 4, 5]

function aFunc(x, ...y){
console.log('x =', x, ', y = ' , y)
}

aFunc(1,2,3) //x = 1, y = [2, 3]
aFunc() //x = undefined, y = []

其餘參數在傳入參數定義中,只能位於最後一位, 並且在參數中只能有一個其餘參數。


解構賦值


陣列解構

const x = [1, 2, 3, 4, 5];
const [y, z] = x;
console.log(y); // 1
console.log(z); // 2

// 結合rest operator
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(a); // 10
console.log(b); // 20
console.log(rest); // [30, 40, 50]

物件解構

const person = {
name: "Min",
studentID: "B10923012",
skills: {
frontend: ["HTML", "JS", "CSS"],
backend: ["flask", "express", "docker"]
}
};

const { studentID } = person; // "B10923012"
const { skills: { frontend } } = person;
// ["HTML", "JS", "CSS"]

模板語法


語法

用「`」括起來,就可以使用 ${...} 來加入變數或函式

let firstName = "John";
let lastName = "Doe";

let text = `Welcome ${firstName}, ${lastName}!`;

補充說明

這部分可以先跳過,有興趣再聽,後面有練習題可以先寫


var && let

前面說過 var 的作用域是函式(function),而 let 是區塊(block)。 那作用域到底是什麼呢


{ // block
var a = "apple";
let b = "banana";
console.log(a); // apple
console.log(b); // banana
}
console.log(a); // apple
console.log(b); // ReferenceError: b is not defined

// var
function varTest(){
var i = "Hello World"
console.log(i) // "Hello World"
}
console.log(i); //ReferenceError: i is not defined

// let
function letTest() {
let j = "Hello World"
console.log(j) // "Hello World"
}
console .log(j) //ReferenceError: j is not defined

var 可以被重複宣告,let 會報錯

var a = "123"
var a = "456"

let b = "123"
let b = "456" //SyntaxError: Identifier 'b' has already been declared

hoisting(提升)?


如果我們試圖對未宣告的變數取值,node 會報錯

console.log(a) // ReferenceError: x is not defined

那如果我們在使用後再宣告呢

console.log(b) //猜猜看結果
var b = 123
console.log(b) //猜猜看結果

Answer:

  • undefined
  • 123

在 JavaScript 中,不管你在函數中的哪一行用 var 宣告變數,一律視為在函數的第一行宣告,這個行為就叫做 hoisting(提升)。 要注意的是,只有「宣告」這個動作有 hoisting 的特性,賦值 (把值指定給變數) 的動作不會 hoisting。


所以剛剛的範例可以看成這樣

var b;
console.log(b);
b = 123;
console.log(b);

在 let 裡就有點不一樣了 似乎 let 不會 hoisting...嗎

console.log(b); //ReferenceError: Cannot access 'b' before initialization
let b = 123;
console.log(b);

嗯?

let x = "outer value";
{
console.log(x); //ReferenceError: Cannot access 'x' before initialization
let x = "inner value";
}

TDZ(暫時死區)

let/const 中也存在變數提升的,但是他還有另一個概念「暫時死區」,也就是如果在宣告變數之前使用變數,這個變數就是存在「暫時死區」中無法存取!這時候使用它就會報錯 ReferenceError。


懶人包


最後ㄉ小練習

遊戲機制 小天玩射擊遊戲,原本積分 7000 分,他想知道經過今天對戰後最後總積分多少, 但是只知道小天今天戰績(名次,擊殺數): (13,1), (1,8), (2,5), (4,3), (1,6)


  1. 協助小天依照表格(遊戲機制說明),使用 calcScore 函式計算每一場取得的分數 calcScore(13,1) -> 10 calcScore(1,8) -> ? calcScore(2,5) -> ? calcScore(4,3) -> ? calcScore(1,6) -> ?

  2. 印出文字 "總共對戰 ... 次,目前總分 ... 分,最後一場排名第 ... 名。"


附上我寫的範例

let allScore = 7000; //初始分數
// 戰績
let records = [
{ rank: 13, kill: 1 },
{ rank: 1, kill: 8 },
{ rank: 2, kill: 5 },
{ rank: 4, kill: 3 },
{ rank: 1, kill: 6 }
];

function calcScore({ rank, kill }) { //用解構賦值拿出rank跟kill
//如果人數大於6則以6計算
const maxKill = kill > 6 ? 6 : kill;
// 基本的分數加成
let bonus = 10;
if (rank === 1) {
bonus = 25;
} else if (rank === 2 || rank === 3) {
bonus = 20;
} else if (rank === 4 || rank === 5) {
bonus = 15;
} else if (rank >= 6 && rank <= 10) {
bonus = 12;
}
// 算出得分
return maxKill * bonus;
}

for (let i = 0; i < records.length; i++) {
// 計算分數並加上
allScore += calcScore(records[i]);
}

console.log(
`總共對戰 ${records.length} 次,目前總分 ${allScore} 分,最後一場排名第 ${records[records.length - 1]["rank"]} 名。`
);

延伸閱讀

重新認識 JavaScript JS 模組化 NodeJS 入門


結束~~~

記得禮拜五 18:00-18:30 在茶道教室(GA242)集合ㄛ