跳到主要内容

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# LINQJavaScript需要留意
Wherefilter返回新数组,立即遍历
Selectmap返回新数组
FirstOrDefaultfind未找到返回 undefined
Anysome返回布尔值
Allevery空数组也返回 true
Aggregatereduce一般提供初始值

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 = [] 重新赋值才会报错。

参考:MDN JavaScript 指南。