掌握 React 你需要学习的 10 个 JavaScript 概念
目录
箭头函数
默认参数
模板字符串
Let 和 Const
课程
解构
三元运算符
导入/导出模块
异步/等待
扩展运算符/剩余参数
结论
如果你想构建 React 应用,掌握 JavaScript / ES6+ 的知识至关重要。事实上,ES6+ 为 JavaScript 带来了许多很酷的功能,让编写 React 组件变得更加轻松、简洁。
虽然 ES6 及其后续更新带来了许多新功能,但为了编写更好、更简洁的 React 应用,你仍然需要了解一些概念。掌握这些概念将使你成为更优秀的 JavaScript 开发人员,并将你的 React 应用提升到一个新的水平。
因此,我决定创建这篇文章,与您分享成为更好的 React 开发人员所需掌握的 10 个最有用的 JavaScript / ES6+ 概念。
🔖收藏这篇文章,分享给你的开发者朋友们!希望你会喜欢。
如果您想了解更多关于 ReactJS 的知识并提升您的 JavaScript 技能,请点击此处查看我的 ReactJS 在线课程。无需任何 ReactJS 经验。您将学习如何使用现代 ReactJS 逐步构建一个真实的应用程序。
目录
箭头函数
您可能知道,定义 React 组件的最简单方法是编写一个 JavaScript 函数,如下例所示。
function MyComponent(props) {
return <h1>Hello from AlterClass.io</h1>;
}
但是还有另一种非常简单、简洁的方法来创建 React 函数组件,它甚至比常规函数更好。它叫做arrow functions
。
const MyComponent = (props) => <h1>Hello from AlterClass.io</h1>;
正如您所见,它使我们能够编写更少的代码来实现相同的结果。
箭头函数是 JavaScript 和 React 应用程序中最常见的函数。因此,理解并掌握它们是一个好主意。
在深入探讨如何在 React 中使用箭头函数之前,我们先来看看如何编写它们。事实上,箭头函数有多种语法可供选择。我们将在这里介绍一些常用的语法,帮助你快速上手。
// Basic syntax with multiple parameters
const add = (a, b) => { return a + b };
// Curly brackets aren’t required if only one expression is present
// The `return` keyword is also implicit and can be ommited
const add = (a, b) => a + b;
// Parentheses are optional when only one parameter is present
const getUser = data => data.user;
// However, parentheses are required when no parameters are present
const hello = () => console.log("Hello from AlterClass.io");
既然我们已经了解了基本语法,让我们来看看如何在 React 中使用箭头函数。除了像上面那样定义 React 组件之外,箭头函数在操作数组、异步回调和 Promises 时也非常有用。
确实,在 React 中,我们通常需要从服务器获取数据并将其显示给用户。为了获取这些数据,我们经常使用 Promises 链式调用。
// ES5
fetch(apiURL)
.then(function(res) {
return res.json();
})
.then(function(data) {
return data.products;
})
.catch(function(error) {
console.log(error);
});
Promises 链式调用更加简化,更易读,使用箭头函数更加简洁:
// ES6
fetch(apiURL)
.then(res => res.json())
.then(data => data.products)
.catch(error => console.log(error));
最后,检索到数据后,我们需要将其显示出来。要在 React 中渲染数据列表,我们必须在 JSX 内部进行循环。这通常使用 map/reduce/filter 数组方法来实现。
const products = [
{ _id: 1234, name: "ReactJS Pro Package", price: 199 },
{ _id: 5678, name: "ReactJS Basic Package", price: 99 },
...
];
// ES5
function ProductList(props) {
return (
<ul>
{props.products
.filter(function(product) {
return product.price <= 99;
})
.map(function(product) {
return <li key={product._id}>{product.name}</li>;
})}
</ul>
);
}
现在,让我们看看如何使用 ES6 箭头函数实现同样的功能。
// ES6
const ProductList = props => (
<ul>
{props.products
.filter(product => product.price <= 99)
.map(product => (
<li key={product._id}>{product.name}</li>
))}
</ul>
);
默认参数
了解了什么是箭头函数之后,我们来谈谈默认参数。ES6+ 的这项功能允许即使函数调用时未包含相应的参数,也可以使用默认值来初始化函数。
但首先,你还记得在 ES6 之前我们是怎样检查函数中未声明参数的吗?你可能见过或用过这样的东西:
// ES5
function getItems(url, offset, limit, orderBy) {
offset = (typeof offset !== 'undefined') ? offset : 0;
limit = (typeof limit !== 'undefined') ? limit : 10;
orderBy = (typeof orderBy !== 'undefined') ? orderBy : 'date';
...
}
为了防止函数崩溃,或者计算出无效/错误的结果,我们必须编写额外的代码来测试每个可选参数和指定的默认值。事实上,这种技术是为了避免函数内部出现不良影响。如果没有它,任何未初始化的参数都会被默认为undefined
。
以上就是 ES6 之前处理默认参数的简要总结。在 ES6 中定义默认参数要容易得多。
// ES6
function getItems(url, offset = 0, limit = 10, orderBy = 'date') {
...
}
// Default parameters are also supported with arrow functions
const getItems = (url, offset = 0, limit = 10, orderBy = 'date') => {
...
}
简洁干净👌。如果在函数调用中传入 offset、limit 和 orderBy,它们的值将覆盖函数定义中定义的默认参数。无需额外代码。
⚠️请注意,
null
被视为有效值。这意味着,如果您null
为其中一个参数传递一个值,它将不会采用函数定义的默认值。因此,当您希望使用默认值时,请确保使用undefined
而不是。null
现在您知道了如何在 ES6 中使用默认参数。那么默认参数和 React 呢?
在 React 中,你可以使用defaultProps属性为组件的 props 设置默认值。然而,这仅适用于类组件。实际上,React 团队正在弃用defaultProps
函数组件上的该属性,并将移除它。
别担心!我们可以利用默认参数来为 React 函数组件的 props 设置默认值。请查看下面的示例。
const Button = ({ size = 'md', disabled = false, children }) => (
<button
type="button"
disabled={disabled}
className={`btn-${size}`}
>
{children}
</button>
);
模板字符串
模板字面量是允许嵌入 JavaScript 表达式的字符串。换句话说,它是一种在字符串中输出变量/表达式的方法。
+
在 ES5 中,我们必须使用运算符连接多个值来断开字符串。
// ES5
console.log("Something went wrong: " + error.message);
在 ES6 中,模板字面量用反引号括起来,而不是用双引号或单引号。要在这些模板中插入表达式,我们可以使用新的语法${expression}
。
// ES6
console.log(`Something went wrong: ${error.message}`);
...
console.log(`Hello, ${getUserName()}!`);
...
模板字面量使这种替换更具可读性。在 React 中使用它们可以帮助你动态设置组件的 prop 值,或者元素的属性值。
const Button = (props) => (
<button
type="button"
className={`btn-${props.size}`}
>
{props.children}
</button>
);
Let 和 Const
在 ES5 中,声明变量的唯一方法是使用var
关键字 。ES6 引入了两种新方法: 和const
。let
如果你想了解这些方法的每一个细节,请查看这篇很棒的文章。在这里,我仅列出主要的区别:
变量
- 函数作用域
undefined
在声明变量之前访问该变量时保持让
- 块作用域
ReferenceError
在声明变量之前访问它常量
- 块作用域
ReferenceError
在声明变量之前访问它- 无法重新分配
- 应在声明时初始化
自从 let 和 const 引入以来,经验法则就是用它们代替 var。你不应该再使用 var 了。let 和 const 更具体,并且能提供更可预测的变量。
另外,默认情况下,最好使用 const 而不是 let,因为它不能被重新赋值或重新声明。当你需要重新赋值变量时,请使用 let。
在 React 应用中,const
用于声明 React 组件,因为它们不会被重新赋值。除此之外,需要重新赋值的变量使用 let 声明,不需要重新赋值的变量使用 const 声明。
const OrderDetails = (props) => {
const [totalAmount, setTotalAmount] = useState(0.0);
const { state } = useContext(Context);
useEffect(() => {
let total = state.course.price;
// substract promotional discount
total -= state.course.price * state.course.discountRate;
// add taxes
total += total * state.course.taxPercentage;
setTotalAmount(total);
},
[state]
);
const handleOnClick = () => { ... };
return (
<>
<span>Total: ${totalAmount}</span>
<button onClick={handleOnClick}>Pay</button>
</>
);
};
课程
JavaScript 类是在 ES6 中引入的。正如 MDN 网页文档所述,类“主要是 JavaScript 现有基于原型的继承的语法糖”。不过,类也有一些属性值得了解,因为它们与使用常规函数编写的类并不完全相同。为此,请查看这篇很棒的文章。
// ES6 class definition
class User {
constructor(name) {
this.name = name;
}
greet() {
return `${this.name} says hello!`;
}
}
// Usage
let user = new User("Greg");
user.greet(); // --> Greg says hello!
与类相关的一个有趣概念是继承。继承并非 JavaScript 独有,而是面向对象编程中的一个常见概念。简而言之,继承就是将一个类创建为另一个类的子类。子类将继承其父类的属性(实际上,这比继承要复杂得多,具体取决于您使用的 OOP 语言)。
在 ES6 中,extends
关键字用于基于另一个类创建一个类。
class Employee extends User {
constructor(name, salary) {
// call the constructor of the User class
super(name);
// add a new property
this.salary = salary;
}
raiseSalary() {
this.salary += 10000;
return this.salary;
}
}
// Usage
let employee = Employee("Greg", 250000);
employee.raiseSalary(); // --> 260000
在 React 应用程序中,你也可以使用 ES6 类来定义组件。要定义 React 组件类,你需要扩展React.Component
基类,如下所示:
class Button extends React.Component {
render() {
return <button type="buttom">Click me</button>;
}
}
通过创建这样的组件,您将可以访问一系列与 React 组件相关的方法和属性(状态、属性、生命周期方法等)。请参阅React 文档,获取该类的详细 API 参考React.Component
。
解构
解构在 React 中非常常用。这个概念既可以用于对象,也可以用于数组。解构是一种简化 JavaScript 代码的简单方法,因为它允许我们一行代码就能从对象或数组中提取数据。
数组解构与对象解构类似,不同之处在于我们按照数据在数组中出现的顺序逐个提取数据。
让我们直接了解它在 React 应用程序中的使用方法。
// grab `useState` with object destructuring
import React, { useState } from 'react';
// grab individual props with object destructuring
const Button = ({ size = 'md', disabled = false }) => {
// grab stateful value and update function with array destructing
const [loading, setLoading] = useState(false);
return (...);
};
三元运算符
三元运算符是该语句的简写形式if
。典型语句的语法if
如下:
if (condition) {
// value if true
}
else {
// value if false
}
使用三元运算符如下所示:
condition ? valueIfTrue : valueIfFalse
正如您所见,这是定义条件语句的一种更简短的方法。
如果条件为真,则执行第一个语句(冒号 之前:
)。否则,如果条件为假(false、null、NaN、0、"" 或 undefined),则执行第二个语句(冒号 之后:
)。
然而,这不一定是最简洁或最易读的条件语句编写方式。因此,使用时务必谨慎,因为它可能难以理解,尤其是在像下面这样串联多个条件语句的情况下。
return condition1 ? value1
: condition2 ? value2
: condition3 ? value3
: value4;
在 React 中,三元运算符允许我们在 JSX 中编写更简洁的条件语句。通常使用它来根据条件决定显示哪个组件或显示/隐藏组件。
const App = () => {
const [loading, setLoading] = useState(false);
const [showPopup, setShowPopup] = useState(false);
...
return (
<>
<Navbar />
{loading ? <Spinner /> : <Body />}
...
{showPopup && <Popup />}
</>
);
};
导入/导出模块
在 ES6 之前,由于 JavaScript 本身不支持模块,我们使用诸如 RequiredJS 或 CommonJS 之类的库来导入/导出模块。你可能之前见过这种情况,尤其是如果你已经使用过 Node.js 的话。
// ES5 with CommonJS
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
...
});
module.exports = router;
在 ES6 中,我们可以原生使用export
andimport
语句来处理应用程序中的模块。
// auth.js
export const login = (email, password) => { ... };
export const register = (name, email, password) => { ... };
// main.js
import { login, register } from './auth';
这在 React 中非常有用,因为我们将应用程序 UI 拆分成了组件层级结构。组件在各自的文件中定义,并在其他文件中被引用,如下例所示:
// Button.js
const Button = ({ size = 'md', disabled = false, children) => (
<button
type="button"
disabled={disabled}
className={`btn-${size}`}
>
{children}
</button>
);
export default Button;
// App.js
import Button from './Button';
const App = () => (
<>
...
<Button size='lg'>Submit</Button>
</>
);
异步/等待
您可能熟悉异步编程的概念。在 JavaScript 中,有很多方法可以处理异步代码(回调、Promise,以及 Q、Bluebird 和 deferred.js 等外部库)。这里我仅讨论其中一种async/await
。
Async/await 是一种特殊的语法,可以更轻松地处理 Promise。它非常易于理解和使用。
如果您需要了解承诺,请查看MDN 文档页面。
您可能已经注意到,有两个新关键字:async
和await
。
我们先从 async 关键字开始。async 用于定义一个异步函数,该函数返回一个隐式的 Promise 作为其结果。
async function myAsyncFunc() {
return "Hello from AlterClass!";
}
// Usage
myAsyncFunc().then(...);
请注意,使用异步函数的代码的语法和结构看起来与常规同步函数类似。很简单,对吧?但是等等!还有一个关键字:await
。
关键字 await 仅在 async 函数内部有效。它使程序等待,直到 Promise 完成并返回其结果。以下是几秒钟后完成解析的 Promise 的示例:
async function myAsyncFunc() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Hello!"), 3000)
});
let result = await promise; // wait until the promise resolves
alert(result); // "Hello!"
}
与使用相比,这是一种获得承诺结果的更加优雅的方式promise.then()
,而且更易于阅读和编写。
⚠️再次强调,await 不能在常规函数中使用,否则会报语法错误。
关于 async/await 还有一点值得一提,那就是如何处理错误。事实上,如果 Promise 正常解析,它会返回结果。但如果被拒绝,它会抛出一个错误。你可以使用 Promisecatch
方法,也try..catch
可以像常规 throw 一样处理拒绝。
asynFunction().catch(error => console.log(error));
// or
try {
asynFunction();
}
catch(error) {
console.log(error)
}
我之所以把 async/await 也加到这个列表中,是因为在每个前端项目中,我们都会做很多需要异步代码的事情。一个常见的例子就是我们想通过 API 调用来获取数据。
在 React 中,我们可以使用 promises + async/await 来实现这一点。
const App = () => {
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
// Check if user is authenticated
const user = await getUser();
// Stop loading spinner
setLoading(false);
};
fetchData().catch(alert);
}, []);
if (loading) {
return <Spinner />;
}
return <>...</>;
};
扩展运算符/剩余参数
扩展运算符和剩余参数用三个点表示...
。扩展运算符将可迭代对象扩展为单个元素。剩余参数则将参数列表的剩余部分收集到一个数组中。
让我们看一些例子来了解它们如何工作以及如何使用它们。
// Rest parameter
function sum(...args) {
let sum = 0;
for (let i = 0; i < args.length; i++) {
sum += args[i];
}
return sum;
}
// Spreading elements on function calls
let array = [10, 6, 4];
console.log(Math.max(...array)); // 10
// Copying an array
let items = ['item1', 'item2', 'item3'];
let newArray = [...items];
console.log(newArray); // ['item1', 'item2', 'item3']
// Concatenating arrays
let array1 = ['1', '2', '3'];
let array2 = ['A', 'B', 'C'];
let result = [...array1, ...array2];
console.log(result); // ['1', '2', '3', 'A', 'B', 'C']
// Spread syntax for object literals
var object1 = { _id: 123, name: 'Greg' }
var object2 = { age: 28, country: 'FR'}
const user = { ...object1, ...object2 }
console.log(user); // { "_id": 123, "name": "Greg", "age": 28, "country": "FR" }
扩展运算符在 Redux 等库中被广泛使用,用于以不可变的方式处理应用程序状态。然而,它也常用于 React,以便轻松地将所有对象的数据作为单独的 props 传递。这比逐个传递每个 props 要简单得多。
如果你之前听说过HOC(高阶组件),你就会知道需要将所有 props 传递给被包装的组件。扩展运算符可以帮你实现这一点。
const withStorage = (WrappedComponent) => {
class WithStorageHOC extends React.Component {
...
render() {
return <WrappedComponent {...this.props} />;
}
}
};
结论
在本文中,我介绍了一些很棒的 ES6+ 特性,可以帮助你构建出色的 React 应用程序。当然,还有很多其他 JavaScript 特性可以使用,但这 10 个特性是我在任何 React 项目中最常看到和使用的。
如果你喜欢这篇文章,别忘了收藏并分享给你的朋友。如果你有任何疑问,欢迎在下方留言。欢迎关注我,获取更多后续文章!
文章来源:https://dev.to/alterclass/10-javascript-concepts-you-should-learn-to-master-react-6o8如果您想了解更多关于 ReactJS 的知识并提升您的 JavaScript 技能,请点击此处查看我的 ReactJS 在线课程。无需任何 ReactJS 经验。您将学习如何使用现代 ReactJS 逐步构建一个真实的应用程序。