iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 2
15
Modern Web

從 Hooks 開始,讓你的網頁 React 起來系列 第 2

[Day 02] React 中一定會用到的 JavaScript 語法

感謝 iT 邦幫忙與博碩文化,本系列文章已出版成書「從 Hooks 開始,讓你的網頁 React 起來」,首刷版稅將全額贊助 iT 邦幫忙鐵人賽,歡迎前往購書,鼓勵筆者撰寫更多優質文章。

在開始撰寫 React 之前,需要讓大家有一個比較統一的開發環境,如此可以避免因為作業系統、環境版本、React 版本的差異造成影響。一開始在專案還沒有很複雜前,我們會使用 Codepen 來練習基本的 React 概念,等到專案更複雜之後,我們會再使用其他開發工具。

註冊 CodePen 帳號

Codepen 這個工具很容易上手,其中一側可以編輯 HTML、CSS 和 JS,另一側會顯示對應的畫面。之後幾天我們會持續用 CodePen 進行練習,所以如果還沒註冊帳號的話,可以趕快來註冊一個:

Imgur

學習/複習 React 中一定會用到的 JavaScript 語法

在過幾天進入到 React 的內容後,只會提供 JavaScript 語法的關鍵字讓大家進一步到 MDN 查找資料,因此趁著今天先來簡單學習/複習和 React 有關的 JavaScript 語法。

如果覺得今天的內容太多一時無法吸收也不用擔心,後面幾天會實際把這些 JavaScript 語法應用在 React 的專案中,到時候在「使用關鍵字 + MDN」去查詢並學習理解即可:

Imgur

如果需要的話,你可以在 CodePen 中開啟一個新的 Pen 搭配練習,只需要把 JavaScript 語法撰寫在 JS 的區塊中,接著打開瀏覽器的 console 介面作為練習:

Imgur

像是這樣:

Imgur

如果覺得今天的內容太多一時無法吸收也不用擔心,後面幾天會實際把這些 JavaScript 語法應用在 React 的專案中,到時候在「使用關鍵字 + MDN」去查詢並學習理解即可。

現在就讓我們開始吧!

樣板字面值(Template literals/Template strings)

「樣板字面值」方便我們可以直接在字串中帶入 JavaScript 表達式(expression),使用上只需要將原本字串的內容用反引號(鍵盤 1 左邊那個)包起來,需要帶入表達式的地方在使用 ${} 帶入即可。

實際的應用像是這樣:

// 帶入變數
const favoritePhone = 'iPhone';

console.log(`I want to buy the ${favoritePhone}`); // I want to buy the iPhone

也可以帶入其他表達式:

// 帶入加減數值運算
const favoritePhone = 'Galaxy Note';
const currentPrice = 31900;

console.log(`The ${favoritePhone} is ${currentPrice} now.`);
// The Galaxy Note is 31900 now.


console.log(`The ${favoritePhone} is ${currentPrice * 0.7} now.`);
// The Galaxy Note is 22330 now.

「表達式」簡單來說就是當你給它一段程式碼去執行後,它會直接回傳一個值給你。若還不太清楚 statements 和 expressions 的差異,可以參考 [筆記] 進一步談JavaScript中函式的建立─function statements and function expressions

箭頭函式(arrow functions)

箭頭函式是在 ES6 中另一種更簡便來定義函式的語法。傳統上我們會這樣定義 JavaScript 的函式:

function showIphonePrice(currentPrice) {
  return `The iPhone is ${currentPrice} now.`;
}

console.log(showIphonePrice(26900));
// The iPhone is 26900 now.

在 ES6 中則可以用箭頭函式讓它變得更精簡。箭頭函式的使用會把函式的參數放在前面的小括號 () 中,中間搭配 =>,原本函式執行的內容則放在最後的大括號 {} 中:

const showIphonePrice = (currentPrice) => {
  return `The iPhone is ${currentPrice} now.`;
};

console.log(showIphonePrice(26900));
// The iPhone is 26900 now.

如果在箭頭函式中只會回傳一個值而不需要做其他的操作的話,甚至可以精簡成這樣:

const showIphonePrice = currentPrice => `The iPhone is ${currentPrice} now.`;

console.log(showIphonePrice(26900));
// The iPhone is 26900 now.

物件屬性名稱縮寫(Shorthand property names)

當物件的屬性值是一個變數,而屬性名稱又和該變數名稱相同時可以縮寫偷懶,寫出屬性名稱即可。什麼意思呢?來看下面這個例子。

首先定義了三個變數,分別是 deviceNamecurrentPricestorage,現在想要把這三個變數當成物件 galaxyNote 的屬性值,原本會這樣寫:

const deviceName = 'Galaxy Note';
const currentPrice = 30900;
const storage = '256G';

const galaxyNote = {
  deviceName: deviceName,
  currentPrice: currentPrice,
  storage: storage,
};

console.log(galaxyNote.deviceName);    // Galaxy Note

但由於物件的「屬性名稱」和當成屬性值的「變數名稱」相同,因此可以縮寫成這樣子:

// 當物件的「屬性名稱」和當成屬性值的「變數名稱」相同時,可以縮寫成這樣
const deviceName = 'Galaxy Note';
const currentPrice = 30900;
const storage = '256G';

const galaxyNote = {
  deviceName,
  currentPrice,
  storage,
};

console.log(galaxyNote.deviceName);   // Galaxy Note

解構賦值(Destructuring assignment)

解構賦值可以幫助我們用簡短的語法,從物件或陣列中取出所需要的資料,並建立成新的變數。

簡單來說,解構賦值讓開發者可以達到快速建立變數並取值的動作

物件的解構賦值

常見的情況時,當從伺服器拿到的資料是帶有一大包內容的物件,而我們只需要用到該物件裡面的其中一些屬性,這時就很適合使用解構賦值。

舉例來說,現在從伺服器拿到一大包和商品名稱有關的資料:

// 一個帶有非常多資料的物件
const product = {
  name: 'iPhone',
  image: 'https://i.imgur.com/b3qRKiI.jpg',
  description:
    '全面創新的三相機系統,身懷萬千本領,卻簡練易用。電池續航力突飛猛進,前所未見。令你大為驚豔的晶片更加碼機器學習技術,並突破智慧型手機所能成就的極限。第一部威力強大,Pro 如其名的 iPhone,全新登場。',
  brand: {
    name: 'Apple',
  },
  aggregateRating: {
    ratingValue: '4.6',
    reviewCount: '120',
  },
  offers: {
    priceCurrency: 'TWD',
    price: '26,900',
  },
};

如果現在要建立新的變數,並在變數中代入該商品的名稱(name)和描述(description),傳統上可能會這樣做:

/* 一般從物件取出屬性值,並建立新變數的做法 */
const name = product.name;
const description = product.description;

在使用物件的解構賦值之後,可以達到快速建立變數並取值的動作

/* 物件的解構賦值 */

// 自動產生名為 name 和 description 的變數
// 並把 product 物件內的 name 和 description 當作變數的值
const { name, description } = product;

console.log(name);         // iPhone
console.log(description);  // 全面創新的三相機系統,身懷萬千本領,卻簡練易用。...

進階:透過解構賦值取出物件中的物件

同時你可能好奇,像現在 product 物件中還有 offers 這個物件,如果要取得 offers 物件內的 price 一樣可以透過解構賦值取出嗎?

是可以的,寫法會像這樣,意思就是要定義一個名為 price 的變數,它的值會是 product.offers.price。但要注意的是,現在就沒有新增 offers 這個變數了:

const {
  offers: { price },
} = product;

console.log(price);    // 26,900
console.log(offers);   // ReferenceError: offers is not defined

如果同時需要建立 offersprice 這兩個變數,有時會這樣寫。先透過解構賦值先取出 offers,接著一樣透過解構賦值再從 offers 中取出 price

const { offers } = product;    // 透過解構賦值先從 product 取出 offers
const { price } = offers;      // 透過解構賦值再從 offers 中取出 price

console.log(price);   // 26,900
console.log(offers);  // { priceCurrency: 'TWD', price: '26,900' }

陣列的解構賦值

陣列同樣也有解構賦值的寫法,這在 React Hooks 中經常被使用。

直接看程式碼會比較清楚,假設伺服器回傳一個陣列中包含各種廠牌的智慧型手機,而且陣列中元素的順序是照當年度銷售排行:

const mobileBrands = [
  'Samsung', 'Apple', 'Huawei', 'Oppo',
  'Vivo', 'Xiaomi', 'LG', 'Lenovo', 'ZTE'
];

如果想要取得當年度排行前三名的手機,並建立成變數,傳統上可能需要這樣寫:

/* 一般從陣列取出元素值並建立新變數的做法 */
const best = mobileBrands[0];    // Samsung
const second = mobileBrands[1];  // Apple
const third = mobileBrands[2];   // Huawei

這時同樣可以使用解構賦值來達到快速建立變數並取值的動作,陣列的解構賦值會依據陣列內元素的順序把值取出來,像是這樣:

/* 陣列的解構賦值 */

// 自動建立名為 best、second、third 的變數
// 並把 mobileBrands 陣列中的第一、第二和第三個元素當作變數的值帶入
const [best, second, third] = mobileBrands;

console.log(best);     // Samsung
console.log(second);   // Apple
console.log(third);    // Huawei

展開語法和其餘語法(Spread Syntax/Rest Syntax)

最後一個同樣經常在 React 中會使用到的語法,稱作「展開語法(Spread Syntax)」和「其餘語法(Rest Syntax)」。這兩個語法的使用時機不同,但在寫程式時是用相同的文字來表達,也就是 ...。

沒錯,就是 ...,這個 ... 可以同樣可以使用於物件和陣列上。

展開語法(spread syntax)

展開語法最常用在要「複製一個物件,並為該物件添加一些屬性時」,舉例來說,我們先定義智慧型手機的基本屬性:

// 定義一個物件
const mobilePhone = {
  name: 'mobile phone',
  publishedYear: '2019',
};

接下來如果要複製 mobilePhone 這個物件變成一個新的物件(iPhone),同時添加一些屬性時,可以使用 ... 展開語法:

/* 展開語法(spread syntax) */

const iPhone = {
  ...mobilePhone,
  name: 'iPhone',
  os: 'iOS',
};

console.log(iPhone);  // { name: 'iPhone', publishedYear: '2019', os: 'iOS' }

這裡我們定義了一個新的物件,稱作 iPhone,接著把原本 mobilePhone 的內容全部複製一份進去,最後再添加 nameos 這兩個屬性進去。

可以注意到,在原本的 mobilePhone 就已經有 name 這個屬性,後來我們又添加同樣名為 name 的屬性時,就會把原本的 name 給覆蓋掉;而 os 這個屬性,因為在原本的 mobilePhone 中沒有這個屬性,所以就會直接被添加到新的 iPhone 這個物件內。

我習慣把「展開語法」的 ... 當作「解壓縮」的概念,就是把原本的物件,解開來,再放進去新的物件裡面,同時還可以添加一些新的屬性。

展開語法同樣可以用來複製陣列,並在新的陣列中添加元素,像是這樣:

/* 展開語法(spread syntax) */

const mobilesOnSale = ['Samsung', 'Apple', 'Huawei'];
const allMobiles = [...mobilesOnSale, 'Oppo', 'Vivo', 'Xiaomi'];

console.log(allMobiles); // [ 'Samsung', 'Apple', 'Huawei', 'Oppo', 'Vivo', 'Xiaomi' ]

其餘語法(rest syntax)

其餘語法和展開語法的寫法一樣,都是 ...,但是使用時機不太一樣,如果說展開語法像是「解壓縮」,那麼其餘語法就像是「壓縮」。它可以把在解構賦值中沒有被取出來的物件屬性或陣列元素都放到一個壓縮包裡。

舉例來說,在前面談到物件解構賦值的時候,從伺服器拿到一大包資料:

const product = {
  name: 'iPhone',
  image: 'https://i.imgur.com/b3qRKiI.jpg',
  description:
    '全面創新的三相機系統,身懷萬千本領,卻簡練易用。電池續航力突飛猛進,前所未見。令你大為驚豔的晶片更加碼機器學習技術,並突破智慧型手機所能成就的極限。第一部威力強大,Pro 如其名的 iPhone,全新登場。',
  brand: {
    name: 'Apple',
  },
  aggregateRating: {
    ratingValue: '4.6',
    reviewCount: '120',
  },
  offers: {
    priceCurrency: 'TWD',
    price: '26,900',
  },
};

雖然我們取出了所需的資料,但其餘剩下沒有取出來的物件屬性仍想要取出來稍後使用,這時就可以使用其餘語法,把除了 namedescription 剩下沒有取出來的物件屬性,都放到取名為 other 的變數中(變數名稱可以自己取),如此 other 就會是 product 物件中,扣除掉 namedescription 屬性後的所有其餘資料:

/* 物件解構賦值時使用其餘語法 */
const { name, description, ...other } = product;
console.log(other);

// {
//   image: 'https://i.imgur.com/b3qRKiI.jpg',
//   brand: { name: 'Apple' },
//   aggregateRating: { ratingValue: '4.6', reviewCount: '120' },
//   offers: { priceCurrency: 'TWD', price: '26,900' }
// }

其餘語法同樣可以用在陣列中,前面我們有把銷量前三名的手機取出來,那其餘剩下沒被取出來的怎麼辦呢?這時候就可以使用「其餘語法」把剩下的陣列元素全都拿出來,像是這樣:

/* 陣列解構賦值時使用其餘語法 */

const mobileBrands = [
  'Samsung', 'Apple', 'Huawei', 'Oppo',
  'Vivo', 'Xiaomi', 'LG', 'Lenovo', 'ZTE'
];

// 變數名稱不一定要取名為 other
const [best, second, third, ...other] = mobileBrands;
console.log(other);   // [ 'Oppo', 'Vivo', 'Xiaomi', 'LG', 'Lenovo', 'ZTE' ]

這些 JavaScript 全都要會才能繼續看下去嗎

如果上面提到許多 JavaScript 語法,你都覺得非常陌生的話先不會擔心,因為如果你是第一次吸收這些語法的話,一次塞這麼多東西反而會很難吸收。建議可以等到後面開始實作 React 的時候,你一定還會看到這些語法,等到那時候再來慢慢搭配關鍵字到 MDN 查詢這些語法就可以了。

參考資源


上一篇
[Day 01] 沒學過 React 可以從 Hooks 開始嗎?
下一篇
[Day 03 - 計數器] 用原生 JavaScript 做一個簡單的計數器
系列文
從 Hooks 開始,讓你的網頁 React 起來30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

6
hannahpun
iT邦新手 4 級 ‧ 2019-10-31 01:40:19

實用推!! 一篇複習 es6 基本語法!! 覺得解構賦值真心好用
以前想要改變陣列位置兩兩對調還要先把值拉出來存,因為陣列是 by reference
但解構負值一行搞定 !

let a = ['a', 'b', 'c', 'd'];
// Want a to b, b to a

function position( ind1, ind2){
    [a[ind1], a[ind2]] = [a[ind2], a[ind1]]
}
position(0,1);  // ['b', 'a', 'c', 'd']
pjchender iT邦新手 3 級 ‧ 2019-11-05 11:20:03 檢舉

這個好用耶!很容易就把陣列元素交換順序!

9
hannahpun
iT邦新手 4 級 ‧ 2020-11-01 15:00:33

重新複習這一系列,然後想補充 "其餘語法(rest syntax)" 那邊假如硬要同時抓 offersprice 其實也可以

const {
  offers,
  offers: { price },
} = product;

offers; // {priceCurrency: "TWD", price: "26,900"}
price; // "26,900"

WILL.I.AM iT邦新手 3 級 ‧ 2022-08-10 11:41:08 檢舉

女神!

我要留言

立即登入留言