本文是TypeScript系列第三篇,将系统讲解ES6+的现代JavaScript语法特性。这些特性是学习TypeScript的重要基础,掌握它们能让您的TypeScript学习之路更加顺畅。
一、为什么需要学习现代JavaScript语法?
TypeScript与现代JavaScript的关系
TypeScript不是要取代JavaScript,而是增强JavaScript。它建立在现代JavaScript语法之上:
javascript
// ES5传统写法(繁琐)
var numbers = [1, 2, 3, 4, 5];
var results = [];
for (var i = 0; i < numbers.length; i++) {
results.push(numbers[i] * 2);
}
// ES6+现代写法(简洁)
const numbers = [1, 2, 3, 4, 5];
const results = numbers.map(num => num * 2);
// TypeScript在此基础上添加类型
const numbers: number[] = [1, 2, 3, 4, 5];
const results: number[] = numbers.map((num: number) => num * 2);
学习现代语法的必要性
-
提高开发效率:更少的代码,更多的功能
-
改善代码可读性:语法更接近自然语言
-
TypeScript项目标配:现代前端项目都使用这些特性
-
面试必备技能:企业招聘的重要考察点
二、变量声明:let与const
从var到let/const的演进
var的问题:
javascript
// 1. 变量提升(hoisting)
console.log(x); // undefined,不会报错
var x = 5;
// 2. 没有块级作用域
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出3次3,而不是0,1,2
}
let/const的优势:
javascript
// 1. 块级作用域
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 正确输出0,1,2
}
// 2. 暂时性死区
console.log(y); // 报错:Cannot access 'y' before initialization
let y = 5;
使用规范
javascript
// 推荐做法
const PI = 3.14159; // 常量使用const
let count = 0; // 会改变的变量使用let
const user = { name: "Alice" }; // 对象引用使用const
user.name = "Bob"; // 可以修改对象属性
// 避免的做法
var oldVariable = "avoid this"; // 不要使用var
let shouldBeConstant = "will change"; // 应该用const却用了let
三、箭头函数:更简洁的函数写法
基本语法
javascript
// 传统函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => {
return a + b;
};
// 更简洁的箭头函数(只有一条返回值时)
const add = (a, b) => a + b;
// 单个参数可以省略括号
const square = x => x * x;
// 无参数时需要空括号
const greet = () => "Hello!";
this绑定的区别
传统函数的this问题:
javascript
const obj = {
name: "Alice",
traditionalFunction: function() {
console.log("Traditional:", this.name); // this指向obj
},
problemFunction: function() {
setTimeout(function() {
console.log("Problem:", this.name); // this指向window/undefined
}, 100);
}
};
obj.traditionalFunction(); // "Traditional: Alice"
obj.problemFunction(); // "Problem: " (空字符串)
箭头函数的this解决方案:
javascript
const obj = {
name: "Alice",
arrowFunction: function() {
setTimeout(() => {
console.log("Arrow:", this.name); // this继承自外层作用域
}, 100);
}
};
obj.arrowFunction(); // "Arrow: Alice"
使用场景
javascript
// 1. 数组方法回调
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
// 2. 事件处理函数
button.addEventListener('click', () => {
console.log('Button clicked!');
});
// 3. 简单的返回值函数
const getUserName = user => user.name;
// 注意:不适合用于方法定义或构造函数
四、模板字符串:强大的字符串处理
基本用法
javascript
const name = "Alice";
const age = 25;
// 传统字符串拼接
const message1 = "大家好,我是" + name + ",今年" + age + "岁。";
// 模板字符串
const message2 = `大家好,我是${name},今年${age}岁。`;
console.log(message1); // 大家好,我是Alice,今年25岁。
console.log(message2); // 大家好,我是Alice,今年25岁。
高级特性
javascript
// 1. 多行字符串
const address = `
北京市朝阳区
某某街道123号
邮编:100000
`;
// 2. 表达式计算
const a = 5, b = 10;
console.log(`5 + 10 = ${a + b}`); // 5 + 10 = 15
// 3. 函数调用
function formatCurrency(amount) {
return `¥${amount.toFixed(2)}`;
}
console.log(`总价:${formatCurrency(99.9)}`); // 总价:¥99.90
// 4. 条件表达式
const score = 85;
console.log(`成绩:${score >= 60 ? '及格' : '不及格'}`); // 成绩:及格
实际应用
javascript
// 生成HTML模板
function createUserCard(user) {
return `
<div class="user-card">
<h3>${user.name}</h3>
<p>邮箱:${user.email}</p>
<p>年龄:${user.age}岁</p>
<p>状态:${user.isActive ? '在线' : '离线'}</p>
</div>
`;
}
const user = {
name: "张三",
email: "zhangsan@example.com",
age: 28,
isActive: true
};
console.log(createUserCard(user));
五、解构赋值:快速提取数据
数组解构
javascript
// 基本解构
const numbers = [1, 2, 3, 4, 5];
const [first, second] = numbers;
console.log(first, second); // 1 2
// 跳过某些元素
const [a, , c] = numbers;
console.log(a, c); // 1 3
// 默认值
const colors = ['red'];
const [primary = 'blue', secondary = 'green'] = colors;
console.log(primary, secondary); // red green
// 剩余元素
const [x, y, ...rest] = numbers;
console.log(x, y, rest); // 1 2 [3, 4, 5]
// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];
console.log(m, n); // 2 1
对象解构
javascript
const user = {
name: "Alice",
age: 25,
email: "alice@example.com",
address: {
city: "Beijing",
street: "Main St"
}
};
// 基本解构
const { name, age } = user;
console.log(name, age); // Alice 25
// 重命名变量
const { name: userName, age: userAge } = user;
console.log(userName, userAge); // Alice 25
// 默认值
const { name, phone = '未填写' } = user;
console.log(phone); // 未填写
// 嵌套解构
const { address: { city, street } } = user;
console.log(city, street); // Beijing Main St
// 函数参数解构
function printUser({ name, age }) {
console.log(`${name}今年${age}岁`);
}
printUser(user); // Alice今年25岁
实际应用场景
javascript
// 1. 函数返回多个值
function getStats(numbers) {
return {
min: Math.min(...numbers),
max: Math.max(...numbers),
avg: numbers.reduce((a, b) => a + b) / numbers.length
};
}
const { min, max, avg } = getStats([1, 2, 3, 4, 5]);
console.log(`最小值: ${min}, 最大值: ${max}, 平均值: ${avg}`);
// 2. 导入模块时的解构
// import { useState, useEffect } from 'react';
// 3. 事件处理
button.addEventListener('click', ({ target, clientX, clientY }) => {
console.log(`点击了${target.tagName},位置: (${clientX}, ${clientY})`);
});
六、展开与剩余运算符:...的三重用法
展开运算符(Spread)
数组展开:
javascript
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 复制数组
const copy = [...arr1]; // [1, 2, 3],浅拷贝
// 函数调用
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3,相当于Math.max(1, 2, 3)
// 添加元素
const newArr = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]
对象展开:
javascript
const user = { name: "Alice", age: 25 };
const address = { city: "Beijing", country: "China" };
// 合并对象
const userWithAddress = { ...user, ...address };
// { name: "Alice", age: 25, city: "Beijing", country: "China" }
// 复制对象
const userCopy = { ...user }; // 浅拷贝
// 覆盖属性
const updatedUser = { ...user, age: 26 };
// { name: "Alice", age: 26 }
// 添加新属性
const userWithEmail = { ...user, email: "alice@example.com" };
剩余参数(Rest)
函数参数:
javascript
// 收集剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 结合普通参数
function greet(greeting, ...names) {
return `${greeting} ${names.join('、')}!`;
}
console.log(greet('Hello', 'Alice', 'Bob', 'Charlie')); // Hello Alice、Bob、Charlie!
解构中的剩余元素:
javascript
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first, second, rest); // 1 2 [3, 4, 5]
const { name, ...otherProps } = {
name: "Alice",
age: 25,
city: "Beijing"
};
console.log(name, otherProps); // Alice { age: 25, city: "Beijing" }
七、可选链与空值合并:安全的属性访问
可选链操作符 ?.
可选链操作符允许我们在尝试访问对象的深层属性时,如果中间某个属性不存在(即值为 null 或 undefined),表达式会短路返回 undefined,而不会抛出错误。
解决的问题:
javascript
// 传统写法(繁琐)
const city = user && user.address && user.address.city;
// 可选链写法(简洁)
const city = user?.address?.city;
各种使用场景:
javascript
const user = {
profile: {
name: "Alice",
getAge: function() { return 25; }
}
};
// 1. 对象属性访问
console.log(user?.profile?.name); // "Alice"
console.log(user?.address?.city); // undefined(不会报错)
// 2. 数组元素访问
const arr = null;
console.log(arr?.[0]); // undefined
// 3. 函数调用
console.log(user?.profile?.getAge?.()); // 25
console.log(user?.profile?.getName?.()); // undefined
// 4. 结合nullish判断
const defaultValue = user?.profile?.name ?? '默认用户';
空值合并操作符 ??
与||的区别:
javascript
const settings = {
theme: null,
fontSize: 0,
username: '',
notifications: false
};
// || 运算符(检查falsy值)
console.log(settings.theme || 'default'); // 'default'
console.log(settings.fontSize || 16); // 16(0是falsy)
console.log(settings.username || 'anonymous'); // 'anonymous'(''是falsy)
console.log(settings.notifications || true); // true(false是falsy)
// ?? 运算符(只检查null和undefined)
console.log(settings.theme ?? 'default'); // 'default'
console.log(settings.fontSize ?? 16); // 0(0不是nullish)
console.log(settings.username ?? 'anonymous'); // ''(空字符串不是nullish)
console.log(settings.notifications ?? true); // false(false不是nullish)
实际应用:
javascript
// API响应处理
function processResponse(response) {
const data = response?.data ?? {};
const message = response?.message ?? '操作成功';
const code = response?.code ?? 200;
return { data, message, code };
}
// 配置处理
const config = {
apiUrl: process.env.API_URL ?? 'https://api.default.com',
timeout: parseInt(process.env.TIMEOUT) ?? 5000,
retry: process.env.RETRY === 'true' ?? false
};
八、数组迭代方法:告别for循环
forEach:简单遍历
javascript
const numbers = [1, 2, 3, 4, 5];
// 传统for循环
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// forEach方法
numbers.forEach(number => {
console.log(number);
});
// 带索引的forEach
numbers.forEach((number, index) => {
console.log(`索引${index}: ${number}`);
});
map:数据转换
javascript
const numbers = [1, 2, 3, 4, 5];
// 每个元素乘以2
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 对象数组转换
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']
// 添加新属性
const usersWithId = users.map((user, index) => ({
...user,
id: index + 1
}));
filter:数据筛选
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 筛选偶数
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// 对象数组筛选
const users = [
{ name: 'Alice', age: 25, active: true },
{ name: 'Bob', age: 30, active: false },
{ name: 'Charlie', age: 35, active: true }
];
const activeUsers = users.filter(user => user.active);
console.log(activeUsers); // [{ name: 'Alice', ... }, { name: 'Charlie', ... }]
reduce:数据聚合
javascript
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 15
// 求最大值
const max = numbers.reduce((max, num) => num > max ? num : max, numbers[0]);
console.log(max); // 5
// 数组转对象
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const usersById = users.reduce((obj, user) => {
obj[user.id] = user;
return obj;
}, {});
console.log(usersById);
// {
// 1: { id: 1, name: 'Alice' },
// 2: { id: 2, name: 'Bob' },
// 3: { id: 3, name: 'Charlie' }
// }
其他实用方法
javascript
const numbers = [1, 2, 3, 4, 5];
// find:查找第一个符合条件的元素
const firstEven = numbers.find(num => num % 2 === 0);
console.log(firstEven); // 2
// some:检查是否有元素符合条件
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
// every:检查所有元素是否符合条件
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
// includes:检查是否包含某个值
const hasThree = numbers.includes(3);
console.log(hasThree); // true
九、实际项目综合示例
用户数据处理
javascript
// 原始数据
const users = [
{ id: 1, name: 'Alice', age: 25, active: true, skills: ['JavaScript', 'React'] },
{ id: 2, name: 'Bob', age: 30, active: false, skills: ['Python', 'Django'] },
{ id: 3, name: 'Charlie', age: 35, active: true, skills: ['JavaScript', 'Vue'] },
{ id: 4, name: 'Diana', age: 28, active: true, skills: ['React', 'Node.js'] }
];
// 1. 获取所有活跃用户的名字
const activeUserNames = users
.filter(user => user.active)
.map(user => user.name);
console.log('活跃用户:', activeUserNames); // ['Alice', 'Charlie', 'Diana']
// 2. 按技能分组
const usersBySkill = users.reduce((groups, user) => {
user.skills.forEach(skill => {
if (!groups[skill]) {
groups[skill] = [];
}
groups[skill].push(user.name);
});
return groups;
}, {});
console.log('按技能分组:', usersBySkill);
// 3. 统计年龄信息
const ageStats = users.reduce((stats, user) => {
return {
total: stats.total + user.age,
count: stats.count + 1,
average: (stats.total + user.age) / (stats.count + 1)
};
}, { total: 0, count: 0, average: 0 });
console.log('年龄统计:', ageStats);
API请求处理
javascript
// 模拟API响应
const apiResponse = {
data: {
users: [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
],
pagination: {
page: 1,
totalPages: 5
}
},
status: 200
};
// 安全地处理API响应
function processApiResponse(response) {
// 使用可选链和空值合并
const users = response?.data?.users ?? [];
const currentPage = response?.data?.pagination?.page ?? 1;
const status = response?.status ?? 500;
// 处理用户数据
const processedUsers = users.map(user => ({
...user,
displayName: user.name.toUpperCase(),
isValid: user.email?.includes('@') ?? false
}));
return {
users: processedUsers,
pagination: { currentPage },
success: status === 200
};
}
const result = processApiResponse(apiResponse);
console.log(result);
十、练习
练习题目
练习1:重构传统代码
javascript
// 将传统代码重构为现代语法
var numbers = [1, 2, 3, 4, 5];
var results = [];
for (var i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
results.push(numbers[i] * 2);
}
}
console.log(results);
练习2:用户数据处理
javascript
const users = [
{ name: 'Alice', age: 25, scores: [85, 90, 78] },
{ name: 'Bob', age: 30, scores: [92, 88, 95] },
{ name: 'Charlie', age: 35, scores: [76, 85, 80] }
];
// 任务:
// 1. 计算每个用户的平均分
// 2. 找出平均分最高的用户
// 3. 按年龄排序用户
// 4. 生成用户报告字符串
练习3:配置合并
javascript
const defaultConfig = {
apiUrl: 'https://api.default.com',
timeout: 5000,
retry: 3
};
const userConfig = {
apiUrl: 'https://api.custom.com',
retry: 5
};
// 合并配置,用户配置优先,但保留默认配置的其他属性
十一、总结
现代JavaScript语法让代码更加简洁、可读和易维护。这些特性不仅是TypeScript的基础,也是现代前端开发的必备技能。
关键要点:
-
使用
const/let替代var -
箭头函数简化回调写法
-
模板字符串提供更好的字符串处理
-
解构赋值让数据提取更简单
-
展开运算符简化数组合并和对象合并
-
可选链和空值合并让代码更安全
-
数组迭代方法替代传统循环
掌握了这些现代JavaScript特性后,下一篇我们将深入探讨**类型注解与类型推断**的细节。
觉得本文有帮助?欢迎在评论区分享你遇到的问题或心得体会!