1. JavaScript语句基础与核心概念
JavaScript作为现代Web开发的基石语言,其语句体系构成了程序逻辑的骨架。理解这些基础构建块对于编写高效、可维护的代码至关重要。让我们从最基础的变量声明开始,逐步深入JavaScript的语句世界。
1.1 变量声明:从var到const的演进
在ES6之前,JavaScript只有var一种变量声明方式,这导致了许多意料之外的行为:
javascript复制function varExample() {
console.log(x); // 输出undefined而非报错
var x = 5;
console.log(x); // 输出5
}
这种"变量提升"现象常常让初学者困惑。ES6引入的let和const通过块级作用域解决了这个问题:
javascript复制function letExample() {
// console.log(y); // 这里会报错,与var不同
let y = 10;
console.log(y); // 输出10
if (true) {
let y = 20; // 不同的块级作用域
console.log(y); // 输出20
}
console.log(y); // 输出10
}
重要实践建议:在现代JavaScript开发中,默认使用
const声明变量,只有当确实需要重新赋值时才使用let,基本可以完全避免使用var。这能显著减少作用域相关的问题。
1.2 赋值操作:不只是简单的等号
赋值语句看似简单,但JavaScript提供了多种复合赋值运算符,可以简化代码:
javascript复制let count = 0;
count += 5; // 等同于 count = count + 5
count *= 2; // 等同于 count = count * 2
count++; // 后置递增
++count; // 前置递增
特别要注意的是解构赋值,这是ES6引入的强大特性:
javascript复制// 数组解构
const [first, second] = [10, 20];
// 对象解构
const {name, age} = {name: 'Alice', age: 25};
// 函数参数解构
function greet({name, age}) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
2. 流程控制:程序决策的艺术
2.1 条件语句:if与switch的抉择
if...else是条件判断的基础,但有几个细节值得注意:
javascript复制if (condition) {
// 当condition为truthy时执行
} else if (anotherCondition) {
// 当第一个条件不满足且anotherCondition为truthy时执行
} else {
// 当前面条件都不满足时执行
}
关键点:JavaScript中,以下值会被视为falsy:false、0、""、null、undefined、NaN,其他所有值都是truthy。
switch语句适用于多个明确值的比较:
javascript复制switch(expression) {
case value1:
// 代码块
break; // 必须的,否则会"贯穿"到下一个case
case value2:
// 代码块
break;
default:
// 默认代码块
}
2.2 循环结构:从基础到高级迭代
for循环有多种变体,满足不同需求:
javascript复制// 传统for循环
for (let i = 0; i < 10; i++) {
console.log(i);
}
// for...of循环(ES6)
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item); // 1, 2, 3
}
// for...in循环(遍历对象属性)
const obj = {a: 1, b: 2};
for (const key in obj) {
console.log(key); // 'a', 'b'
}
while和do...while的区别在于条件检查的时机:
javascript复制let i = 0;
while (i < 3) {
console.log(i); // 0, 1, 2
i++;
}
let j = 0;
do {
console.log(j); // 0, 1, 2
j++;
} while (j < 3);
3. 函数与异常处理
3.1 函数定义:从声明到箭头函数
JavaScript函数有多种定义方式,各有特点:
javascript复制// 函数声明(会提升)
function add(a, b) {
return a + b;
}
// 函数表达式
const multiply = function(a, b) {
return a * b;
};
// 箭头函数(ES6)
const divide = (a, b) => a / b;
箭头函数有几个重要特性:
- 没有自己的
this,继承自外层作用域 - 不能用作构造函数
- 没有
arguments对象 - 更简洁的语法
3.2 异常处理:try-catch-finally模式
健壮的程序需要妥善处理可能出现的错误:
javascript复制try {
// 可能抛出错误的代码
const result = riskyOperation();
console.log(result);
} catch (error) {
// 处理错误
console.error('操作失败:', error.message);
} finally {
// 无论是否出错都会执行
cleanupResources();
}
最佳实践:不要用try-catch处理预期内的流程控制,它应该只用于真正的异常情况。过度使用会影响性能。
4. 异步编程:从回调地狱到async/await
4.1 Promise:异步编程的基础
Promise代表了异步操作的最终完成或失败:
javascript复制const fetchData = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('数据获取成功');
} else {
reject(new Error('获取数据失败'));
}
}, 1000);
});
fetchData
.then(data => console.log(data))
.catch(error => console.error(error));
Promise链可以避免回调地狱:
javascript复制doFirstThing()
.then(result => doSecondThing(result))
.then(newResult => doThirdThing(newResult))
.catch(error => console.error(error));
4.2 async/await:同步风格的异步代码
async函数让异步代码看起来像同步代码:
javascript复制async function fetchUserData() {
try {
const response = await fetch('/api/user');
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
重要提示:await只能在async函数中使用。在顶层模块中,现代JavaScript环境支持顶层await。
5. 模块化:组织大型代码库
5.1 export与import的基本用法
模块化是现代JavaScript开发的基石:
javascript复制// math.js
export const PI = 3.14159;
export function square(x) {
return x * x;
}
// app.js
import { PI, square } from './math.js';
console.log(square(PI));
5.2 默认导出与命名导出
模块可以有一个默认导出和多个命名导出:
javascript复制// utils.js
export default function() {
console.log('默认导出');
}
export function helper1() {}
export function helper2() {}
// app.js
import mainFunction, { helper1, helper2 } from './utils.js';
6. 高级语句与技巧
6.1 标签语句:控制复杂循环
标签语句可以与break或continue配合使用,控制多层循环:
javascript复制outerLoop: for (let i = 0; i < 3; i++) {
innerLoop: for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) {
break outerLoop; // 直接跳出外层循环
}
console.log(i, j);
}
}
6.2 with语句:为何要避免使用
with语句可以简化对同一对象多个属性的访问,但存在严重问题:
javascript复制const obj = {a: 1, b: 2};
with (obj) {
console.log(a + b); // 3
}
强烈建议:永远不要使用with语句。它会降低代码可读性,导致性能问题,并且在严格模式下被禁止。
7. 性能优化与调试技巧
7.1 语句级性能考量
某些语句写法会影响性能:
javascript复制// 较慢的循环写法
for (let i = 0; i < array.length; i++) {
// ...
}
// 更快的循环写法
const length = array.length;
for (let i = 0; i < length; i++) {
// ...
}
// 最快的现代写法
array.forEach(item => {
// ...
});
7.2 调试技巧:利用debugger语句
debugger语句可以设置断点:
javascript复制function problematicFunction() {
debugger; // 执行到这里会暂停
// 问题代码
}
结合浏览器开发者工具,这是调试复杂问题的有力工具。
8. 现代JavaScript最佳实践
8.1 类型检查与防御性编程
虽然JavaScript是动态类型语言,但类型检查很重要:
javascript复制function safeDivide(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('参数必须是数字');
}
if (b === 0) {
throw new Error('除数不能为零');
}
return a / b;
}
8.2 函数式编程风格
JavaScript支持函数式编程范式:
javascript复制// 纯函数
function add(a, b) {
return a + b;
}
// 高阶函数
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
console.log(double(5)); // 10
9. 常见陷阱与解决方案
9.1 变量作用域问题
javascript复制for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出3次3
}
// 解决方案1:使用let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出0,1,2
}
// 解决方案2:闭包
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100); // 输出0,1,2
})(i);
}
9.2 异步操作顺序问题
javascript复制// 错误的顺序
function fetchData() {
let data;
fetch('/api').then(response => data = response);
console.log(data); // undefined
}
// 正确的处理
async function fetchDataCorrectly() {
const response = await fetch('/api');
const data = await response.json();
console.log(data); // 正确的数据
}
10. 实战案例:构建一个小型应用
让我们用学到的语句知识构建一个简单的待办事项应用:
javascript复制// 模块1: 状态管理
const state = {
todos: [],
currentFilter: 'all'
};
// 模块2: 操作函数
export function addTodo(text) {
if (!text.trim()) throw new Error('待办内容不能为空');
state.todos.push({
id: Date.now(),
text,
completed: false
});
}
export function toggleTodo(id) {
const todo = state.todos.find(t => t.id === id);
if (todo) todo.completed = !todo.completed;
}
// 模块3: 视图渲染
export function renderTodos() {
const filteredTodos = state.todos.filter(todo => {
if (state.currentFilter === 'active') return !todo.completed;
if (state.currentFilter === 'completed') return todo.completed;
return true;
});
const todoList = document.getElementById('todo-list');
todoList.innerHTML = filteredTodos.map(todo => `
<li class="${todo.completed ? 'completed' : ''}">
<input type="checkbox" ${todo.completed ? 'checked' : ''}
onchange="toggleTodo(${todo.id})">
${todo.text}
</li>
`).join('');
}
// 主模块
import { addTodo, toggleTodo, renderTodos } from './todoApp.js';
document.getElementById('add-todo').addEventListener('submit', e => {
e.preventDefault();
const input = document.getElementById('todo-input');
try {
addTodo(input.value);
input.value = '';
renderTodos();
} catch (error) {
alert(error.message);
}
});
这个示例展示了如何运用各种JavaScript语句构建实际应用,包括变量声明、函数定义、条件判断、循环、错误处理等。