|
|
### 6.5.1 问题:为什么是 `() => ({})` 而不是 `() => {}`?
**疑问代码**:
```typescript
state: () => ({ // ← 为什么多了一层括号?
isLogin: false
})
// 为什么不是这样?
state: () => { // ← 这种写法是错误的
isLogin: false
}
```
---
### 6.5.2 答案:这是箭头函数返回对象的语法
**核心语法**:箭头函数返回对象时,必须用圆括号包裹对象
```typescript
// ✅ 正确:用圆括号包裹对象
const fn1 = () => ({ name: 'Alice' });
// ❌ 错误:没有圆括号,会被识别为代码块
const fn2 = () => { name: 'Alice' };
// ✅ 正确:显式 return
const fn3 = () => {
return { name: 'Alice' };
};
```
---
### 6.5.3 语法解析
#### 语法 1:`() => ({})` 箭头函数直接返回对象
```typescript
state: () => ({
isLogin: false
})
```
**解析**:
```
state: () => ({ isLogin: false })
│ │ └─────────────────┘
│ │ │
│ │ 返回的对象字面量
│ │
│ └─ 箭头(=>)
│
└─ 参数列表(空)
```
**等价写法**:
```typescript
// 写法 1:箭头函数直接返回对象(简洁)
state: () => ({
isLogin: false
})
// 写法 2:箭头函数 + return(完整)
state: () => {
return {
isLogin: false
};
}
// 写法 3:传统函数
state: function() {
return {
isLogin: false
};
}
```
---
#### 语法 2:`() => {}` 箭头函数的代码块
```typescript
// ❌ 错误示例
state: () => {
isLogin: false // ← 这会被解析为标签(label),不是对象!
}
```
**为什么错误**:
- 在 JavaScript 中,`{ }` 可以表示**代码块**或**对象字面量**
- 箭头函数的 `=>` 后面紧跟 `{ }` 时,会被解析为**代码块**,而不是对象
- `{ isLogin: false }` 在代码块中会被解析为:
- `isLogin:` 是一个**标签(label)**
- `false` 是一个**表达式语句**
**完整解释**:
```typescript
// 这段代码:
() => {
isLogin: false
}
// 会被解析为:
function() {
isLogin: false // 标签(label),类似于 goto 的标签
// 等价于:
// label: false;
}
```
**标签(Label)示例**:
```typescript
// JavaScript 的标签语法
outerLoop:
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
if (j === 3) break outerLoop; // 跳出外层循环
}
}
```
---
### 6.5.4 JavaScript 语法规则详解
#### 规则 1:`{ }` 的歧义性
```typescript
// 情况 1:对象字面量
const obj = { name: 'Alice' }; // ✅ 明确是对象
// 情况 2:代码块
{
console.log('Hello'); // ✅ 明确是代码块
}
// 情况 3:箭头函数后的歧义
const fn = () => {
console.log('Hello'); // ✅ 这是代码块
};
const fn2 = () => ({
name: 'Alice' // ✅ 这里的括号强制解析为对象
});
```
---
#### 规则 2:箭头函数的语法优先级
```typescript
// 语法 A:单行表达式(不需要 return 关键字)
() => expression // 自动返回表达式的值
// 语法 B:代码块(需要 return 才能返回值)
() => {
// 代码块
return value; // 必须显式 return
}
// 关键:对象字面量属于表达式,但 { } 看起来像代码块
() => { name: 'Alice' } // ❌ 被解析为代码块
() => ({ name: 'Alice' }) // ✅ 用括号强制解析为表达式
```
---
### 6.5.5 完整对比示例
#### 示例 1:箭头函数的各种写法
```typescript
// ✅ 写法 1:直接返回对象(推荐)
const fn1 = () => ({ name: 'Alice', age: 30 });
// ✅ 写法 2:使用 return
const fn2 = () => {
return { name: 'Alice', age: 30 };
};
// ✅ 写法 3:传统函数
function fn3() {
return { name: 'Alice', age: 30 };
}
// ❌ 写法 4:错误(没有圆括号)
const fn4 = () => { name: 'Alice', age: 30 };
// 等价于:
const fn4 = () => {
name: 'Alice', // 标签
age: 30 // 表达式语句
};
// 返回 undefined(没有 return)
```
---
#### 示例 2:Pinia state 的正确写法
```typescript
// ✅ 正确:箭头函数直接返回对象
export const useUserStore = defineStore('user', {
state: () => ({
isLogin: false,
userInfo: null
})
});
// ✅ 正确:箭头函数 + return
export const useUserStore = defineStore('user', {
state: () => {
return {
isLogin: false,
userInfo: null
};
}
});
// ✅ 正确:传统函数
export const useUserStore = defineStore('user', {
state: function() {
return {
isLogin: false,
userInfo: null
};
}
});
// ❌ 错误:没有圆括号
export const useUserStore = defineStore('user', {
state: () => {
isLogin: false, // 标签
userInfo: null // 表达式语句
// 没有 return,返回 undefined
}
});
```
---
### 6.5.6 为什么需要这个语法?
#### 原因 1:箭头函数的设计
**箭头函数的语法糖**:
```typescript
// 设计目标:让简单的函数更简洁
// 传统写法
function add(a, b) {
return a + b;
}
// 箭头函数(单行表达式)
const add = (a, b) => a + b;
// 箭头函数(多行代码块)
const add = (a, b) => {
const result = a + b;
return result;
};
```
**问题**:如何区分"单行表达式"和"代码块"?
```typescript
// 表达式
() => 42 // 返回数字 42
() => 'hello' // 返回字符串 'hello'
// 代码块
() => { // 代码块开始
console.log('hello');
return 42;
}
// 对象字面量的歧义
() => { x: 1 } // 这是代码块还是对象?
// JavaScript 规则:优先解析为代码块
() => ({ x: 1 }) // 用括号强制解析为表达式
```
---
#### 原因 2:对象字面量的特殊性
```typescript
// 对象字面量在赋值时的语法
const obj = { x: 1 }; // ✅ 明确是对象
// 但在箭头函数后会有歧义
const fn = () => { x: 1 };
// ^^^^^^^^
// 这是代码块还是对象?
//
// JavaScript 解析器:
// 1. 看到 `=> {`,认为 `{` 是代码块开始
// 2. 看到 `x:`,认为是标签(label)
// 3. 看到 `1`,认为是表达式语句
// 4. 代码块结束,没有 return,返回 undefined
// 解决方案:用括号包裹
const fn = () => ({ x: 1 });
// ^^^^^^^
// 括号内只能是表达式,所以解析为对象
```
---
### 6.5.7 语法速查表
| 写法 | 含义 | 返回值 |
|------|------|--------|
| `() => ({ x: 1 })` | 箭头函数返回对象 | `{ x: 1 }` ✅ |
| `() => { x: 1 }` | 箭头函数的代码块 | `undefined` ❌ |
| `() => { return { x: 1 }; }` | 箭头函数代码块 + return | `{ x: 1 }` ✅ |
| `() => x` | 箭头函数返回变量值 | `x` 的值 |
| `() => { x }` | 箭头函数代码块 | `undefined` ❌ |
---
### 6.5.8 实际应用示例
#### 示例 1:Pinia Store 的 state
```typescript
export const useUserStore = defineStore('user', {
// ✅ 箭头函数直接返回对象(最简洁)
state: () => ({
isLogin: false,
userInfo: null,
permissions: []
}),
// ✅ 等价写法(如果需要逻辑)
state: () => {
const initialState = {
isLogin: false,
userInfo: null,
permissions: []
};
return initialState;
},
});
```
---
#### 示例 2:Vue 组件的 data
```typescript
// Vue 3 Composition API
import { ref } from 'vue';
const count = ref(0); // 简单值用 ref
// Vue 3 Options API
export default {
data() {
return { // ✅ 返回对象
count: 0,
message: 'hello'
};
}
};
// ❌ 错误:直接返回对象字面量
export default {
data: () => {
count: 0, // 标签
message: 'hello' // 表达式语句
// 没有 return
}
};
```
---
#### 示例 3:React 组件的 state
```typescript
// React 函数组件
function Counter() {
const [count, setCount] = useState(0); // Hook
// ...
}
// React 类组件
class Counter extends React.Component {
state = { // ✅ 类属性,直接赋值
count: 0
};
}
// ❌ 不相关的语法(仅作对比)
const fn = () => {
count: 0 // 标签,不是对象
};
```
---
### 6.5.9 调试技巧
#### 如何检测错误的写法?
```typescript
// ❌ 错误写法
const state = () => {
isLogin: false
};
// 测试
console.log(state()); // undefined(而不是 { isLogin: false })
// ✅ 正确写法
const state = () => ({
isLogin: false
});
console.log(state()); // { isLogin: false }
```
---
#### TypeScript 的类型检查
```typescript
// TypeScript 能够检测到错误
const fn1 = () => ({ x: 1 }); // ✅ 类型: () => { x: number }
const fn2 = () => { x: 1 }; // ❌ 可能报错:x 没有定义(作为变量)
// 显式类型注解
const fn3: () => { x: number } = () => ({ x: 1 }); // ✅
const fn4: () => { x: number } = () => { x: 1 }; // ❌ 类型错误
```
---
### 6.5.10 关键要点总结
1. **`()` 表示参数列表**
- `()` 表示空参数列表
- 如果有参数:`(arg1, arg2) => ({ ... })`
2. **`=>` 表示箭头函数**
- 分隔参数列表和函数体
3. **`({})` 表示返回对象**
- **外层括号**:强制解析为表达式
- **内层花括号**:对象字面量
- **合起来**:`({ x: 1 })` 是包裹在括号中的对象字面量
4. **如果没有外层括号**
- `{ x: 1 }` 会被解析为代码块
- `x:` 会被解析为标签
- 函数返回 `undefined`
5. **记忆口诀**
```
箭头函数返回对象,外层括号不能少
() => ({ object }) ✅ 正确
() => { object } ❌ 错误
```
---
### 6.5.11 完整语法图解
```
state: () => ({
isLogin: false
})
│ │ │ └────────────────┘
│ │ │ │
│ │ │ 对象字面量
│ │ │ { isLogin: false }
│ │ │
│ │ └─ 圆括号(强制解析为表达式)
│ │
│ └─ 箭头(=>)
│
└─ 参数列表(空)
```
**等价展开**:
```typescript
state: function() {
return {
isLogin: false
};
}
```
---
### 6.5.12 常见错误与解决方案
| 错误代码 | 问题 | 解决方案 |
|---------|------|---------|
| `() => { x: 1 }` | 被解析为代码块 | 改为 `() => ({ x: 1 })` |
| `() => { return { x: 1 } }` | 多余的代码块 | 改为 `() => ({ x: 1 })` |
| `() => { x: 1, y: 2 }` | 多个标签 | 改为 `() => ({ x: 1, y: 2 })` |
---
### 6.5.13 最佳实践建议
```typescript
// ✅ 推荐 1:简单对象直接返回
state: () => ({
isLogin: false,
userInfo: null
})
// ✅ 推荐 2:需要逻辑时使用完整写法
state: () => {
const defaultState = {
isLogin: false,
userInfo: null
};
if (process.env.NODE_ENV === 'development') {
defaultState.debugMode = true;
}
return defaultState;
}
// ❌ 避免:不必要的代码块
state: () => {
return {
isLogin: false
};
}
```
---
**总结**:`state: () => ({})` 是箭头函数返回对象的简洁语法,外层括号是必需的,用于告诉 JavaScript 这是一个对象字面量,而不是代码块。
---
|
|
|
|
|
|
|
|
```typescript
numbers.reduce(
(total, num) => total + num,
0
)
```
`reduce` 在这里的作用是**将数组中的所有元素累加起来**。
## 逐步解析:
### 1. **基本语法**
`reduce` 方法接收两个参数:
- **回调函数**:对每个元素执行的函数
- **初始值**:累加的起始值(这里是 `0`)
### 2. **回调函数的参数**
```typescript
(total, num) => total + num
```
- total:累加器,保存当前的累加结果
- num:当前正在处理的数组元素
### 3. **执行过程**
假设调用 `sum(1, 2, 3, 4)`:
- numbers 数组 = `[1, 2, 3, 4]`
- 初始值 total = `0`
执行步骤:
1. 第一次:`total = 0 + 1 = 1`
2. 第二次:`total = 1 + 2 = 3`
3. 第三次:`total = 3 + 3 = 6`
4. 第四次:`total = 6 + 4 = 10`
最终返回 `10`
## 实际应用示例:
```typescript
// 各种调用方式
console.log(sum(1, 2, 3, 4)); // 输出: 10
console.log(sum(5, 10, 15)); // 输出: 30
console.log(sum()); // 输出: 0(空数组,只返回初始值)
// 也可以用数组调用
const numbers = [1, 2, 3, 4];
console.log(sum(...numbers)); // 输出: 10(展开运算符)
```
## 如果没有 reduce,等效的实现:
```typescript
function sum(...numbers: number[]): number {
let total = 0;
for (let i = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
```
或者用 `for...of` 循环:
```typescript
function sum(...numbers: number[]): number {
let total = 0;
for (const num of numbers) {
total += num;
}
return total;
}
```
`reduce` 的优势在于代码更简洁、函数式,而且可以链式调用其他数组方法。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
分析一下面代码中单个问号?与双问号??的语法含义
```
catch (error: any) {
// if (type === 'run' || type === 'refreRun') {
console.log('SQL执行失败:', error);
const msg = error?.data?.msg || '';}
if (savedState) {
this.model = savedState.model ?? 0;
this.data = savedState.data ?? {};
this.screen = savedState.screen ?? {};
this.analyze = savedState.analyze ?? {};
}
```
## 代码中 `?` 与 `??` 的语法含义分析
### 1. 可选链操作符 (`?.`)
**语法含义:**
- 可选链操作符允许安全地访问深层嵌套的对象属性,而不必显式验证每个中间引用是否有效。
- 如果引用为 `null` 或 `undefined`,表达式会短路返回 `undefined`。
**代码示例:**
```typescript
const msg = error?.data?.msg || '';
```
**等价写法:**
```typescript
// 传统写法
const msg = (error !== null && error !== undefined && error.data !== null && error.data !== undefined)
? error.data.msg
: undefined;
const finalMsg = msg || '';
// 或者更简洁的传统写法
const msg = error && error.data && error.data.msg || '';
```
**执行逻辑:**
1. 检查 `error` 是否为 `null` 或 `undefined`
2. 如果是,返回 `undefined`,不再继续访问 `.data`
3. 如果不是,继续访问 `error.data`
4. 检查 `error.data` 是否为 `null` 或 `undefined`
5. 如果是,返回 `undefined`
6. 如果不是,返回 `error.data.msg` 的值
7. 最后通过 `|| ''` 将 `undefined` 或假值转换为空字符串
---
### 2. 空值合并操作符 (`??`)
**语法含义:**
- 空值合并操作符是一个逻辑操作符,当左侧操作数为 `null` 或 `undefined` 时,
- 返回右侧操作数;
- 否则返回左侧操作数。
**代码示例:**
```typescript
this.model = savedState.model ?? 0;
this.data = savedState.data ?? {};
this.screen = savedState.screen ?? {};
this.analyze = savedState.analyze ?? {};
```
**等价写法:**
```typescript
// 传统写法1:使用三元运算符
this.model = savedState.model !== null && savedState.model !== undefined
? savedState.model
: 0;
// 传统写法2:使用逻辑或(注意:这种方式有陷阱)
this.model = savedState.model || 0; // 当 model = 0 或 false 时会错误地使用默认值
```
**执行逻辑:**
- 如果 `savedState.model` 是 `null` 或 `undefined`,使用默认值 `0`
- 如果 `savedState.model` 是其他任何值(包括 `0`、`false`、`''`、`NaN`),都使用该值本身
---
### 3. 两者的关键区别
| 操作符 | 名称 | 触发默认值的条件 | 典型使用场景 |
|--------|------|-----------------|--------------|
| `??` | 空值合并 | 仅当值为 `null` 或 `undefined` | 需要保留 `0`、`false`、`''` 等有效值 |
| `||` | 逻辑或 | 值为假值(`null`、`undefined`、`0`、`false`、`''`、`NaN`) | 需要过滤所有假值,用默认值替换 |
| `?.` | 可选链 | 访问不存在属性时不报错 | 安全访问深层嵌套对象 |
---
### 4. 组合使用示例
```typescript
// 可选链 + 空值合并的组合使用
const value = obj?.nested?.property ?? 'default';
// 等价于:
const value = obj !== null && obj !== undefined
&& obj.nested !== null && obj.nested !== undefined
? (obj.nested.property ?? 'default')
: 'default';
```
---
### 5. 注意事项
1. **不能与 `&&` 或 `||` 混用而不加括号:**
```typescript
// ❌ 错误 - 语法错误
const result = null || undefined ?? 'default';
// ✅ 正确
const result = (null || undefined) ?? 'default';
```
2. **`??` 会保留假值:**
```typescript
const count = 0 ?? 100; // 结果为 0
const count = 0 || 100; // 结果为 100
```
3. **可选链支持函数调用:**
```typescript
const result = obj?.method?.(); // 仅当 obj.method 存在且为函数时才调用
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|