2.2 用 C# 思维理解 JavaScript
本章目标: 能看懂 Vue 示例中的 JavaScript 表达式,并用数组处理任务数据。本章 JavaScript 示例请在浏览器 Console 中逐段运行。
学习项目使用严格的 <script setup lang="ts">。本章刻意演示动态类型、混合类型比较等 JS 行为,不能原样粘入 TypeScript 脚本;有些例子会被类型检查拒绝。到 TypeScript 章节再学习如何为这些数据和函数添加类型。
变量与类型
const title = '学习 Vue';
let count = 0;
count += 1;
const task = { id: 1, title, isDone: false };
task.isDone = true;
const 禁止重新赋值这个绑定,但不冻结对象。let 允许重新赋值,具有块作用域。现代项目优先 const,需要重新赋值时用 let。
| C# 经验 | JavaScript 实际规则 |
|---|---|
| 变量有固定静态类型 | JS 值有类型,变量可绑定不同类型的值 |
int、double、decimal | 常规数值主要用 number,遵循浮点数规则 |
null | 另有 undefined,常表示缺失或未赋值 |
| 引用类型对象 | 对象和数组也通过引用共享 |
const 常量 | JS const 是不可重新赋值的绑定 |
number 不能安全表示所有 C# long,也不能精确处理所有金额。API 契约应约定大整数用字符串,金额使用明确的最小单位或十进制字符串。
判断相等与空值
console.log(1 === '1'); // false
console.log(1 == '1'); // true,会做类型转换
const user = { name: '', address: null };
console.log(user.address?.city ?? '未填写');
console.log(user.name ?? '匿名'); // 空字符串仍被保留
console.log(user.name || '匿名'); // 空字符串被替换
优先使用 === / !==。?? 只处理 null 和 undefined;|| 还把 0、false、空字符串等假值当作需要替换的值。
函数与箭头函数
function formatTitle(title) {
return title.trim();
}
const isPending = task => !task.isDone;
const createTask = (id, title) => ({ id, title, isDone: false });
箭头函数可类比 C# Lambda,但它还捕获外层的 this,不能把两种函数形式视作完全一样。返回对象字面量时要用括号包住,否则花括号会被理解成函数体。
本手册用 Composition API,通常直接用闭包访问状态,很少需要 this。
对象解构与展开
const task = { id: 1, title: '学习 Vue', isDone: false };
const { id, title } = task;
const completed = { ...task, isDone: true };
const tasks = [task, completed];
const copy = [...tasks];
解构提取属性;展开创建新对象或数组。展开是浅拷贝:嵌套对象仍可能共享引用。
const original = { profile: { name: '小林' } };
const copy = { ...original };
copy.profile.name = '小陈';
console.log(original.profile.name); // 小陈
不要用 JSON 序列化当作通用深拷贝。它会改变或丢失某些类型的信息。
数组方法与 LINQ 对照
const tasks = [
{ id: 1, title: '学习 JS', isDone: true },
{ id: 2, title: '学习 Vue', isDone: false },
];
const pending = tasks.filter(t => !t.isDone);
const titles = tasks.map(t => t.title);
const first = tasks.find(t => t.id === 2);
const hasPending = tasks.some(t => !t.isDone);
const doneCount = tasks.filter(t => t.isDone).length;
| C# LINQ | JavaScript | 需要留意 |
|---|---|---|
Where | filter | 返回新数组,立即遍历 |
Select | map | 返回新数组 |
FirstOrDefault | find | 未找到返回 undefined |
Any | some | 返回布尔值 |
All | every | 空数组也返回 true |
Aggregate | reduce | 一般提供初始值 |
push、splice、sort 会修改原数组。filter、map 不修改数组本身,但回调仍可能修改其中的对象。排序数字需要比较器:numbers.sort((a, b) => a - b)。
字符串与异常
const title = 'Vue 3';
console.log(`正在学习 ${title}`);
try {
JSON.parse('这不是 JSON');
} catch (error) {
console.error(error);
}
反引号字符串支持插值和换行,类似 C# 的字符串插值。JS 可以抛出任意值,后续 TypeScript 示例会将捕获到的异常按 unknown 处理。
练习
从任务数组中找出未完成任务,生成它们的标题数组。再解释为什么 const tasks = [] 后仍能执行 tasks.push(...)。
参考答案
const titles = tasks.filter(t => !t.isDone).map(t => t.title);
const 限制的是变量绑定,数组内部仍然可修改;tasks = [] 重新赋值才会报错。