
這些天,React是最受歡迎的JavaScript庫之一。它可以用來建立動態和響應式的應用程式,允許更好的效能,並且可以很容易地擴充套件。底層邏輯是基於可以在不同情況下重複使用的元件,減少了多次編寫相同程式碼的需要。簡而言之,使用React你可以建立高效和強大的應用程式。
因此,現在是學習如何建立React應用程式的最佳時機。
然而,如果沒有對一些關鍵的JavaScript功能的紮實瞭解,構建React應用程式可能是困難的,甚至是不可能的。
出於這個原因,我們彙編了一份在開始使用React之前需要了解的JavaScript特性和概念清單。你越瞭解這些概念,你就越容易構建專業的React應用程式。
既然如此,下面是我們將在本文中討論的內容:
- JavaScript和ECMAScript
- 語句與表示式
- React中的不可變性(Immutability)
- 模板字串
- 箭頭函式
- 類
- 關鍵詞this
- 三元運算子
- 短路求值(最小化求值)
- 展開語法
- 解構賦值
- filter(), map(), and reduce()
- 匯出和匯入
JavaScript和ECMAScript
JavaScript是一種流行的指令碼語言,與HTML和CSS一起用於建立動態網頁。HTML用於建立網頁的結構,CSS用於建立其元素的樣式和佈局,而JavaScript則是用於向網頁新增行為的語言,即建立功能和互動性。
✍️ JavaScript是由Netscape Communications的Brendan Eich在1995年開發的,旨在為Netscape Navigator瀏覽器的網頁增加互動性。
此後,該語言被各大瀏覽器採用,並編寫了一份檔案來描述JavaScript的工作方式:ECMAScript標準。
從2015年起,ECMAScript標準的更新每年都會發布,因此每年都會有新的功能加入到JavaScript中。
ECMAScript 2015是該標準的第六個版本,因此也被稱為ES6。之後的版本都是按進度標註的,所以我們把ECMAScript 2016稱為ES7,ECMAScript 2017稱為ES8,以此類推。
由於標準中新增新功能的頻率,有些功能可能不被所有瀏覽器支援。那麼,你怎樣才能確保你新增到JS應用中的最新JavaScript功能在所有的網路瀏覽器中都能如期工作呢?
你有三個選擇:
- 等到所有主要的瀏覽器都提供對新功能的支援。但如果你的應用程式絕對需要那個令人驚奇的新JS功能,這不是一個選擇。
- 使用Polyfill,它是 “一段程式碼(通常是網路上的JavaScript),用於在不支援現代功能的舊瀏覽器上提供現代功能”(另見MDN網路文件)。
- 使用JavaScript轉碼器,如Babel或Traceur,將ECMAScript 2015+程式碼轉換成所有瀏覽器都支援的JavaScript版本。
語句與表示式
在構建React應用程式時,理解語句和表示式之間的區別是至關重要的。因此,讓我們暫時回到程式設計的基本概念。
一個計算機程式是一個由計算機執行的指令列表。這些指令被稱為語句。
與語句不同,表示式是產生一個值的程式碼片段。在語句中,表示式是返回一個值的部分,我們通常在等號的右邊看到它。
✍️ 語句是一個做某事的程式碼塊。
而:
✍️ 一個表示式是產生一個值的程式碼片段。
JavaScript語句可以是程式碼塊或程式碼行,通常以分號結尾或用大括號括起來。
下面是一個JavaScript語句的簡單例子:
document.getElementById("hello").innerHTML = "Hello World!";
上面的語句在一個 id="hello" 的DOM元素中寫下了 "Hello World!" 。
正如我們已經提到的,expessions產生一個值或者本身就是一個值。請看下面的例子:
msg = document.getElementById("hello").value;
document.getElementById("hello").value 是一個表示式,因為它返回一個值。
一個額外的例子應該有助於澄清表示式和語句之間的區別:
const msg = "Hello World!";
function sayHello( msg ) {
console.log( msg );
}
在上面的例子中
- 第一行是一個語句,其中
"Hello World!"是一個表示式、 - 函式宣告是一個語句,其中傳遞給函式的引數
msg是一個表示式、 - 在控制檯中列印訊息的那一行是一個語句,其中引數
msg也是一個表示式。
為什麼表示式在React中很重要
在構建React應用程式時,你可以在JSX程式碼中注入JavaScript表示式。例如,你可以傳遞一個變數,寫一個事件處理器或一個條件。要做到這一點,你需要在大括號中包含你的JS程式碼。
例如,你可以傳遞一個變數:
const Message = () => {
const name = "Carlo";
return <p>Welcome {name}!</p>;
}
簡而言之,大括號告訴你的轉碼器將大括號中的程式碼作為JS程式碼來處理。在開頭 <p> 標籤之前和結尾 </p> 標籤之後的所有內容都是正常的JavaScript程式碼。開頭 <p> 和結尾 </p> 標籤內的所有內容都被當作JSX程式碼處理。
下面是另一個例子:
const Message = () => {
const name = "Ann";
const heading = <h3>Welcome {name}</h3>;
return (
<div>
{heading}
<p>This is your dashboard.</p>
</div>
);
}
你也可以傳遞一個物件:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22',
description: 'Content Writer'
}
return (
<div>
<h2>Welcome {person.name}</h2>
<img
className="card"
src={person.avatar}
alt={person.name}
/>
<p>Description: {person.description}.</p>
</div>
);
}
而下面是一個更全面的例子:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
description: 'Content Writer',
theme: {
boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', width: '200px'
}
}
return (
<div style={person.theme}>
<img
src={person.avatar}
alt={person.name}
style={ { width: '100%' } }
/>
<div style={ { padding: '2px 16px' } }>
<h3>{person.name}</h3>
<p>{person.description}.</p>
</div>
</div>
);
}
注意 img 和 div 這兩個元素的 style 屬性中的雙大括號。我們用雙括號來傳遞兩個包含卡片和影象樣式的物件。

一個用React構建的卡片例項
你應該注意到,在上面的所有例子中,我們在JSX中包含了JavaScript表示式。
✍️ JSX只接受大括號中的JavaScript表示式。你不允許在你的JSX程式碼中寫語句。
這包括:
- 變數
- 帶引號的字串
- 函式呼叫
- 物件
- 條件性表示式
React中的不可變性(Immutability)
可變性和不變性是物件導向和函數語言程式設計的兩個關鍵概念。
不可變性是指一個值在被建立後不能被改變。當然,可變性的意思正好相反。
在Javascript中,基元值是不可變的,這意味著一旦一個基元值被建立,它就不能被改變。相反,陣列和物件是可變的,因為它們的屬性和元素可以被改變,而不需要重新分配一個新值。
在JavaScript中使用不可變的物件有幾個原因:
- 提高效能
- 減少了記憶體消耗
- 執行緒安全
- 更容易編碼和除錯
遵循不可變性的模式,一旦一個變數或物件被分配,它就不能被重新分配或改變。當你需要修改資料時,你應該建立它的副本並修改其內容,而不改變原來的內容。
不變性(Immutability)也是React的一個關鍵概念。
React文件指出:
類元件的狀態可以用 this.state 來表示。狀態欄位必須是一個物件。請不要直接改變狀態。如果你想改變狀態,用新的狀態呼叫 setState 。
每當一個元件的狀態改變時,React會計算是否重新渲染該元件並更新虛擬DOM。如果React沒有跟蹤之前的狀態,它就不能確定是否重新渲染元件。React文件對此提供了一個很好的例子。
我們可以使用哪些JavaScript特性來保證React中狀態物件的不變性?
宣告變數
在JavaScript中,你有三種方法來宣告變數: var 、 let 和 const 。
var 語句從JavaScript的開始就存在。它被用來宣告一個函式範圍或全域性範圍的變數,可以選擇將其初始化為一個值。
當你使用 var 宣告一個變數時,你可以在全域性和區域性範圍內重新宣告和更新該變數。下面的程式碼是允許的:
// Declare a variable var msg = "Hello!"; // Redeclare the same variable var msg = "Goodbye!" // Update the variable msg = "Hello again!"
var 的宣告在任何程式碼執行之前都會被處理。因此,在程式碼中的任何地方宣告一個變數都等同於在頂部宣告它。這種行為被稱為變數提升(hoisting)。
值得注意的是,只有變數宣告被提升,而不是初始化,初始化只發生在控制流到達賦值語句的時候。在那之前,該變數是 undefined 的:
console.log(msg); // undefined var msg = "Hello!"; console.log(msg); // Hello!
在JS函式中宣告的 var 的範圍是該函式的整個主體。
這意味著該變數不是在塊級定義的,而是在整個函式級定義的。這導致了一些問題,可能會使你的JavaScript程式碼出現錯誤並難以維護。
為了解決這些問題,ES6引入了 let 關鍵字。
let declaration宣告瞭一個block-scoped的區域性變數,可以選擇將其初始化為一個值。
與 var 相比, let 的優點是什麼?這裡有一些:
let宣告一個變數到一個塊語句的範圍內,而var宣告一個變數到整個函式的全域性或區域性,與塊範圍無關。- 全域性
let變數不是window物件的屬性。你不能用window.variableName訪問它們。 -
let只能在其宣告到達後被訪問。在控制流到達它被宣告的那行程式碼之前,該變數不會被初始化(let宣告是non-hoisted)。 - 用
let重新宣告一個變數會產生一個SyntaxError。
由於使用 var 宣告的變數不能被block-scoped,如果你在一個迴圈中或 if 語句中使用 var 定義一個變數,它可以從block之外被訪問,這可能導致程式碼的bug。
第一個例子中的程式碼執行起來沒有錯誤。現在在上面看到的程式碼塊中用 let 代替 var :
console.log(msg); let msg = "Hello!"; console.log(msg);
在第二個例子中,使用 let 而不是 var 產生了一個 Uncaught ReferenceError:

Chrome瀏覽器中未捕獲的引用錯誤
✍️ 因此,作為一般規則,你應該總是使用 let 而不是 var 。
ES6還引入了第三個關鍵字: const 。
const 與 let 非常相似,但有一個關鍵區別:
✍️ 用 const 宣告的變數不能被賦值,除非是在它們被宣告的地方。
請看下面的例子:
const MAX_VALUE = 1000; MAX_VALUE = 2000;
上述程式碼將產生以下型別錯誤:

谷歌瀏覽器中的Uncaught TypeError: Assignment to constant variable
此外:
✍️ 你不能宣告一個 const 而不給它一個值。
宣告一個 const 而不給它一個值,會產生以下 SyntaxError (參見ES6 in Depth:let和const):

谷歌瀏覽器中的Uncaught SyntaxError: Missing initializer in const declaration
✍️ 未發現的語法錯誤: 在Chrome中常量宣告中缺少初始化器
但是如果一個常量是一個陣列或一個物件,你就可以編輯該陣列或物件中的屬性或專案。
例如,你可以改變、增加和刪除陣列專案:
// Declare a constant array
const cities = ["London", "New York", "Sydney"];
// Change an item
cities[0] = "Madrid";
// Add an item
cities.push("Paris");
// Remove an item
cities.pop();
console.log(cities);
// Array(3)
// 0: "Madrid"
// 1: "New York"
// 2: "Sydney"
但你不允許重新分配陣列:
const cities = ["London", "New York", "Sydney"]; cities = ["Athens", "Barcelona", "Naples"];
上面的程式碼會導致一個TypeError。

谷歌瀏覽器中的Uncaught TypeError: Assignment to constant variable
你可以新增、重新分配和刪除物件的屬性和方法:
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language',
content: 'JavaScript is a scripting language that enables you to create dynamically updating content.'
};
// add a new property
post.slug = "javascript-is-awesome";
// Reassign property
post.id = 5;
// Delete a property
delete post.excerpt;
console.log(post);
// {id: 5, name: 'JavaScript is awesome', content: 'JavaScript is a scripting language that enables you to create dynamically updating content.', slug: 'javascript-is-awesome'}
但你不允許重新分配物件本身。下面的程式碼會經歷一個 Uncaught TypeError:
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language'
};
post = {
id: 1,
name: 'React is powerful',
excerpt: 'React lets you build user interfaces'
};
✍️ 在React中,用 const 宣告變數是預設的。 當 const 不合適時,應該使用 let 。非常不鼓勵使用 var 。
Object.freeze()
我們現在同意,使用 const 並不總是保證強大的不變性(尤其是在處理物件和陣列時)。那麼,你如何在你的React應用程式中實現不變性模式呢?
首先,當你想防止陣列的元素或物件的屬性被修改時,你可以使用靜態方法 Object.freeze() 。
凍結一個物件可以防止擴充套件,並使現有的屬性不可寫入和不可配置。一個被凍結的物件不能再被改變:新的屬性不能被新增,現有的屬性不能被刪除,它們的可列舉性、可配置性、可寫性或值不能被改變,而且物件的原型不能被重新分配。freeze() 返回與傳入的物件相同。
任何試圖新增、改變或刪除一個屬性的行為都會失敗,要麼是無聲無息,要麼是丟擲一個 TypeError ,最常見的是在嚴格模式下。
你可以這樣使用 Object.freeze() :
'use strict'
// Declare a constant obj
const post = {
id: 1,
name: 'JavaScript is awesome',
excerpt: 'JavaScript is an awesome scripting language'
};
// Freeze the object
Object.freeze(post);
如果你現在嘗試新增一個屬性,你會收到一個 Uncaught TypeError:
// Add a new property post.slug = "javascript-is-awesome"; // Uncaught TypeError

火狐瀏覽器中的Uncaught TypeError: can’t define property “slug” : Object is not extensible
當你試圖重新分配一個屬性時,你會得到另一種 TypeError :
// Reassign property post.id = 5; // Uncaught TypeError

重新分配一個只讀屬性會產生一個未發現的型別錯誤

谷歌瀏覽器中的Uncaught TypeError: Cannot assign to read only property ‘id’ of object ‘#<Object>’
你也可以嘗試刪除一個屬性。其結果將是另一個 TypeError :
// Delete a property delete post.excerpt; // Uncaught TypeError

火狐瀏覽器中的Uncaught TypeError: property “excerpt” is non-configurable and can’t be deleted
模板字串
當你需要在JavaScript中把字串和表示式的輸出結合起來時,你通常使用加法運算子 + 。然而,你也可以使用一個JavaScript特性,它允許你在字串中包含表示式而不使用加法運算子:模板字串。
模板字串是一種特殊的字串,以回車鍵( ` )字元為界。
在模板文字中,你可以包括佔位符,它是由美元字元限定的嵌入式表示式,用大括號包裹。
下面是一個例子:
const align = 'left';
console.log(`This string is ${ align }-aligned`);
字串和佔位符被傳遞給一個預設函式,該函式執行字串插值,以替代佔位符,並將各部分連線成一個字串。你也可以用一個自定義函式來替換預設函式。
你可以在以下情況下使用模板字串:
多行字串:換行符是模板字串的一部分。
console.log(`Twinkle, twinkle, little bat! How I wonder what you’re at!`);
字串插值:如果沒有模板字串,你只能使用加法運算子將表示式的輸出與字串相結合。請看下面的例子:
const a = 3;
const b = 7;
console.log("The result of " + a + " + " + b + " is " + (a + b));
這有點令人困惑,不是嗎?但你可以用模板字串的方式來寫這段程式碼,使其更易讀和可維護:
const a = 3;
const b = 7;
console.log(`The result of ${ a } + ${ b } is ${ a + b }`);
但請記住,這兩種語法之間是有區別的:
✍️ 模板字串直接將其表示式脅迫為字串,而加法則先將其運算元脅迫為基數。
模板字串可以有多種用途。在下面的例子中,我們使用一個三元運算子來給一個 class 屬性賦值。
const page = 'archive';
console.log(`class=${ page === 'archive' ? 'archive' : 'single' }`);
下面,我們進行一個簡單的計算:
const price = 100;
const VAT = 0.22;
console.log(`Total price: ${ (price * (1 + VAT)).toFixed(2) }`);
也可以通過在 ${expression} 佔位符中包含模板字樣來巢狀模板字樣(但要謹慎使用巢狀模板,因為複雜的字串結構可能難以閱讀和維護)。
標籤模板:正如我們上面提到的,也可以定義一個自定義函式來執行字串連線。這種模板字面意義被稱為標籤模板。
標籤允許你用一個函式來解析模板字面。標籤函式的第一個引數包含一個字串值的陣列。其餘的引數與表示式有關。
標籤允許你用一個自定義函式來解析模板字面。這個函式的第一個引數是一個包含在模板字面意義中的字串陣列,其他引數是表示式。
你可以建立一個自定義函式,對模板引數進行任何形式的操作,並返回所操作的字串。下面是一個非常基本的標籤模板的例子:
const name = "Carlo";
const role = "student";
const organization = "North Pole University";
const age = 25;
function customFunc(strings, ...tags) {
console.log(strings); // ['My name is ', ", I'm ", ', and I am ', ' at ', '', raw: Array(5)]
console.log(tags); // ['Carlo', 25, 'student', 'North Pole University']
let string = '';
for ( let i = 0; i < strings.length - 1; i++ ){
console.log(i + "" + strings[i] + "" + tags[i]);
string += strings[i] + tags[i];
}
return string.toUpperCase();
}
const output = customFunc`My name is ${name}, I'm ${age}, and I am ${role} at ${organization}`;
console.log(output);
上面的程式碼列印了 strings 和 tags 陣列元素,然後在瀏覽器控制檯中列印輸出之前將字串字元大寫。
箭頭函式
箭頭函式是JavaScript中匿名函式(沒有名字的函式)的替代品,但有一些區別和限制。
下面的宣告都是有效的箭頭函式例子:
// Arrow function without parameters
const myFunction = () => expression;
// Arrow function with one parameter
const myFunction = param => expression;
// Arrow function with one parameter
const myFunction = (param) => expression;
// Arrow function with more parameters
const myFunction = (param1, param2) => expression;
// Arrow function without parameters
const myFunction = () => {
statements
}
// Arrow function with one parameter
const myFunction = param => {
statements
}
// Arrow function with more parameters
const myFunction = (param1, param2) => {
statements
}
如果你只向函式傳遞一個引數,你可以省略圓括號。如果你傳遞兩個或更多的引數,你必須用圓括號把它們括起來。下面是一個例子:
const render = ( id, title, category ) => `${id}: ${title} - ${category}`;
console.log( render ( 5, 'Hello World!', "JavaScript" ) );
單行箭頭函式預設會返回一個值。如果你使用多行語法,你將不得不手動返回一個值:
const render = ( id, title, category ) => {
console.log( `Post title: ${ title }` );
return `${ id }: ${ title } - ${ category }`;
}
console.log( `Post details: ${ render ( 5, 'Hello World!', "JavaScript" ) }` );
✍️ 你通常會在React應用程式中使用Arrow Function,除非有特殊原因不使用它們。
要記住普通函式和Arrow函式的一個關鍵區別是,Arrow函式沒有自己的關鍵字 this 的繫結。如果你試圖在Arrow函式中使用 this ,它將超出函式範圍。
關於Arrow函式的更深入的描述和使用例項,請閱讀mdn web docs。
類
JavaScript中的類是一種特殊型別的函式,用於建立使用原型繼承機制的物件。
根據mdn web docs:
說到繼承,JavaScript只有一種結構:物件。每個物件都有一個私有屬性,它持有一個指向另一個物件的連結,稱為它的原型。該原型物件有一個自己的原型,以此類推,直到達到一個以 null 為原型的物件。
和函式一樣,你有兩種方法來定義一個類:
- 一個類的表示式
- 一個類的宣告
你可以使用 class 關鍵字在表示式中定義一個類,如下面的例子中所示:
const Circle = class {
constructor(radius) {
this.radius = Number(radius);
}
area() {
return Math.PI * Math.pow(this.radius, 2);
}
circumference() {
return Math.PI * this.radius * 2;
}
}
console.log('Circumference: ' + new Circle(10).circumference()); // 62.83185307179586
console.log('Area: ' + new Circle(10).area()); // 314.1592653589793
一個類有一個主體,就是包含在大括號中的程式碼。在這裡你將定義建構函式和方法,它們也被稱為類成員。即使不使用 'strict mode' 指令,類的主體也會以嚴格模式執行。
constructor 方法用於建立和初始化用類建立的物件,在類被例項化時自動執行。如果你沒有在你的類中定義一個構造方法,JavaScript會自動使用一個預設的構造方法。
一個類可以用 extends 關鍵字來擴充套件。
class Book {
constructor(title, author) {
this.booktitle = title;
this.authorname = author;
}
present() {
return this.booktitle + ' is a great book from ' + this.authorname;
}
}
class BookDetails extends Book {
constructor(title, author, cat) {
super(title, author);
this.category = cat;
}
show() {
return this.present() + ', it is a ' + this.category + ' book';
}
}
const bookInfo = new BookDetails("The Fellowship of the Ring", "J. R. R. Tolkien", "Fantasy");
console.log(bookInfo.show());
一個建構函式可以使用 super 關鍵字來呼叫父建構函式。如果你向 super() 方法傳遞了一個引數,這個引數也會在父級建構函式類中出現。
關於對JavaScript類的深入研究和幾個使用例項,也可以參見mdn web docs。
類經常被用來建立React元件。通常情況下,你不會建立你自己的類,而是擴充套件內建的React類。
React中的所有類都有一個 render() 方法,可以返回一個React元素:
class Animal extends React.Component {
render() {
return <h2>Hey, I am a {this.props.name}!</h2>;
}
}
在上面的例子中, Animal 是一個類元件。請記住
- 元件的名稱必須以大寫字母開頭
- 該元件必須包括表示式
extends React.Component。這樣就可以訪問React.Component的方法。 -
render()方法返回HTML,這是必須的。
一旦你建立了你的類元件,你就可以在頁面上渲染HTML了:
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Animal name="Rabbit" />;
root.render(element);
下面的圖片顯示了頁面上的結果(你可以在CodePen上看到它的操作)。

一個簡單的React類元件
但是請注意,不建議在React中使用類元件,最好是將元件定義為函式。
關鍵詞this
在JavaScript中, this 關鍵字是一個通用的佔位符,通常在物件、類和函式中使用,它根據上下文或範圍指代不同的元素。
this 可以在全域性範圍內使用。如果你在瀏覽器的控制檯中挖出 this ,你會得到:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
你可以訪問 Window 物件的任何方法和屬性。因此,如果你在瀏覽器的控制檯中執行 this.location ,你會得到以下輸出:
Location {ancestorOrigins: DOMStringList, href: 'https://www.wbolt.com/tw/', origin: 'https://www.wbolt.com', protocol: 'https:', host: 'www.wbolt.com', ...}
當你在一個物件中使用 this 時,它指的是物件本身。通過這種方式,你可以在物件本身的方法中引用物件的值:
const post = {
id: 5,
getSlug: function(){
return `post-${this.id}`;
},
title: 'Awesome post',
category: 'JavaScript'
};
console.log( post.getSlug );
現在讓我們試著在一個函式中使用 this :
const useThis = function () {
return this;
}
console.log( useThis() );
如果你不是在嚴格的模式下,你會得到:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
但如果你呼叫嚴格模式,你會得到一個不同的結果:
const doSomething = function () {
'use strict';
return this;
}
console.log( doSomething() );
在這種情況下,該函式返回 undefined。這是因為在一個函式中 this 指的是它的顯式值。
那麼,如何在一個函式中顯式地設定 this 呢?
首先,你可以手動分配屬性和方法給函式:
function doSomething( post ) {
this.id = post.id;
this.title = post.title;
console.log( `${this.id} - ${this.title}` );
}
new doSomething( { id: 5, title: 'Awesome post' } );
但你也可以使用 call, apply, 和 bind 方法,以及箭頭函式。
✍️ 一個函式的 call() 方法接受 this 所指的物件。
const doSomething = function() {
console.log( `${this.id} - ${this.title}` );
}
doSomething.call( { id: 5, title: 'Awesome post' } );
call() 方法可以在任何函式上使用,並且完全按照它所說的做:呼叫該函式。
此外, call() 還接受在函式中定義的任何其他引數:
const doSomething = function( cat ) {
console.log( `${this.id} - ${this.title} - Category: ${cat}` );
}
doSomething.call( { id: 5, title: 'Awesome post' }, 'JavaScript' );
✍️ apply() 方法接受一個物件, this 將被引用,並接受一個函式引數陣列。
const doSomething = function( cat1, cat2 ) {
console.log( `${this.id} - ${this.title} - Categories: ${cat1}, ${cat2}` );
}
doSomething.apply( { id: 5, title: 'Awesome post' }, ['JavaScript', 'React'] );
✍️ bind() 方法將一個物件與一個函式聯絡起來,這樣,每當你呼叫這個函式時,this 指代這個物件。
const post = { id: 5, title: 'Awesome post', category: 'JavaScript' };
const doSomething = function() {
return `${this.id} - ${this.title} - ${this.category}`;
}
const bindRender = doSomething.bind( post );
console.log( bindRender() );
除了上面討論的選項外,還有一種方法是使用箭頭函式。
箭頭函式表示式應該只用於非方法函式,因為它們沒有自己的 this。
這使得箭頭函式對事件處理程式特別有用。
這是因為 “當程式碼從內聯事件處理程式屬性中呼叫時,其 this 被設定為監聽器所在的DOM元素”(見cdn web docs)。
但箭頭函式的情況就不同了,因為…
… 箭頭函式是 this 根據箭頭函式定義的範圍來建立的,而 this 值不會因為函式的呼叫方式而改變。
✍️ 使用箭頭函式允許你直接將上下文繫結到事件處理程式。
將 “this” 繫結到React的事件處理程式上
當涉及到React時,你有幾種方法來確保事件處理程式不會丟失其上下文:
1. 在渲染方法中使用 bind() :
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage(){
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={ this.showMessage.bind( this ) }>Show message from state!</button> );
}
}
export default MyComponent;
2. 在建構函式中將上下文與事件處理程式繫結:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
constructor(props) {
super(props);
this.showMessage = this.showMessage.bind( this );
}
showMessage(){
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={ this.showMessage }>Show message from state!</button> );
}
}
export default MyComponent;
3. 使用箭頭函式定義事件處理程式:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage = () => {
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={this.showMessage}>Show message from state!</button> );
}
}
export default MyComponent;
4. 在渲染方法中使用箭頭函式:
import React, { Component } from 'react';
class MyComponent extends Component {
state = { message: 'Hello World!' };
showMessage() {
console.log( 'This refers to: ', this );
console.log( 'The message is: ', this.state.message );
}
render(){
return( <button onClick={()=>{this.showMessage()}}>Show message from state!</button> );
}
}
export default MyComponent;
無論你選擇哪種方法,當你點選按鈕時,瀏覽器控制檯顯示以下輸出:
This refers to: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
The message is: Hello World!
三元運算子
條件運算子(或三元運算子)允許你在JavaScript中編寫簡單的條件表示式。它需要三個運算元:
- 一個條件,後面跟著一個問號(
?) - 如果條件是真實的,則執行一個表示式,後面是一個分號(
:) - 第二個表示式,如果條件是falsy,則執行。
const drink = personAge >= 18 ? "Wine" : "Juice";
也可以將多個表示式連在一起:
const drink = personAge >= 18 ? "Wine" : personAge >= 6 ? "Juice" : "Milk";
但要小心,因為連鎖多個表示式會導致混亂的程式碼,難以維護。
三元運算子在React中特別有用,尤其是在你的JSX程式碼中,它只接受大括號中的表示式。
例如,你可以使用三元運算子根據一個特定的條件來設定一個屬性的值:
render(){
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/...',
description: 'Content Writer',
theme: 'light'
}
return (
<div
className='card'
style={
person.theme === 'dark' ?
{ background: 'black', color: 'white' } :
{ background: 'white', color: 'black'}
}>
<img
src={person.avatar}
alt={person.name}
style={ { width: '100%' } }
/>
<div style={ { padding: '2px 16px' } }>
<h3>{person.name}</h3>
<p>{person.description}.</p>
</div>
</div>
);
}
在上面的程式碼中,我們檢查條件 person.theme === 'dark' ,以設定容器 div 的 style 屬性的值。
短路求值(最小化求值)
邏輯與( && )運算子從左到右評估運算元,當且僅當所有運算元為 true 時返回 true 。
邏輯與是一個短路運算子。每個運算元都被轉換為布林值,如果轉換的結果是 false,AND運算子就會停止並返回假運算元的原始值。如果所有的值都是 true,它就返回最後一個運算元的原始值。
✍️ 在JavaScript中,true && expression 總是返回 expression,而 false && expression 總是返回 false。
短路求值是React中常用的JavaScript功能,因為它允許你根據特定條件輸出程式碼塊。下面是一個例子:
{
displayExcerpt &&
post.excerpt.rendered && (
<p>
<RawHTML>
{ post.excerpt.rendered }
</RawHTML>
</p>
)
}
在上面的程式碼中,如果 displayExcerpt 和 post.excerpt.rendered 求值為true,React會返回最後的JSX塊。
簡而言之,”如果條件為 true,緊隨 && 之後的元素將出現在輸出中。如果是 false,React會忽略並跳過它”。
展開語法
在JavaScript中,展開語法允許你將一個可迭代的元素,如陣列或物件,擴充套件為函式引數、陣列字面,或物件字面。
在下面的例子中,我們在一個函式呼叫中對一個陣列進行解包:
function doSomething( x, y, z ){
return `First: ${x} - Second: ${y} - Third: ${z} - Sum: ${x+y+z}`;
}
const numbers = [3, 4, 7];
console.log( doSomething( ...numbers ) );
你可以使用spread語法來複制一個陣列(甚至是多維陣列)或連線陣列。在下面的例子中,我們以兩種不同的方式串聯兩個陣列:
const firstArray = [1, 2, 3]; const secondArray = [4, 5, 6]; firstArray.push( ...secondArray ); console.log( firstArray );
另外,還可以選擇:
let firstArray = [1, 2, 3]; const secondArray = [4, 5, 6]; firstArray = [ ...firstArray, ...secondArray]; console.log( firstArray );
你也可以使用展開語法來克隆或合併兩個物件:
const firstObj = { id: '1', title: 'JS is awesome' };
const secondObj = { cat: 'React', description: 'React is easy' };
// clone object
const thirdObj = { ...firstObj };
// merge objects
const fourthObj = { ...firstObj, ...secondObj }
console.log( { ...thirdObj } );
console.log( { ...fourthObj } );
解構賦值
你會發現React中經常使用的另一種語法結構是解構賦值語法。
✍️ 解構賦值語法允許你將陣列中的值或物件中的屬性解包到單獨的變數中。
在下面的例子中,我們從一個陣列中解包數值:
const user = ['Carlo', 'Content writer', 'Kinsta'];
const [name, description, company] = user;
console.log( `${name} is ${description} at ${company}` );
而這裡是一個用物件進行解構賦值的簡單例子:
const user = {
name: 'Carlo',
description: 'Content writer',
company: 'Kinsta'
}
const { name, description, company } = user;
console.log( `${name} is ${description} at ${company}` );
但我們可以做得更多。在下面的例子中,我們把一個物件的一些屬性拆開,用展開語法把剩餘的屬性分配給另一個物件:
const user = {
name: 'Carlo',
family: 'Daniele',
description: 'Content writer',
company: 'Kinsta',
power: 'swimming'
}
const { name, description, company, ...rest } = user;
console.log( rest ); // {family: 'Daniele', power: 'swimming'}
你也可以給一個陣列賦值:
const user = [];
const object = { name: 'Carlo', company: 'Kinsta' };
( { name: user[0], company: user[1] } = object );
console.log( user ); // (2) ['Carlo', 'Kinsta']
注意,當使用沒有宣告的物件字面解構賦值時,賦值語句周圍的括號是必須的。
關於解構賦值的更深入分析,以及幾個使用例項,請參考 mdn web docs。
filter(), map()和reduce()
JavaScript提供了幾個有用的方法,你會發現在React中經常使用。
filter()
✍️ filter() 方法建立一個淺拷貝,將給定陣列的一部分過濾到符合所提供函式條件的元素。
在下面的例子中,我們將過濾器應用於 numbers 陣列,得到一個元素大於5的陣列:
const numbers = [2, 6, 8, 2, 5, 9, 23]; const result = numbers.filter( number => number > 5); console.log(result); // (4) [6, 8, 9, 23]
在下面的例子中,我們得到一個標題中包含 “JavaScript “一詞的posts陣列:
const posts = [
{id: 0, title: 'JavaScript is awesome', content: 'your content'},
{id: 1, title: 'WordPress is easy', content: 'your content'},
{id: 2, title: 'React is cool', content: 'your content'},
{id: 3, title: 'With JavaScript to the moon', content: 'your content'},
];
const jsPosts = posts.filter( post => post.title.includes( 'JavaScript' ) );
console.log( jsPosts );

標題中包含 “JavaScript” 的posts陣列
map()
✍️ map() 方法對陣列中的每個元素執行所提供的函式,並返回一個由回撥函式產生的每個元素填充的新陣列。
const numbers = [2, 6, 8, 2, 5, 9, 23]; const result = numbers.map( number => number * 5 ); console.log(result); // (7) [10, 30, 40, 10, 25, 45, 115]
在React元件中,你會經常發現 map() 方法被用來建立列表。在下面的例子中,我們正在對映WordPress的 posts 物件來建立一個文章的列表:
<ul>
{ posts && posts.map( ( post ) => {
return (
<li key={ post.id }>
<h5>
<a href={ post.link }>
{
post.title.rendered ?
post.title.rendered :
__( 'Default title', 'author-plugin' )
}
</a>
</h5>
</li>
)
})}
</ul>
reduce()
✍️ reduce() 方法在陣列的每個元素上執行一個回撥函式(reducer),並將返回的值傳遞給下一次迭代。簡而言之,還原器將一個陣列的所有元素 “reduces” 為一個單一的值。
reduce() 接受兩個引數:
- 一個回撥函式,為陣列中的每個元素執行。它返回一個值,在下次呼叫時成為累加器引數的值。在最後一次呼叫時,該函式返回的值將成為
reduce()的返回值。 - 一個初始值,是傳遞給回撥函式的累加器的第一個值。
回撥函式需要幾個引數:
- 一個累加器(accumulator):從上一次呼叫回撥函式返回的值。在第一次呼叫時,如果指定的話,它會被設定為一個初始值。否則,它取陣列中第一項的值。
- 當前元素的值:如果已經設定了一個初始值,該值被設定為陣列的第一個元素(
array[0]),否則它取第二個元素的值(array[1])。 - 當前的索引是當前元素的索引位置。
下面額例子會讓一切變得更清晰。
const numbers = [1, 2, 3, 4, 5]; const initialValue = 0; const sumElements = numbers.reduce( ( accumulator, currentValue ) => accumulator + currentValue, initialValue ); console.log( numbers ); // (5) [1, 2, 3, 4, 5] console.log( sumElements ); // 15
讓我們詳細瞭解一下每次迭代時發生的情況。回到前面的例子中,改變 initialValue:
const numbers = [1, 2, 3, 4, 5];
const initialValue = 5;
const sumElements = numbers.reduce(
( accumulator, currentValue, index ) => {
console.log('Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index);
return accumulator + currentValue;
},
initialValue
);
console.log( sumElements );
下圖顯示了瀏覽器控制檯的輸出:

使用reduce(),初始值設定為5
現在讓我們來看看沒有 initialValue 引數會發生什麼:
const numbers = [1, 2, 3, 4, 5];
const sumElements = numbers.reduce(
( accumulator, currentValue, index ) => {
console.log( 'Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index );
return accumulator + currentValue;
}
);
console.log( sumElements );

使用reduce()而不使用初始值
更多的例子和用例在mdn web docs網站上討論。
匯出和匯入
從ECMAScript 2015(ES6)開始,可以從一個JavaScript模組中匯出值,並將其匯入另一個指令碼中。你將在你的React應用程式中廣泛使用匯入和匯出,因此,對它們的工作原理有一個良好的理解是很重要的。
下面的程式碼建立了一個功能元件。第一行是匯入React庫:
import React from 'react';
function MyComponent() {
const person = {
name: 'Carlo',
avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
description: 'Content Writer',
theme: 'dark'
}
return (
<div
className = 'card'
style = {
person.theme === 'dark' ?
{ background: 'black', color: 'white' } :
{ background: 'white', color: 'black'}
}>
<img
src = { person.avatar }
alt = { person.name }
style = { { width: '100%' } }
/>
<div
style = { { padding: '2px 16px' } }
>
<h3>{ person.name }</h3>
<p>{ person.description }.</p>
</div>
</div>
);
}
export default MyComponent;
我們使用了 import 關鍵字,後面是我們要分配給我們要匯入的東西的名字,然後是我們要安裝的包的名字,因為它在package.json檔案中被提及。
✍️ import 宣告是用來匯入由其他模組匯出的只讀的活體繫結的。
注意,在上面的 MyComponent() 函式中,我們使用了前幾節討論的一些JavaScript特性。我們在大括號中包含了屬性值,並使用條件運算子語法為 style 屬性賦值。
指令碼以匯出我們的自定義元件結束。
現在我們對匯入和匯出有了一些瞭解,讓我們仔細看看它們是如何工作的。
Export
✍️ export 宣告用於從一個JavaScript模組匯出值。
每個React模組可以有兩種不同型別的匯出:命名匯出和預設匯出。
✍️ 每個模組可以有多個命名匯出,但只有一個預設匯出。
例如,你可以用一個 export 語句同時匯出幾個功能:
export { MyComponent, MyVariable };
你也可以匯出單個特徵(function, class, const, let):
export function MyComponent() { ... };
export let myVariable = x + y;
但你只能有一個單一的預設匯出:
export default MyComponent;
你也可以對個別功能使用預設匯出:
export default function() { ... }
export default class { ... }
Import
一旦元件被匯出,它就可以和其他模組一起被匯入另一個檔案,例如 index.js 檔案:
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import MyComponent from './MyComponent'; const root = ReactDOM.createRoot( document.getElementById( 'root' ) ); root.render( <React.StrictMode> <MyComponent /> </React.StrictMode> );
在上面的程式碼中,我們以幾種方式使用了匯入宣告。
在前兩行中,我們為匯入的資源指定了一個名稱,在第三行中,我們沒有指定一個名稱,只是匯入了./index.css檔案。最後一條 import 語句匯入了./MyComponent檔案並指定了一個名稱。
讓我們來看看這些匯入的區別。
總的來說,有四種型別的匯入:
Named import
import { MyFunction, MyVariable } from "./my-module";
Default import
import MyComponent from "./MyComponent";
Namespace import
import * as name from "my-module";
Side effect import
import "module-name";
✍️ 不帶大括號的 import 語句用於匯入預設 export。帶大括號的 import 語句是用來匯入一個命名的 export。
一旦你在index.css中新增了一些樣式,你的卡片應該像下面的圖片一樣,你也可以看到相應的HTML程式碼:

一個簡單的React元件
注意, import 宣告只能在頂層的模組中使用(不能在函式、類等內部使用)。
對於 import 和 export 語句的更全面的概述,你可能還想檢視以下資源:
- export (mdn web docs)
- import (mdn web docs)
- 匯入和匯出元件 (React dev)
- 什麼時候應該在ES6匯入時使用大括號? (Stack Overflow)
小結
React是當今最流行的JavaScript庫之一,是網路開發世界中要求最多的技能之一。
有了React,就可以建立動態的網路應用程式和高階介面。由於其可重複使用的元件,建立大型、動態和互動的應用程式可以很容易。
但React是一個JavaScript庫,充分了解JavaScript的主要功能對於開始你的React之旅至關重要。這就是為什麼我們在一個地方收集了一些你會發現在React中最常使用的JavaScript功能。掌握這些功能會讓你在React學習之旅中佔得先機。
而當涉及到網路開發時,從JS/React轉移到WordPress只需要很少的努力。
現在輪到你了,你認為哪些JavaScript功能在React開發中是最有用的?我們有沒有遺漏任何你希望在我們的列表中看到的重要功能?請在下面的評論中與我們分享你的想法。

評論留言