# react_cainiao
**Repository Path**: jiayouyc/react_cainiao
## Basic Information
- **Project Name**: react_cainiao
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-01-22
- **Last Updated**: 2026-01-28
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# React 父子组件通信完整知识点整理
## 🎯 学习目标
掌握React组件间通信的核心概念和实现方式
## 📚 核心知识点
### 1. React组件基础概念
#### 什么是组件?
- **定义**:可复用的UI模块,像乐高积木一样构建应用
- **特点**:独立封装、可复用、可组合
- **类比**:组件就像是家里的电器,每个都有特定功能
#### 组件类型
```jsx
// 类组件 (Class Component)
// 特点:有内部状态、生命周期方法
class ParentComponent extends React.Component {
state = { message: '' }
render() {
return
{this.state.message}
}
}
// 函数组件 (Function Component)
// 特点:简洁、主要接收props
const ChildComponent = (props) => {
return
}
```
### 2. 单向数据流原则
#### 核心概念
- **数据流向**:只能从父组件流向子组件
- **类比**:像自来水管,水只能从上往下流
- **为什么**:保证数据可预测性,便于调试
#### 数据流动限制
```jsx
// ✅ 正确:父→子传递数据
// ❌ 错误:子组件不能直接修改父组件数据
function Child({data}) {
data.value = "修改"; // 不允许!
}
```
### 3. Props(属性)详解
#### Props是什么?
```jsx
// 类比:函数的参数
function calculate(a, b) { return a + b; }
// React中:props就是组件的参数
```
#### Props的特性
- **只读性**:子组件不能修改props
- **传递性**:父组件可以传递任何类型的数据
- **必要性**:建立父子组件间的数据桥梁
#### 传递多种数据类型
```jsx
```
### 4. State(状态)管理
#### State是什么?
- **定义**:组件内部的状态数据
- **作用**:存储会变化的数据
- **特点**:修改State会触发重新渲染
#### 类组件State管理
```jsx
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
message: ""
};
}
// 正确更新State的方式
increment = () => {
this.setState({
count: this.state.count + 1
});
}
}
```
#### 重要原则
- **不可直接修改**:`this.state.count = 1`是错误的!
- **必须用setState**:`this.setState({count: 1})`
- **异步更新**:setState可能异步执行
### 5. 父子组件通信模式
#### 通信方向对比
```
┌─────────────┐ ┌─────────────┐
│ 父组件 │─────────▶│ 子组件 │
│ │ props │ │
│ 状态管理 │ │ UI展示 │
│ 业务逻辑 │ │ 用户交互 │
└─────────────┘ └─────────────┘
▲
│ 回调函数
│
┌───┴───┐
│ 数据 │
│ 事件 │
└───────┘
```
#### 父→子通信(Props传递)
```jsx
// 父组件
function Parent() {
const data = "这是父组件的数据";
return (
父组件
);
}
// 子组件
function ChildComponent({message}) {
return (
);
}
```
#### 子→父通信(回调函数)
```jsx
// 父组件
class Parent extends React.Component {
state = { receivedData: '' };
// 定义回调函数
handleDataFromChild = (data) => {
this.setState({ receivedData: data });
}
render() {
return (
父组件收到的数据: {this.state.receivedData}
{/* 传递回调函数作为props */}
);
}
}
// 子组件
function ChildComponent({onData}) {
const sendData = () => {
// 调用父组件传递的回调函数
onData("这是来自子组件的数据");
};
return ;
}
```
### 6. 回调函数深入解析
#### 回调函数是什么?
```jsx
// 类比:餐厅点餐
type MenuItem = {
name: string;
price: number;
onOrder: (item: MenuItem) => void;
};
// React中的回调函数
function Child({onAction}) {
// onAction就像是服务员,收到订单就去厨房告诉师傅
const handleClick = () => {
onAction("红烧肉"); // 告诉父组件用户要点什么
};
return ;
}
```
#### 回调函数执行流程
```mermaid
graph LR
A[父组件定义回调函数] --> B[作为props传递给子组件]
B --> C[子组件收到回调函数]
C --> D[用户交互触发事件]
D --> E[子组件调用回调函数]
E --> F[回调函数在父组件执行]
F --> G[更新父组件状态]
G --> H[重新渲染UI]
```
#### 回调函数编写技巧
```jsx
// 1. 箭头函数避免this绑定问题
handleData = (data) => {
this.setState({result: data});
}
// 2. 传递额外参数
this.handleAction(data, this.state.id)}
/>
// 3. 传递多个回调
```
### 7. 组件渲染机制
#### 初次渲染流程
```
1. 应用启动 → ReactDOM.render()
2. 父组件创建 → constructor() → 初始化state
3. 父组件render() → 创建子组件
4. 子组件创建 → 接收props
5. 子组件render() → 生成JSX
6. 虚拟DOM创建
7. 真实DOM更新
```
#### 状态更新渲染
```
1. setState() 调用
2. React对比新旧state
3. Diff算法对比虚拟DOM
4. 只更新发生变化的部分
5. 批量更新DOM以提高性能
```
### 8. 组件设计最佳实践
#### 🏠 父组件与子组件的关系详解(房子装修类比)
**父组件就像房子,子组件就像房间里的家具**
```jsx
// 父组件 - 整套房子
function Parent() {
return (
我的家
{/* 客厅区域 - 子组件 */}
{/* 卧室区域 - 子组件 */}
{/* 厨房区域 - 子组件 */}
);
}
// 子组件 - 客厅
function LivingRoom() {
return (
<沙发 />
<茶几 />
<电视 />
);
}
```
🔍 **重要结论:是的,子组件就是写在父组件里面的!**
就像你会把客厅、卧室、厨房包含在房子的描述中一样。
#### 🍽️ 回调函数通信详解(餐厅点餐类比)
**用最简单的餐厅点餐来理解回调函数**
```jsx
// 父组件 - 厨师
function Parent() {
// 第一步:厨师准备一个"做菜"的方法
const handleChildAction = (childData) => {
console.log("子组件传来的数据:", childData);
// 这里厨师会根据订单做菜
};
// 第二步:厨师把"接单方法"交给服务员
return ;
// 解读:onAction是服务员,handleChildAction是厨师的做菜方法
}
// 子组件 - 顾客
function Child({onAction}) {
// 顾客想点红烧肉
const 点红烧肉 = () => {
// 第三步:顾客通过服务员告诉厨师要什么菜
onAction("红烧肉");
};
return ;
}
```
**🎯 完整通信流程:**
```
1. 顾客(子组件)点击 "我要红烧肉" 按钮
2. 顾客调用 onAction("红烧肉")
3. onAction(服务员)收到订单
4. 服务员把订单传给 handleChildAction(厨师)
5. 厨师在控制台打印 "子组件传来的数据: 红烧肉"
```
#### 🏗️ 容器组件 vs 展示组件(装修公司类比)
**假设你在装修房子,有两种角色:**
| 角色类型 | 类比 | 负责什么 | 实际例子 |
|---------|------|---------|---------|
| **容器组件** | **装修公司** | 买材料、请工人、管理进度 | 管理数据、处理业务逻辑 |
| **展示组件** | **家具摆设** | 让房间看起来好看、舒适 | 只负责UI展示和用户交互 |
**具体代码实现:**
```jsx
// 容器组件 - 装修公司
// 职责:获取数据、管理状态、处理业务逻辑
class UserContainer extends React.Component {
state = {
users: [], // 所有用户材料
loading: false, // 装修工人是否在工作
error: null // 装修中是否有问题
};
// 装修公司的工作:去找材料(获取数据)
componentDidMount() {
this.fetchUsers();
}
// 装修公司的工作:添加新用户
addUser = (user) => {
this.setState({
users: [...this.state.users, user]
});
};
render() {
// 把材料和工人(数据和函数)交给展示组件去做装修
return (
);
}
}
// 展示组件 - 家具摆设/UI界面
// 职责:只关心怎么摆放好看,不关心材料怎么来的
function UserList({users, loading, onAddUser}) {
// 展示组件只做一件事:让界面看起来漂亮
if (loading) return 工人们正在装修中...
;
return (
用户列表
{/* 根据材料(users)展示界面 */}
{users.map(user =>
{user.name}
)}
);
}
```
#### 🏢 单一职责原则(装修工人分工类比)
**就像你不能让一个工人既搬砖又刷墙又贴瓷砖一样**
```jsx
// ❌ 错误:一个工人做所有装修活
class BadComponent extends React.Component {
// 既管材料采购
fetchData() { /* 买砖、买水泥、买电线 */ }
// 又管砌墙刷漆
buildWalls() { /* 砌墙、刷漆、贴瓷砖 */ }
// 还管安装电路
installWiring() { /* 走线、装开关、装插座 */ }
render() {
return (
{this.fetchData()} // 采购材料
{this.buildWalls()} // 砌墙刷漆
{this.installWiring()} // 安装电路
);
}
}
// ✅ 正确:分工明确,各司其职
class DataManager extends React.Component {
fetchData() { /* 只负责采购材料 */ }
}
class Construction extends React.Component {
buildWalls() { /* 只负责砌墙刷漆 */ }
}
class ElectricWork extends React.Component {
installWiring() { /* 只负责电路安装 */ }
}
// 项目经理(父组件)统一管理
class ProjectManager extends React.Component {
render() {
return (
{/* 派给材料管理员 */}
{/* 派给装修队 */}
{/* 派给电工队 */}
);
}
}
```
#### 🛒 实际项目应用:购物车系统
**完整的项目示例来理解所有概念**
```jsx
// 父组件 - 整个购物系统
class ShoppingApp extends React.Component {
state = {
cart: [], // 购物车商品
total: 0 // 总金额
};
// 添加商品到购物车
addToCart = (product) => {
this.setState({
cart: [...this.state.cart, product],
total: this.state.total + product.price
});
};
// 从购物车移除商品
removeFromCart = (productId) => {
this.setState({
cart: this.state.cart.filter(item => item.id !== productId),
total: this.state.total - this.state.cart.find(item => item.id === productId).price
});
};
render() {
return (
我的购物商城
{/* 商店区域 - 子组件1 */}
{/* 购物车区域 - 子组件2 */}
);
}
}
// 子组件1 - 商品商店(展示组件)
function ProductShop({onAddToCart}) {
const products = [
{id: 1, name: "苹果", price: 5},
{id: 2, name: "香蕉", price: 3}
];
return (
商品列表
{products.map(product => (
{product.name} - ¥{product.price}
))}
);
}
// 子组件2 - 购物车(展示组件)
function ShoppingCart({items, total, onRemove}) {
return (
购物车
{items.map(item => (
{item.name}
))}
总金额: ¥{total}
);
}
```
### 🎯 总结重点
1. **父子关系**:子组件就是写在父组件内部的一部分,就像房间的家具
2. **通信方式**:用回调函数就像找服务员传话,父给子props,子叫父回调
3. **设计模式**:分工明确,容器组件管数据,展示组件管UI
4. **单一职责**:每个组件只做一件事,就像装修工人各司其职
### 9. 常见错误与解决方案
#### 错误1:直接修改props
```jsx
// ❌ 错误
function Child({data}) {
data.value = "新值"; // props是只读的
}
// ✅ 正确:通过回调函数让父组件修改
function Child({data, onUpdate}) {
const handleClick = () => {
onUpdate("新值"); // 通知父组件修改
};
}
```
#### 错误2:直接修改state
```jsx
// ❌ 错误
this.state.count = 5; // 不会触发重新渲染
// ✅ 正确
this.setState({ count: 5 }); // 会触发重新渲染
```
#### 错误3:事件处理函数立即执行
```jsx
// ❌ 错误
// handleClick()会立即执行,而不是点击时执行
// ✅ 正确
// 或者
```
### 10. 实践练习题
#### 基础练习
1. 创建一个简单的计数器,父组件显示数字,子组件提供加减按钮
2. 创建一个消息系统,多个子组件可以向父组件发送消息
#### 进阶练习
1. 创建一个表单组件,子组件负责输入,父组件负责提交
2. 创建一个购物车,商品列表是子组件,总价格显示在父组件
---
## 🎓 学习路线图
### 第一阶段:基础概念
1. 理解React组件概念
2. 学会创建函数组件和类组件
3. 理解JSX语法
### 第二阶段:状态管理
1. 学会使用State
2. 掌握setState的正确用法
3. 理解状态更新机制
### 第三阶段:组件通信
1. 掌握Props传递
2. 学会使用回调函数
3. 理解单向数据流
### 第四阶段:最佳实践
1. 组件设计模式
2. 状态管理策略
3. 性能优化
## ✅ 核心要点总结卡片
| 概念 | 要点 | 记住的一句话 |
|------|------|-------------|
| 组件 | **可复用的UI模块** | 组件就像积木,可以搭建任何界面 |
| Props属性 | **父→子传递数据,只读不可修改** | Props是组件的参数,不能修改 |
| State状态 | **组件内部数据,用setState修改** | State要修改必须用setState |
| 回调函数 | **子→父通信的桥梁** | 回调函数建立了父子沟通的桥梁 |
| 单向数据流 | **数据从父到子单向流动** | 像水管,水只能从上往下流 |
| this关键字 | **指代组件实例本身** | 用this访问组件的state和方法 |
| 箭头函数 | **解决this绑定问题** | 箭头函数让this指向正确 |
## 🔍 核心疑问深度解答
### ❓ 1. 传递函数到子组件:传的是函数结果还是函数本身?
**重要概念:React中传递的是函数本身,不是执行结果!**
```jsx
// ✅ 正确:传递函数本身
// 子组件收到的是可以调用的函数
// ❌ 错误:传递函数执行结果
// 子组件收到的是undefined(因为handleMessage没有返回值)
// ❌ 更错误的写法:
function Parent() {
const result = this.handleMessage(); // 立即执行了函数
return ; // 传了个undefined
}
```
**类比理解:**
- 传函数就像是给朋友**一把能打电话的手机**
- 传函数结果就像是给朋友**一张已经打过电话的记录纸条**
- 朋友需要的是能打电话的手机(onMessage={this.handleMessage})
- 不是打过电话的纸条(onMessage={this.handleMessage()})
### ❓ 2. 父→子传递流程详解
**完整流程分解:**
```jsx
// 第1步:父组件创建函数
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = { message: '' };
}
// 1. 父组件自己创建一个函数
handleMessage = (msg) => {
this.setState({ message: msg });
};
render() {
return (
{/* 2. 把函数当作属性传递 */}
{/* 解读:onMessage是属性名,this.handleMessage是要传的函数 */}
);
}
}
// 第2步:子组件接收并使用
const ChildComponent = (props) => {
const sendMessage = () => {
// 3. 子组件通过props得到函数并可以调用
props.onMessage('Hello from Child!');
// 这行代码相当于调用:this.handleMessage('Hello from Child!')
};
return ;
};
```
**流程图:**
```
┌─────────────────────────────────────────────────┐
│ 父组件 │
│ ┌─────────────────────────────────┐ │
│ │ 创建函数: handleMessage │ │
│ │ { this.setState({...}) } │ │
│ └─────────────┬───────────────────┘ │
│ │ 作为props传递 │
│ ┌─────────────▼───────────────────┐ │
│ │ │ │
│ └─────────────────────────────────┘ │
└────────────────┬───────────────────────────────┘
│
函数传递 │
│
┌────────────────▼───────────────────────────────┐
│ 子组件 │
│ ┌─────────────────────────────────┐ │
│ │ 接收函数: props.onMessage │ │
│ │ │ │
│ │ 在需要时调用它 │ │
│ │ props.onMessage('Hello!') │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
```
### ❓ 3. this关键字详解
**在类组件中,this指代什么?**
```jsx
class MyComponent extends React.Component {
constructor(props) {
super(props);
// this指向当前组件的实例
this.state = { count: 0 }; // this.state是这个实例的属性
}
// this.handleClick是这个实例的方法
handleClick = () => {
// 这里的this.state指向实例的state属性
this.setState({ count: this.state.count + 1 });
};
render() {
// 这里的this.handleClick指向实例的handleClick方法
return ;
}
}
```
**为什么必须用this?**
```jsx
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { name: '张三' };
}
// ❌ 错误写法:没有this
printName() {
console.log(state.name); // ReferenceError: state is not defined
}
// ✅ 正确写法:有this
printName = () => {
console.log(this.state.name); // 正常工作:输出'张三'
};
// ❌ 错误写法:没有this
handleClick() {
setState({ name: '李四' }); // ReferenceError: setState is not defined
}
// ✅ 正确写法:有this
handleClick = () => {
this.setState({ name: '李四' }); // 正常工作
};
}
```
**类比理解:**
```jsx
// 想象你是一个公司
class Company {
constructor() {
// this.companyData是这个公司的财产
this.companyData = { employees: 100, revenue: 1000000 };
}
// this.hireEmployee是这个公司的能力
hireEmployee = (person) => {
this.companyData.employees += 1;
};
// 没有this就相当于:
// hireEmployee(person) {
// companyData.employees += 1; // 不知道companyData是哪个公司的
// }
// 有this就相当于:
// hireEmployee = (person) => {
// this.companyData.employees += 1; // 明确知道是这个(this)公司的数据
// };
}
```
### ❓ 4. this绑定问题详细演示
**问题演示:普通方法的this绑定问题**
```jsx
class ProblemExample extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: 'ProblemExample组件'
};
}
// ❌ 有问题的写法:普通方法
badHandleClick() {
console.log('badHandleClick中的this:', this);
console.log('this.state:', this.state); // undefined!
console.log('this.setState:', this.setState); // undefined!
// 这行代码会报错:
// TypeError: Cannot read property 'setState' of undefined
this.setState({ count: this.state.count + 1 });
}
render() {
return (
问题演示:普通方法this绑定问题
当前计数: {this.state.count}
{/* 问题出现:作为回调传递时this丢失了 */}
);
}
}
```
**执行流程分析:**
```jsx
// 当用户点击按钮时:
// 1. React会这样调用你的函数:
badHandleClick(); // 注意:这里没有this!
// 2. 相当于:
function badHandleClick() {
// 这里的this是undefined(严格模式下)
// 或者指向全局对象(非严格模式下)
// 所以你访问不到真正组件的state和setState
this.setState(...); // this是undefined --> 报错!
}
```
**解决方案对比:**
```jsx
class SolutionExample extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
// ✅ 解决方案1:箭头函数(推荐)
goodHandleClick1 = () => {
// 箭头函数会捕获定义时的this
// 所以这里的this一定指向组件实例
this.setState({ count: this.state.count + 1 });
};
// ✅ 解决方案2:在构造函数中bind
constructor(props) {
super(props);
this.state = { count: 0 };
// 手动绑定this
this.goodHandleClick2 = this.goodHandleClick2.bind(this);
}
goodHandleClick2() {
// 由于构造函数中绑定了this,这里能正常工作
this.setState({ count: this.state.count + 1 });
}
// ✅ 解决方案3:箭头函数内联
render() {
return (
);
}
}
```
**各种方案原理分析:**
```jsx
// 方案1:箭头函数为何能工作?
class Example1 {
handleClick = () => {
// 箭头函数创建时就捕获了this
// 这里的this在定义时就绑定到类的实例上
this.setState({ count: this.state.count + 1 });
};
}
// 方案2:bind为何能工作?
class Example2 {
constructor() {
// bind创建一个新的函数,强制this指向传入的对象
// 这里强制this指向当前类的实例
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 由于前面bind了,这里的this是绑定的那个this
this.setState({ count: this.state.count + 1 });
}
}
// 方案3:内联箭头函数为何能工作?
class Example3 {
render() {
return (
);
}
}
```
**方案对比和推荐:**
| 方案 | 优点 | 缺点 | 推荐度 |
|------|------|------|--------|
| **方案1:箭头函数** | this指向正确,语法简洁 | 每次创建新实例 | ⭐⭐⭐⭐⭐ 强烈推荐 |
| **方案2:构造函数bind** | this指向正确,性能好 | 代码较复杂,需要写bind | ⭐⭐⭐ 可以使用 |
| **方案3:内联箭头函数** | this指向正确,灵活 | 每次渲染都创建新函数,性能稍差 | ⭐⭐ 简单场景可用 |
### 🎓 this绑定核心概念总结
**1. 什么是this绑定?**
```jsx
// 想象你有一个机器人助手
const robot = {
name: '小R',
work: function() {
console.log(this.name + '在工作'); // this指向robot对象
}
};
// 正常调用:
robot.work(); // 输出:小R在工作
// 但如果把方法单独拿出来:
const separateWork = robot.work;
separateWork(); // 输出:undefined在工作 (this变成全局对象了!)
// 这就是this绑定问题:方法脱离了原来的对象后,this指向就变了
```
**2. React中this绑定的特殊性**
```jsx
class MyComponent extends React.Component {
handleClick() {
// 这里我们希望this指向组件实例
this.setState({ count: 1 }); // 需要访问组件的setState方法
}
render() {
// 但当我们这样传递时:
return ;
// handleClick失去了和组件的连接,this不再指向组件
}
}
```
**3. 为什么箭头函数能解决问题?**
```jsx
// 箭头函数的特点是:继承定义时的this环境
class GoodComponent extends React.Component {
// 这里的箭头函数在创建时,this就指向组件实例
handleClick = () => {
// 无论在哪里调用,this都保持创建时的指向
this.setState({ count: 1 }); // this永远指向组件实例
};
render() {
return ;
// 即使方法被传递出去,this指向也不变
}
}
```
### 📚 补充学习:几种函数的this指向
```jsx
// 1. 普通函数:this由调用者决定
function normalFunc() {
console.log('普通函数的this:', this);
}
const obj = {
name: '小李',
normalFunc: normalFunc
};
normalFunc(); // this是全局对象/undefined
obj.normalFunc(); // this是obj对象
// 2. 箭头函数:this由定义位置决定
const arrowFunc = () => {
console.log('箭头函数的this:', this);
};
const obj2 = {
name: '小王',
arrowFunc: arrowFunc
};
arrowFunc(); // this和定义时一样
obj2.arrowFunc(); // this还是和定义时一样,不会变成obj2
// 3. bind创建的函数:this由bind的第一个参数决定
const boundFunc = normalFunc.bind(obj);
boundFunc(); // this一定是obj,不管在哪里调用
```
### 🔄 this绑定问题在组件通信中的影响
```jsx
// 有问题的组件通信
class BadParent extends React.Component {
state = { count: 0 };
handleIncrement() {
// 这里是普通方法,作为回调传递时this会丢失
this.setState({ count: this.state.count + 1 });
}
render() {
return (
计数: {this.state.count}
{/* 传递给子组件后this指向就丢失了 */}
);
}
}
function IncrementButton({ onIncrement }) {
// 当点击时会调用 onIncrement()
// 但这时this指向undefined,会报错
return ;
}
// 正确的组件通信
class GoodParent extends React.Component {
state = { count: 0 };
handleIncrement = () => {
// 箭头函数,this指向始终正确
this.setState({ count: this.state.count + 1 });
};
render() {
return (
计数: {this.state.count}
{/* 传递给子组件后this指向依然正确 */}
);
}
}
```
### 💡 实践建议
1. **首选箭头函数写法**:在类组件中用`handleEvent = () => {}`
2. **理解this的本质**:this是在函数运行时才确定的
3. **记住箭头函数特点**:this在定义时就确定,不会改变
4. **遇到问题时debug**:
```jsx
handleClick = (event) => {
console.log('当前的this:', this); // 看看this指向什么
console.log('this.setState是否存在:', typeof this.setState); //检查方法是否存在
};
```
## 🔧 JavaScript this绑定方法对比详解
### 📚 call/apply/bind 三兄弟对比
在实际开发中,我们经常需要手动控制函数的this指向,JavaScript提供了三种方法:call、apply和bind。
#### 🎯 基本语法对比
```jsx
// call方法语法
function.call(thisArg, arg1, arg2, arg3, ...);
// apply方法语法
function.apply(thisArg, [arg1, arg2, arg3, ...]);
// bind方法语法
var newFunction = function.bind(thisArg, arg1, arg2, ...);
newFunction();
```
#### 📊 三兄弟特性对比表
| 方法 | 是否立即执行 | 参数传递方式 | 返回值 | 能否预设参数 | 使用场景 |
|------|-------------|-------------|--------|------------|---------|
| **call** | ✅ 立即执行 | 逗号分隔 | 原函数返回值 | ❌ 不能预设 | 立即调用,参数已知 |
| **apply** | ✅ 立即执行 | 数组形式 | 原函数返回值 | ❌ 不能预设 | 参数是数组或类数组 |
| **bind** | ❌ 不立即执行 | 逗号分隔 | 新函数 | ✅ 可以预设 | 预设参数,延迟执行 |
#### 🎬 具体示例演示
```javascript
var person = {
name: '小明',
age: 25,
introduce: function(city, hobby) {
console.log(`${this.name},${this.age}岁,来自${city},爱好${hobby}`);
return `返回值:我是${this.name}`;
}
};
var otherPerson = { name: '小红', age: 30 };
// 🌰 1. call示例
var result1 = person.introduce.call(otherPerson, '北京', '游泳');
console.log(result1);
// 输出:小红,30岁,来自北京,爱好游泳
// 返回值:我是小红
// 🌰 2. apply示例
var params = ['上海', '读书'];
var result2 = person.introduce.apply(otherPerson, params);
console.log(result2);
// 输出:小红,30岁,来自上海,爱好读书
// 返回值:我是小红
// 🌰 3. bind示例
var boundFunc = person.introduce.bind(otherPerson, '广州');
var result3 = boundFunc('篮球'); // 预设了第一个参数,这里传第二个参数
console.log(result3);
// 输出:小红,30岁,来自广州,爱好篮球
// 返回值:我是小红
```
#### 🔍 bind方法的深入解析
**bind的核心特点:参数预设和永久绑定**
```javascript
var zs = { names: 'zhangsan' };
function f(age) {
console.log(this.names);
console.log(age);
}
var fBindZsWithAge = f.bind(zs, 28);
// 关键点:bind预设的参数永远不会被覆盖!
fBindZsWithAge(); // 输出:zhangsan 28
fBindZsWithAge(35); // 输出:zhangsan 28 (35被忽略!)
fBindZsWithAge(40); // 输出:zhangsan 28 (40被忽略!)
// 错误的认知:认为后续参数会覆盖预设
// 正确的认知:bind预设是永久性的,后续参数只能补充,不能覆盖!
```
## ⁉️ this丢失问题深度解析(基于你的疑问补充)
### 🏪 React事件处理中this丢失的完整过程
根据你在文档学习中产生的疑惑,我详细补充this丢失的机制:
```jsx
class ProblemExample extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 }; // 组件内部的state
}
// ❌ 有问题的写法:普通方法
badHandleClick() {
console.log('badHandleClick中的this:', this);
console.log('this.state:', this.state); // undefined!
console.log('this.setState:', this.setState); // undefined!
// 这行代码会报错:
// TypeError: Cannot read property 'setState' of undefined
this.setState({ count: this.state.count + 1 });
}
render() {
return (
{/* 问题出现:作为回调传递时this丢失了 */}
);
}
}
```
### 🔍 this丢失的具体过程分析
**你问的关键问题:**
```jsx
{/* 问题出现:作为回调传递时this丢失了 */}