JavaScript 核心语法速通:从基础到 React 开发实战
适合人群:有 Go、Python、Java 等编程经验,希望快速掌握 JavaScript 并进入 React + TypeScript 开发的工程师。
学习目标:掌握 JavaScript 中最重要的变量、数据类型、对象、数组、函数、解构、Promise、async/await 和模块化语法。
一、JavaScript 语法知识体系
JavaScript 与 Go、Python 等语言的核心编程思想类似,但它有一些非常重要的语言特性。
| 模块 | 核心知识 | 优先级 |
|---|---|---|
| 变量声明 | const、let、var | P0 |
| 数据类型 | string、number、boolean、null、undefined | P0 |
| 运算符 | ===、!==、?.、??、... | P0 |
| 条件与循环 | if、for、for...of | P0 |
| 数组 | map、filter、find、reduce | P0 |
| 对象 | 属性访问、展开运算符、解构 | P0 |
| 函数 | 箭头函数、回调函数 | P0 |
| 异步编程 | Promise、async/await | P0 |
| 模块化 | import、export | P0 |
| DOM 和事件 | querySelector、addEventListener | P0 |
| this、原型链、闭包 | 语言运行机制 | P1 |
| class | 类与继承 | P1 |
二、变量声明:let、const、var
JavaScript 有三种常见的变量声明方式:
ini
var a = 10;
let b = 20;
const c = 30;
1. let:可以重新赋值
ini
let count = 0;
count = 1;
count = 2;
console.log(count); // 2
类似 Go 中可以重新赋值的变量。
2. const:不能重新赋值
ini
const name = "Kevin";
// 报错
name = "Tom";
注意:const 限制的是变量绑定,不是对象内部数据完全不可修改。
ini
const user = {
name: "Kevin",
age: 25
};
// 可以修改对象属性
user.age = 26;
// 不允许重新赋值
user = {};
React 项目中通常优先使用 const,只有需要重新赋值时才使用 let。
3. var:旧式变量声明
ini
var count = 10;
var 和 let 的重要区别是作用域:
var:函数作用域。let:块级作用域。const:块级作用域。
一句话理解:作用域就是变量能在哪些地方被访问。
var:函数作用域,只要在同一个函数内部,通常就能访问。let:块级作用域,只能在声明它的{}代码块内部访问。
现代开发中,一般不推荐继续使用 var。
结论:默认 const,需要重新赋值时使用 let。
三、JavaScript 数据类型
JavaScript 是动态类型语言,变量不需要提前声明类型。
ini
let value = 100;
value = "hello";
value = true;
JavaScript 的原始类型主要有七种:
| 类型 | 示例 |
|---|---|
| string | "hello" |
| number | 123、3.14 |
| boolean | true、false |
| undefined | undefined |
| null | null |
| bigint | 123n |
| symbol | Symbol("id") |
此外,对象类型 object 包括普通对象、数组、函数等引用值。
1. string:字符串
ini
const name = "Kevin";
const age = 25;
推荐掌握模板字符串:
ini
const message = `姓名:${name},年龄:${age}`;
console.log(message);
${} 可以在模板字符串中嵌入表达式。
2. number:数字
JavaScript 普通数字统一使用 number,不区分 int、float 类型。
ini
const a = 10;
const b = 3.14;
需要注意:JavaScript Number 是 IEEE 754 双精度浮点数。
例如:
arduino
console.log(0.1 + 0.2);
// 0.30000000000000004
另外,超大整数 ID 需要特别注意精度问题。
例如后端返回一个超过 Number.MAX_SAFE_INTEGER 的任务 ID,前端最好使用字符串保存,避免精度丢失。
3. null 和 undefined
这是 JavaScript 的重要区别。
ini
let a;
console.log(a); // undefined
const b = null;
通常可以理解为:
undefined:没有提供值。null:显式表示空值。
例如:
arduino
const user = {
name: "Kevin"
};
console.log(user.age); // undefined
4. typeof:查看数据类型
javascript
console.log(typeof 123); // number
console.log(typeof "hello"); // string
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
特殊情况:
javascript
console.log(typeof null); // object
这是 JavaScript 历史遗留行为,不代表 null 真的是普通对象。
四、运算符:重点学习 ===、?.、??、...
1. == 和 ===
JavaScript 有两种相等比较:
ini
console.log(1 == "1"); // true
console.log(1 === "1"); // false
区别:
==:允许类型转换后比较。===:不进行这种隐式类型转换。
实际开发中,推荐优先使用 === 和 !== 。
ini
const status = 1;
if (status === 1) {
console.log("运行中");
}
2. 可选链操作符 ?.
例如:
css
const user = {
profile: {
name: "Kevin"
}
};
传统写法:
ini
const name = user && user.profile && user.profile.name;
现代写法:
ini
const name = user?.profile?.name;
?. 的含义是:
如果左侧为 null 或 undefined,就停止访问并返回 undefined,而不是抛出属性访问错误。
例如:
ini
const user = null;
console.log(user?.profile?.name);
// undefined
React 项目中非常常见:
ini
const username = response?.data?.user?.name;
3. 空值合并运算符 ??
ini
const username = null;
const name = username ?? "默认用户";
console.log(name); // 默认用户
只有左边是 null 或 undefined 时,才使用右边的默认值。
4. ?? 和 || 的区别
arduino
const count = 0;
console.log(count || 100); // 100
console.log(count ?? 100); // 0
原因:
||:左边是假值时,返回右边。??:左边是 null 或 undefined 时,返回右边。
在业务开发中,如果 0 是合法数值,通常更适合使用 ??。
5. 展开运算符 ...
这是 React 开发中非常重要的语法。
数组:
ini
const a = [1, 2];
const b = [...a, 3, 4];
console.log(b); // [1, 2, 3, 4]
对象:
ini
const user = {
name: "Kevin",
age: 25
};
const newUser = {
...user,
age: 26
};
输出:
css
{
name: "Kevin",
age: 26
}
注意:... 的对象展开是浅拷贝,不会递归复制嵌套对象。
五、条件判断与循环
1. if / else
lua
const status = "running";
if (status === "running") {
console.log("任务运行中");
} else if (status === "failed") {
console.log("任务失败");
} else {
console.log("未知状态");
}
2. 三元运算符
ini
const age = 20;
const result = age >= 18 ? "成年人" : "未成年人";
等价于简单的 if/else 赋值。
在 React JSX 中尤其常见:
arduino
function Status({ running }) {
return (
<span>
{running ? "运行中" : "已停止"}
</span>
);
}
3. for 循环
css
for (let i = 0; i < 5; i++) {
console.log(i);
}
4. for...of
用于遍历可迭代对象的值。
ini
const tasks = ["任务A", "任务B", "任务C"];
for (const task of tasks) {
console.log(task);
}
5. for...in
用于遍历对象的可枚举字符串属性键。
vbnet
const user = {
name: "Kevin",
age: 25
};
for (const key in user) {
console.log(key, user[key]);
}
注意:for...in 也可能遍历继承的可枚举属性。只遍历普通对象自身键时,通常推荐 Object.keys()、Object.entries()。
六、数组:React 中最重要的数据处理知识
JavaScript 的数组类似 Python 的 list。
bash
const tasks = [
{ id: 1, name: "任务A", status: "running" },
{ id: 2, name: "任务B", status: "failed" },
{ id: 3, name: "任务C", status: "running" }
];
需要重点掌握以下方法。
1. map:映射转换
ini
const nums = [1, 2, 3];
const result = nums.map(num => num * 2);
console.log(result); // [2, 4, 6]
理解为:
将数组的每个元素经过函数处理,生成一个新数组。
React 中经常使用 map 渲染列表:
javascript
function TaskList() {
const tasks = ["任务A", "任务B", "任务C"];
return (
<div>
{tasks.map(task => (
<div key={task}>{task}</div>
))}
</div>
);
}
其中 key 用于帮助 React 识别列表元素,应该在同级列表中保持稳定且唯一。
2. filter:过滤数据
ini
const nums = [1, 2, 3, 4, 5];
const result = nums.filter(num => num > 3);
console.log(result); // [4, 5]
业务场景:
ini
const runningTasks = tasks.filter(
task => task.status === "running"
);
这个方法非常适合筛选任务列表。
3. find:查找元素
ini
const task = tasks.find(task => task.id === 2);
console.log(task);
find 返回第一个满足条件的元素,找不到返回 undefined。
4. some 和 every
ini
const nums = [1, 2, 3];
console.log(nums.some(n => n > 2)); // true
console.log(nums.every(n => n > 0)); // true
some:是否存在至少一个满足条件的元素。every:是否所有元素都满足条件。
5. reduce:聚合
ini
const nums = [1, 2, 3, 4];
const sum = nums.reduce((total, num) => {
return total + num;
}, 0);
console.log(sum); // 10
reduce 常用于统计、分组和聚合数据。
例如统计运行中的任务数量:
ini
const count = tasks.reduce((total, task) => {
return total + (task.status === "running" ? 1 : 0);
}, 0);
6. push 和 map 的区别
ini
const a = [1, 2];
a.push(3); // 修改原数组
而:
ini
const a = [1, 2];
const b = a.map(x => x * 2); // 创建新数组
React 中尤其要注意,不要直接修改 State 数组:
scss
// 不推荐
tasks.push(newTask);
setTasks(tasks);
// 推荐
setTasks([...tasks, newTask]);
数组最重要的五个方法:map、filter、find、some、reduce。
七、对象:属性访问与解构赋值
JavaScript 对象类似 Python 字典,或 Go 中用于保存动态字段的 map,但具有自己的属性与原型机制。
arduino
const user = {
name: "Kevin",
age: 25,
city: "Beijing"
};
1. 访问对象属性
sql
console.log(user.name);
console.log(user["name"]);
两种写法均可。
当属性名需要动态决定时:
ini
const key = "age";
console.log(user[key]); // 25
2. 修改对象属性
ini
user.age = 26;
3. 解构赋值(非常重要)
传统写法:
ini
const name = user.name;
const age = user.age;
解构写法:
arduino
const { name, age } = user;
相当于一次性从对象中取出多个属性。
也可以重命名:
arduino
const { name: userName } = user;
console.log(userName); // Kevin
4. 数组解构
ini
const nums = [10, 20, 30];
const [a, b] = nums;
console.log(a); // 10
console.log(b); // 20
React 的 useState 正是使用数组解构:
scss
const [count, setCount] = useState(0);
这里:
count:当前状态值。setCount:更新状态的函数。
八、函数:普通函数、箭头函数和回调函数
1. 普通函数
css
function add(a, b) {
return a + b;
}
2. 箭头函数
css
const add = (a, b) => {
return a + b;
};
单表达式可以简写:
css
const add = (a, b) => a + b;
3. 函数可以作为参数
scss
function execute(callback) {
callback();
}
execute(() => {
console.log("执行回调");
});
这种写法在 JavaScript 事件、Promise 和 React 中非常常见。
4. 默认参数
javascript
function greet(name = "Guest") {
console.log(`Hello ${name}`);
}
greet(); // Hello Guest
greet("Kevin"); // Hello Kevin
5. 剩余参数
javascript
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6
这里的 ...nums 用于接收不定数量的参数。
它与前面数组或对象的展开语法使用相同的 ... 符号,但作用不同。
九、异步编程:Promise 和 async/await
这是 JavaScript 需要重点学习的部分。
1. 为什么需要异步?
例如浏览器请求后端接口:
scss
fetch("/api/tasks");
网络请求需要时间。
JavaScript 不应该因为等待接口返回,就阻塞整个页面的正常交互。
2. Promise
Promise 用来表示一个异步操作最终的完成或失败结果。
ini
fetch("/api/tasks")
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
执行流程:
- 发起 HTTP 请求。
- 等待请求完成。
- 请求成功后处理数据。
- 发生错误时进入 catch。
注意,fetch 默认只会因为网络等问题拒绝 Promise;HTTP 404、500 等状态需要通过 response.ok 自己判断。
3. async/await(重点)
现代开发中更推荐使用 async/await 组织异步逻辑:
javascript
async function getTasks() {
try {
const response = await fetch("/api/tasks");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error("请求失败", error);
}
}
理解两个关键词:
async:声明异步函数,返回值会被包装成 Promise。await:等待 Promise 结果,在当前 async 函数中暂停后续代码执行。
注意:await 不意味着阻塞整个 JavaScript 主线程等待网络响应。
4. Promise.all:并发执行
例如需要同时请求任务列表和统计数据:
javascript
async function loadData() {
const [tasks, stats] = await Promise.all([
fetch("/api/tasks").then(r => r.json()),
fetch("/api/stats").then(r => r.json())
]);
console.log(tasks, stats);
}
Promise.all 会等待所有 Promise 成功完成;只要其中一个拒绝,返回的 Promise 就会拒绝。
后端开发者需要特别注意:Promise 是异步结果的抽象,不等价于 Go 的 goroutine。
十、模块化:import 和 export
现代前端项目通常采用 ES Module。
1. 命名导出
utils.js:
javascript
export function add(a, b) {
return a + b;
}
export const API_URL = "/api";
其他文件导入:
csharp
import { add, API_URL } from "./utils.js";
2. 默认导出
TaskList.js:
javascript
export default function TaskList() {
return "任务列表";
}
导入:
javascript
import TaskList from "./TaskList.js";
区别:
| 类型 | 导出 | 导入 |
|---|---|---|
| 命名导出 | export const a |
import { a } |
| 默认导出 | export default a |
import a |
一个模块可以有多个命名导出,但最多只有一个默认导出。
React 项目中经常使用:
javascript
import React from "react";
import { useState, useEffect } from "react";
十一、DOM 与事件:JavaScript 如何操作 HTML?
HTML:
bash
<button id="addBtn">新增任务</button>
JavaScript:
javascript
const button = document.getElementById("addBtn");
button.addEventListener("click", () => {
console.log("新增任务");
});
事件监听中的箭头函数就是回调函数。
特别注意:
less
// 正确:传递函数
button.addEventListener("click", handleClick);
// 错误用法:立即执行函数并传入返回值
button.addEventListener("click", handleClick());
如果需要传递参数:
scss
button.addEventListener("click", () => {
handleClick(123);
});
这个知识点可以直接迁移到 React 的 onClick。
十二、React 中最常见的 JavaScript 语法组合
假设现在需要开发一个任务管理页面。
javascript
import { useState } from "react";
export default function TaskPage() {
const [tasks, setTasks] = useState([
{ id: 1, name: "任务A", status: "running" },
{ id: 2, name: "任务B", status: "failed" }
]);
// 筛选运行中的任务
const runningTasks = tasks.filter(
task => task.status === "running"
);
// 新增任务
const addTask = () => {
const newTask = {
id: Date.now(),
name: "新任务",
status: "running"
};
setTasks(prev => [...prev, newTask]);
};
// 删除任务
const deleteTask = (id) => {
setTasks(prev =>
prev.filter(task => task.id !== id)
);
};
return (
<div>
<h2>任务管理</h2>
<p>运行中:{runningTasks.length}</p>
<button onClick={addTask}>
新增任务
</button>
{tasks.map(task => (
<div key={task.id}>
<span>{task.name}</span>
<button
onClick={() => deleteTask(task.id)}
>
删除
</button>
</div>
))}
</div>
);
}
这里几乎覆盖了 React 中最常用的 JavaScript 语法:
| 代码 | 对应知识点 |
|---|---|
const |
变量声明 |
{ id, name } |
对象 |
[tasks, setTasks] |
数组解构 |
task => ... |
箭头函数 |
filter() |
数组过滤 |
map() |
数组映射 |
...prev |
展开运算符 |
!== |
严格不等 |
onClick={addTask} |
函数作为回调 |
import / export |
ES Module |
因此,真正进入 React 开发之后,你会发现大量代码都围绕这些基础 JavaScript 语法展开。
十三、需要了解但可以后学的知识
1. 闭包(Closure)
javascript
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
内部函数可以持续访问外部函数作用域中的变量,这就是闭包的重要表现。
React Hooks 与闭包关系密切,后续需要进一步理解。
2. this
javascript
const user = {
name: "Kevin",
sayHello() {
console.log(this.name);
}
};
user.sayHello(); // Kevin
普通函数中的 this 通常取决于调用方式。
箭头函数则没有自己的 this,而是从外层词法作用域获取。
3. class
javascript
class User {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(this.name);
}
}
const user = new User("Kevin");
user.sayHello();
现代 React 更常使用函数组件,因此 class 基础了解即可,后续根据项目需求深入。
十四、总结:JavaScript 应该如何快速学习?
对于 React + TypeScript 中后台开发,建议按这个顺序:
第一阶段:基础语法
- const、let
- 数据类型
- 对象和数组
- 条件与循环
- 箭头函数
第二阶段:现代 JavaScript 核心
- 解构赋值
- 展开运算符
- 可选链
?. - 空值合并
?? - map、filter、find、reduce
- 回调函数
第三阶段:前端工程必备
- Promise
- async/await
- import/export
- DOM 与事件
第四阶段:深入理解
- 闭包
- this
- 原型链
- 事件循环
- JavaScript 内存机制
最后记住
对于已经掌握其他编程语言的工程师,JavaScript 不需要从最基础的循环、判断反复练习。
真正值得重点掌握的是:
箭头函数 + 数组操作 + 对象解构 + 展开运算符 + Promise/async-await + ES Module。
掌握这些内容后,就具备了阅读大部分 React 业务代码所需的 JavaScript 语法基础。