关键字
关键字及其作用
-
React
- 作用:从 React 库导入 React 对象,用于创建 React 组件、元素等。
- 示例:
import React from 'react';
-
Component
- 作用:创建类组件时,从 React 库中导入的基类。
- 示例:
class MyComponent extends React.Component { ... }
-
useState
- 作用:React 钩子,用于在函数组件中添加状态。
- 示例:
const [state, setState] = useState(initialState);
-
useEffect
- 作用:React 钩子,用于在函数组件中执行副作用(如数据获取、订阅等)。
- 示例:
useEffect(() => { ... }, [dependencies]);
-
useContext
- 作用:React 钩子,用于在函数组件中访问上下文。
- 示例:
const value = useContext(MyContext);
-
useReducer
- 作用:React 钩子,用于在函数组件中管理复杂状态逻辑。
- 示例:
const [state, dispatch] = useReducer(reducer, initialState);
-
Fragment
- 作用:React 组件,用于返回多个元素而不在 DOM 中添加额外节点。
- 示例:
<React.Fragment>...</React.Fragment>或<></>
-
JSX
- 作用:一种 JavaScript 语法扩展,用于在 React 中描述 UI。
- 示例:
const element = <h1>Hello, world!</h1>;
-
render
- 作用:ReactDOM 的方法,用于将 React 元素渲染到 DOM 中。
- 示例:
ReactDOM.render(<App />, document.getElementById('root'));
代码实例
类组件
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
export default MyComponent;
函数组件
import React, { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default MyComponent;
使用 useEffect 钩子
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
export default MyComponent;
使用 Context API
import React, { useContext } from 'react';
const MyContext = React.createContext();
function MyComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
function App() {
return (
<MyContext.Provider value="Hello, world!">
<MyComponent />
</MyContext.Provider>
);
}
export default App;
这些示例展示了 React 的核心功能和用法,包括类组件和函数组件的创建,以及常用的 React 钩子如 useState 和 useEffect 的使用。
定义变量和类型推断
在 JavaScript 中,变量的定义和类型推断是编程的基本概念。以下是变量定义和类型推断的详细说明和示例。
变量定义
JavaScript 提供了三种方式来定义变量:var、let 和 const。
使用 var 定义变量
var 是最早的变量定义方式,具有函数作用域,但没有块作用域。
var x = 10;
console.log(x); // 输出: 10
if (true) {
var y = 20;
console.log(y); // 输出: 20
}
console.log(y); // 输出: 20,因为 var 没有块作用域
使用 let 定义变量
let 定义块作用域变量,解决了 var 的一些问题。
let x = 10;
console.log(x); // 输出: 10
if (true) {
let y = 20;
console.log(y); // 输出: 20
}
console.log(y); // ReferenceError: y is not defined,因为 let 有块作用域
使用 const 定义常量
const 定义块作用域常量,值一旦定义就不能改变。
const x = 10;
console.log(x); // 输出: 10
x = 20; // TypeError: Assignment to constant variable
const obj = { name: 'Alice' };
obj.name = 'Bob'; // 允许修改对象的属性
console.log(obj.name); // 输出: Bob
类型推断
JavaScript 是一种动态类型语言,变量类型是在运行时确定的。
自动类型推断
JavaScript 会根据赋值自动推断变量类型。
let x = 10; // x 被推断为 number 类型
console.log(typeof x); // 输出: number
let y = 'Hello'; // y 被推断为 string 类型
console.log(typeof y); // 输出: string
let z = true; // z 被推断为 boolean 类型
console.log(typeof z); // 输出: boolean
动态类型
变量的类型可以在运行时更改。
let x = 10;
console.log(typeof x); // 输出: number
x = 'Hello';
console.log(typeof x); // 输出: string
x = true;
console.log(typeof x); // 输出: boolean
类型检查
可以使用 typeof 操作符检查变量的类型。
let x = 10;
console.log(typeof x); // 输出: number
let y = 'Hello';
console.log(typeof y); // 输出: string
let z;
console.log(typeof z); // 输出: undefined
let obj = { name: 'Alice' };
console.log(typeof obj); // 输出: object
let func = function() {};
console.log(typeof func); // 输出: function
let symbol = Symbol();
console.log(typeof symbol); // 输出: symbol
变量提升
var 声明的变量会提升到函数或全局作用域的顶部,但赋值不会提升。
console.log(x); // 输出: undefined,因为 x 被提升了,但还没有赋值
var x = 10;
console.log(x); // 输出: 10
let 和 const 声明的变量不会提升。
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;
console.log(y); // 输出: 10
小结
var定义的变量具有函数作用域,不具有块作用域,并且会发生变量提升。let和const定义的变量具有块作用域,不会发生变量提升。const定义的变量一旦赋值不能改变,但可以修改对象的属性。- JavaScript 是动态类型语言,变量的类型可以在运行时更改。
- 可以使用
typeof操作符检查变量的类型。
基本数据类型和数组
好的,以下是 JavaScript 中基本数据类型和数组的定义及代码实例,使用表格进行展示:
| 数据类型 | 定义 | 示例代码 |
|---|---|---|
| Number | 表示整数或浮点数 | let age = 25; let price = 99.99; |
| String | 表示文本数据 | let name = "John Doe"; let greeting = 'Hello, world!'; |
| Boolean | 表示 true 或 false | let isActive = true; let isAdmin = false; |
| Undefined | 表示变量已声明但尚未赋值 | let myVar; console.log(myVar); // 输出: undefined |
| Null | 表示空值或不存在的值 | let emptyValue = null; |
| Symbol | 表示唯一且不可变的值,通常用于对象属性的唯一标识符 | let sym = Symbol('unique'); |
| BigInt | 表示任意精度的整数 | let bigInt = BigInt(9007199254740991); |
数组
数组是一种特殊的对象,可以存储多个值,并且这些值可以是不同的数据类型。数组使用方括号表示,数组中的元素使用逗号分隔。
| 功能 | 定义 | 示例代码 |
|---|---|---|
| 数组定义 | 使用方括号定义数组,数组中的元素使用逗号分隔 | let numbers = [1, 2, 3, 4, 5]; let mixedArray = [1, 'two', true, null]; |
| 访问元素 | 通过索引访问数组中的元素,索引从0开始 | let firstNumber = numbers[0]; console.log(firstNumber); // 输出: 1 |
| 修改元素 | 通过索引修改数组中的元素 | numbers[1] = 22; console.log(numbers); // 输出: [1, 22, 3, 4, 5] |
| 添加元素 | 使用 push 方法在数组末尾添加元素 | numbers.push(6); console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6] |
| 删除元素 | 使用 pop 方法删除数组末尾的元素 | let lastNumber = numbers.pop(); console.log(numbers); // 输出: [1, 2, 3, 4, 5] |
| 数组长度 | 使用 length 属性获取数组的长度 | let length = numbers.length; console.log(length); // 输出: 5 |
| 数组遍历 | 使用 for 循环遍历数组中的元素 | for (let i = 0; i < numbers.length; i++) { console.log(numbers[i]); } |
| 高阶函数 | 使用数组的高阶函数如 forEach, map, filter 等进行操作 | numbers.forEach(num => console.log(num)); let doubled = numbers.map(num => num * 2); console.log(doubled); // 输出: [2, 4, 6, 8, 10] |
列表和集合容器
列表(数组)
| 功能 | 定义 | 示例代码 |
|---|---|---|
| 数组定义 | 使用方括号定义数组,数组中的元素使用逗号分隔 | let numbers = [1, 2, 3, 4, 5]; let mixedArray = [1, 'two', true, null]; |
| 访问元素 | 通过索引访问数组中的元素,索引从0开始 | let firstNumber = numbers[0]; console.log(firstNumber); // 输出: 1 |
| 修改元素 | 通过索引修改数组中的元素 | numbers[1] = 22; console.log(numbers); // 输出: [1, 22, 3, 4, 5] |
| 添加元素 | 使用 push 方法在数组末尾添加元素 | numbers.push(6); console.log(numbers); // 输出: [1, 2, 3, 4, 5, 6] |
| 删除元素 | 使用 pop 方法删除数组末尾的元素 | let lastNumber = numbers.pop(); console.log(numbers); // 输出: [1, 2, 3, 4, 5] |
| 数组长度 | 使用 length 属性获取数组的长度 | let length = numbers.length; console.log(length); // 输出: 5 |
| 数组遍历 | 使用 for 循环遍历数组中的元素 | for (let i = 0; i < numbers.length; i++) { console.log(numbers[i]); } |
| 高阶函数 | 使用数组的高阶函数如 forEach, map, filter 等 | numbers.forEach(num => console.log(num)); let doubled = numbers.map(num => num * 2); console.log(doubled); // 输出: [2, 4, 6, 8, 10] |
Set
| 功能 | 定义 | 示例代码 |
|---|---|---|
| Set 定义 | 使用 Set 构造函数创建一个新的 Set 对象 | let mySet = new Set(); |
| 添加元素 | 使用 add 方法向 Set 添加元素 | mySet.add(1); mySet.add(5); mySet.add(5); // 重复元素不会被添加 |
| 删除元素 | 使用 delete 方法从 Set 中删除元素 | mySet.delete(1); |
| 检查元素 | 使用 has 方法检查 Set 中是否包含某个元素 | console.log(mySet.has(5)); // 输出: true |
| Set 大小 | 使用 size 属性获取 Set 中元素的数量 | console.log(mySet.size); // 输出: 1 |
| 遍历 Set | 使用 forEach 方法或 for...of 循环遍历 Set | mySet.forEach(value => console.log(value)); for (let value of mySet) { console.log(value); } |
Map
| 功能 | 定义 | 示例代码 |
|---|---|---|
| Map 定义 | 使用 Map 构造函数创建一个新的 Map 对象 | let myMap = new Map(); |
| 设置键值对 | 使用 set 方法向 Map 添加键值对 | myMap.set('key1', 'value1'); myMap.set(2, 'value2'); |
| 获取值 | 使用 get 方法根据键获取值 | console.log(myMap.get('key1')); // 输出: 'value1' |
| 删除键值对 | 使用 delete 方法从 Map 中删除键值对 | myMap.delete(2); |
| 检查键 | 使用 has 方法检查 Map 中是否包含某个键 | console.log(myMap.has('key1')); // 输出: true |
| Map 大小 | 使用 size 属性获取 Map 中键值对的数量 | console.log(myMap.size); // 输出: 1 |
| 遍历 Map | 使用 forEach 方法或 for...of 循环遍历 Map | myMap.forEach((value, key) => console.log(key, value)); for (let [key, value] of myMap) { console.log(key, value); } |
WeakSet
| 功能 | 定义 | 示例代码 |
|---|---|---|
| WeakSet 定义 | 使用 WeakSet 构造函数创建一个新的 WeakSet 对象 | let myWeakSet = new WeakSet(); |
| 添加元素 | 使用 add 方法向 WeakSet 添加对象 | let obj = {}; myWeakSet.add(obj); |
| 删除元素 | 使用 delete 方法从 WeakSet 中删除对象 | myWeakSet.delete(obj); |
| 检查元素 | 使用 has 方法检查 WeakSet 中是否包含某个对象 | console.log(myWeakSet.has(obj)); // 输出: true |
WeakMap
| 功能 | 定义 | 示例代码 |
|---|---|---|
| WeakMap 定义 | 使用 WeakMap 构造函数创建一个新的 WeakMap 对象 | let myWeakMap = new WeakMap(); |
| 设置键值对 | 使用 set 方法向 WeakMap 添加键值对 | let obj = {}; myWeakMap.set(obj, 'value'); |
| 获取值 | 使用 get 方法根据键获取值 | console.log(myWeakMap.get(obj)); // 输出: 'value' |
| 删除键值对 | 使用 delete 方法从 WeakMap 中删除键值对 | myWeakMap.delete(obj); |
| 检查键 | 使用 has 方法检查 WeakMap 中是否包含某个键 | console.log(myWeakMap.has(obj)); // 输出: true |
锁的使用和并发控制及异步能力
锁的使用和并发控制
JavaScript 本身不直接提供锁的机制,但可以通过一些库和新的 API 实现并发控制。以下是一些常用的并发控制方法及代码示例:
Mutex(互斥锁)
使用第三方库 async-mutex 实现互斥锁:
import { Mutex } from 'async-mutex';
const mutex = new Mutex();
async function criticalSection() {
await mutex.runExclusive(async () => {
// 关键代码
console.log('Executing critical section');
await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作
});
}
criticalSection();
criticalSection();
Atomic 操作
使用 Atomics 和 SharedArrayBuffer 进行低级并发控制:
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
Atomics.add(sharedArray, 0, 1);
console.log(Atomics.load(sharedArray, 0)); // 输出: 1
Web Workers
使用 Web Workers 实现并发执行:
// worker.js
onmessage = function(event) {
if (event.data === 'start') {
let result = 0;
for (let i = 0; i < 1e9; i++) {
result += i;
}
postMessage(result);
}
};
// main.js
const worker = new Worker('worker.js');
worker.postMessage('start');
worker.onmessage = (event) => {
console.log(event.data); // 输出计算结果
};
异步能力
JavaScript 的异步能力主要通过回调函数、Promise、async/await 以及事件循环实现。
回调函数
通过回调函数处理异步操作:
function asyncOperation(callback) {
setTimeout(() => {
callback('done');
}, 1000);
}
asyncOperation(result => {
console.log(result); // 输出: done
});
Promise
使用 Promise 对象进行异步操作:
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('done');
}, 1000);
});
}
asyncOperation().then(result => {
console.log(result); // 输出: done
});
async/await
使用 async/await 语法糖简化异步操作:
function asyncOperation() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('done');
}, 1000);
});
}
async function run() {
const result = await asyncOperation();
console.log(result); // 输出: done
}
run();
事件循环
通过事件循环机制管理异步操作:
console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise');
});
console.log('end');
// 输出: start, end, promise, timeout
综合示例:并发控制与异步能力结合
使用 async 函数和互斥锁管理异步任务队列:
import { Mutex } from 'async-mutex';
const mutex = new Mutex();
async function asyncTask(name) {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(name + ' done');
}
async function run() {
await mutex.runExclusive(async () => {
await asyncTask('Task 1');
});
await mutex.runExclusive(async () => {
await asyncTask('Task 2');
});
}
run();
定义函数和方法
在 JavaScript 中,方法和函数是基本的编程构造,用于定义可重用的代码块。函数是独立的代码块,而方法是对象的属性。以下是函数和方法的定义及其用法示例:
函数定义
函数声明
通过 function 关键字定义一个命名函数:
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet('Alice')); // 输出: Hello, Alice!
函数表达式
将函数赋值给一个变量,可以是命名或匿名函数:
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet('Bob')); // 输出: Hello, Bob!
箭头函数
使用箭头函数语法定义函数,箭头函数不能作为构造函数使用,也没有自己的 this:
const greet = (name) => `Hello, ${name}!`;
console.log(greet('Charlie')); // 输出: Hello, Charlie!
方法定义
方法是对象的属性,可以使用函数表达式或箭头函数定义:
对象方法
使用函数表达式定义对象的方法:
const person = {
name: 'Alice',
greet: function() {
return `Hello, my name is ${this.name}`;
}
};
console.log(person.greet()); // 输出: Hello, my name is Alice
简写方法定义
ES6 允许在对象中直接定义方法:
const person = {
name: 'Bob',
greet() {
return `Hello, my name is ${this.name}`;
}
};
console.log(person.greet()); // 输出: Hello, my name is Bob
箭头函数作为方法
箭头函数没有自己的 this,它会捕获定义时所在上下文的 this 值,不适合作为对象的方法:
const person = {
name: 'Charlie',
greet: () => {
return `Hello, my name is ${this.name}`; // this.name 是 undefined
}
};
console.log(person.greet()); // 输出: Hello, my name is undefined
函数与方法的区别
- 函数 是独立的代码块,定义后可以在任何地方调用。函数可以使用函数声明、函数表达式或箭头函数语法定义。
- 方法 是对象的属性,其值是一个函数。方法可以使用函数表达式或对象简写语法定义,但箭头函数不适合作为方法,因为它没有自己的
this。
OOP
在 React 中,面向对象编程(OOP)主要通过类组件来实现。尽管函数组件和 Hooks 现在更流行,但理解类组件及其与 OOP 的结合仍然非常重要。以下是 React 中如何应用 OOP 的详细说明和示例。
类组件
类组件是 React 中的一种组件定义方式,通过扩展 React.Component 类来创建。
定义类组件
使用 ES6 类语法定义类组件,并在其中定义状态和生命周期方法。
import React, { Component } from 'react';
class Greeting extends Component {
constructor(props) {
super(props);
this.state = { name: 'Alice' };
}
render() {
return (
<div>
Hello, {this.state.name}!
</div>
);
}
}
export default Greeting;
状态管理
类组件使用 this.state 定义状态,并使用 this.setState 更新状态。
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState(prevState => ({ count: prevState.count + 1 }));
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
export default Counter;
生命周期方法
类组件提供了一系列生命周期方法,用于在组件的不同阶段执行特定操作。
import React, { Component } from 'react';
class LifecycleDemo extends Component {
constructor(props) {
super(props);
console.log('Constructor');
}
componentDidMount() {
console.log('Component did mount');
}
componentDidUpdate(prevProps, prevState) {
console.log('Component did update');
}
componentWillUnmount() {
console.log('Component will unmount');
}
render() {
console.log('Render');
return (
<div>
Check the console for lifecycle method logs.
</div>
);
}
}
export default LifecycleDemo;
继承与组合
在 React 中,推荐使用组合而不是继承来重用代码和逻辑。
组件继承
虽然在 React 中继承较少见,但可以实现简单的继承。
import React, { Component } from 'react';
class Button extends Component {
render() {
return (
<button>{this.props.label}</button>
);
}
}
class SubmitButton extends Button {
render() {
return (
<button type="submit">{this.props.label}</button>
);
}
}
export default SubmitButton;
组件组合
组合是 React 中更推荐的代码重用方式。
import React from 'react';
const FancyBorder = (props) => {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
};
const WelcomeDialog = () => {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">Welcome</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
};
export default WelcomeDialog;
高阶组件(HOC)
高阶组件是一种函数,接受一个组件并返回一个新的组件,以增强其功能。
import React, { Component } from 'react';
const withLogging = (WrappedComponent) => {
return class extends Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
class MyComponent extends Component {
render() {
return <div>MyComponent</div>;
}
}
export default withLogging(MyComponent);
结论
在 React 中,类组件提供了实现面向对象编程的途径,支持状态管理、生命周期方法以及通过继承和组合实现代码重用。虽然函数组件和 Hooks 现在更为流行,但理解类组件和 OOP 的概念依然对全面掌握 React 开发非常重要。
以下是总结的关键点:
- 类组件:通过继承
React.Component创建类组件,定义状态和生命周期方法。 - 状态管理:使用
this.state和this.setState管理组件状态。 - 生命周期方法:使用
componentDidMount、componentDidUpdate、componentWillUnmount等方法控制组件生命周期中的不同阶段。 - 继承与组合:推荐使用组合而非继承来重用代码。
- 高阶组件:通过高阶组件增强组件功能,实现逻辑重用。
项目目录结构
在 React 项目中,良好的目录结构可以提高代码的可读性和可维护性。以下是一个典型的 React 项目目录结构示例,适用于中小型项目。在大型项目中,您可能需要根据具体需求进行调整和扩展。
my-react-app/
├── node_modules/
├── public/
│ ├── index.html
│ ├── favicon.ico
│ └── ...
├── src/
│ ├── assets/
│ │ ├── images/
│ │ └── styles/
│ ├── components/
│ │ ├── common/
│ │ │ ├── Button.js
│ │ │ ├── Header.js
│ │ │ └── ...
│ │ ├── layout/
│ │ │ ├── Navbar.js
│ │ │ ├── Sidebar.js
│ │ │ └── ...
│ │ └── ...
│ ├── pages/
│ │ ├── HomePage.js
│ │ ├── AboutPage.js
│ │ ├── ContactPage.js
│ │ └── ...
│ ├── hooks/
│ │ ├── useAuth.js
│ │ ├── useFetch.js
│ │ └── ...
│ ├── services/
│ │ ├── api.js
│ │ └── ...
│ ├── utils/
│ │ ├── helpers.js
│ │ ├── constants.js
│ │ └── ...
│ ├── App.js
│ ├── index.js
│ └── ...
├── .gitignore
├── package.json
├── README.md
└── ...
目录结构详细说明
-
node_modules/:- 存放项目的所有依赖包。这个目录是由
npm或yarn自动生成的,不需要手动管理。
- 存放项目的所有依赖包。这个目录是由
-
public/:- 存放公共资源,例如
index.html是项目的主 HTML 文件,favicon.ico是网站图标,其他静态资源也可以放在这里。
- 存放公共资源,例如
-
src/:-
项目源代码的根目录,所有 React 组件、样式和业务逻辑都在这里。
-
assets/:- 存放静态资源,如图片、字体、全局样式等。
images/: 存放图片文件。styles/: 存放全局样式文件。
-
components/:- 存放可重用的组件。
common/: 通用组件,如按钮、输入框等。layout/: 布局相关组件,如导航栏、侧边栏等。
-
pages/:- 存放页面组件,每个页面对应一个文件或文件夹。
-
hooks/:- 存放自定义 Hooks,用于抽离和复用状态逻辑。
useAuth.js: 示例自定义 Hook,用于处理认证逻辑。useFetch.js: 示例自定义 Hook,用于处理数据获取。
-
services/:- 存放与服务器通信的代码,例如 API 请求。
api.js: 示例 API 服务文件。
-
utils/:- 存放工具函数、常量等辅助功能代码。
helpers.js: 示例工具函数文件。constants.js: 示例常量文件。
-
App.js:- 主应用组件,是应用的根组件,包含路由定义和全局状态管理等。
-
index.js:- 应用的入口文件,将
App组件挂载到 DOM 中。
- 应用的入口文件,将
-
-
.gitignore:- Git 忽略文件,定义哪些文件和目录不应该被提交到版本控制系统。
-
package.json:- 项目的配置文件,定义项目的依赖、脚本和其他项目信息。
-
README.md:- 项目说明文件,通常包含项目的简介、安装和使用说明等。