未闭合的段落
已闭合的段落
影响分析:
实际案例: 一个未闭合的 检测工具: 规范详解: 浏览器兼容性: 标准演变: 最佳实践: 影响分析: alt 属性编写建议: 关键表单属性: 表单验证示例: 预防措施: 变量声明最佳实践: 异步编程模式: 错误处理技巧: this 绑定规则: this 使用场景: 闭包原理: 实际应用: DOM 加载阶段: 其他加载事件: 现代替代方案: Promise 状态: 错误处理模式: 类型转换规则: 特殊情况: 最佳实践: 常见内存泄漏场景: 检测工具: 核心功能: 高级功能: ESLint: TypeScript: Jest 测试示例: 测试类型: 测试最佳实践: 现代 JavaScript 特性: 可选链操作符: 空值合并: 默认参数: 输入验证: 编码习惯: 性能优化: 学习资源: 通过系统性地避免这些常见错误,并持续实践良好的编码习惯,开发者可以显著提升前端代码的质量、可维护性和可靠性。
2. 嵌套错误
可以包含块级元素,但需确保语义正确
3. 错误使用自闭合标签
)4. 缺少必要属性
5. 表单错误
name
:表单提交时的参数名id
:用于标签关联和 JavaScript 操作for
(label):与输入字段的id关联required
:客户端表单验证
二、JavaScript 常见错误
1. 变量未定义/拼写错误
// 错误示例
console.log(myVariable); // 未声明变量
let count = 10;
console.log(cout); // 拼写错误
// 正确示例
let myVariable = "Hello";
console.log(myVariable);
'use strict';
// 严格模式下使用未声明变量会抛出错误
no-undef
规则检测未定义变量no-unused-vars
规则检测未使用变量
const
声明不变的值let
声明需要重新赋值的变量var
(存在变量提升问题)2. 异步操作同步处理
// 错误示例:期望同步结果
let result;
fetch('api/data').then(res => result = res);
console.log(result); // 输出undefined
// 正确示例
fetch('api/data')
.then(res => res.json())
.then(data => console.log(data));
// 现代写法:使用async/await
async function getData() {
const response = await fetch('api/data');
const data = await response.json();
console.log(data);
}
.then().catch()
// 方法1:try/catch
async function fetchData() {
try {
const data = await fetchApi();
console.log(data);
} catch (error) {
console.error('API请求失败:', error);
}
}
// 方法2:Promise.catch
fetchApi()
.then(data => console.log(data))
.catch(error => console.error('API请求失败:', error));
3. this 指向问题
// 错误示例
const obj = {
name: "Alice",
greet: function() {
setTimeout(function() {
console.log(`Hello, ${this.name}`); // this指向window
}, 1000);
}
};
// 解决方案1:箭头函数
const obj = {
name: "Alice",
greet: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}`); // this指向obj
}, 1000);
}
};
// 解决方案2:保存this引用
const obj = {
name: "Alice",
greet: function() {
const that = this;
setTimeout(function() {
console.log(`Hello, ${that.name}`);
}, 1000);
}
};
// 1. 事件处理程序
button.addEventListener('click', function() {
console.log(this); // 指向button元素
});
// 2. 类方法
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}`);
}
}
// 3. 模块模式
const module = {
data: [],
add(item) {
this.data.push(item);
}
};
4. 闭包陷阱
// 错误示例:循环中创建闭包
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000); // 全部输出5
}
// 解决方案1:使用let(块级作用域)
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000); // 输出0-4
}
// 解决方案2:立即执行函数
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => console.log(j), 1000);
})(i);
}
const counter = (function() {
let count = 0;
return {
increment() { count++; },
get() { return count; }
};
})();
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
console.log(`Button ${i} clicked`);
});
}
5. DOM 操作时机错误
// 错误示例:DOM未加载完成时操作
document.getElementById('myButton').addEventListener('click', () => {
// 可能报错:无法获取元素
});
// 解决方案1:DOMContentLoaded事件
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('myButton').addEventListener('click', () => {
// 安全操作DOM
});
});
// 解决方案2:放在HTML底部
load
:所有资源(包括图片)加载完成readystatechange
:document.readyState 变化时触发// 使用 defer 属性
// 或使用模块
6. 未处理 Promise 拒绝
// 错误示例:没有catch
fetch('api/data')
.then(res => res.json())
.then(data => console.log(data));
// 正确示例
fetch('api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
// 全局捕获(Node.js)
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的Promise拒绝:', reason);
});
// 全局捕获(Browser)
window.addEventListener('unhandledrejection', event => {
console.error('未处理的Promise拒绝:', event.reason);
});
fetchData()
.then(processData)
.then(saveData)
.catch(logError);
fetchData()
.then(data => {
if (!data.valid) {
throw new Error('Invalid data');
}
return data;
})
.catch(error => {
console.error(error);
return defaultData;
});
7. 错误使用相等运算符
// 错误示例:使用==导致类型转换
if (0 == false) { // true
console.log('0等于false');
}
// 正确示例:使用===严格比较
if (0 === false) { // false
console.log('0严格不等于false');
}
==
会进行类型转换(强制类型转换)===
不会进行类型转换(严格相等)null == undefined; // true
null === undefined; // false
NaN == NaN; // false
NaN === NaN; // false
Number.isNaN(NaN); // true
===
和 !==
Number.isNaN(value)
或 Object.is(value, NaN)
value == null
(同时匹配 null 和 undefined)8. 内存泄漏
// 错误示例:定时器未清除
setInterval(() => {
console.log('执行中...');
}, 1000);
// 正确示例:组件销毁时清除
const timer = setInterval(() => {
console.log('执行中...');
}, 1000);
// 清理
clearInterval(timer);
const elements = [];
function createElement() {
const el = document.createElement('div');
document.body.appendChild(el);
elements.push(el); // 即使移除DOM,数组仍保持引用
}
三、调试工具与预防措施
1. 浏览器开发者工具
console
方法: console.log('普通日志');
console.error('错误');
console.warn('警告');
console.table(data);
console.group('分组');
2. 静态代码分析工具
npm install eslint --save-dev
npx eslint --init
semi
: 强制分号quotes
: 引号风格no-unused-vars
: 未使用变量eqeqeq
: 强制使用 ===
{
"compilerOptions": {
"strict": true,
"target": "es6",
"module": "esnext"
}
}
3. 单元测试
// math.js
function sum(a, b) {
return a + b;
}
// math.test.js
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
4. 防御性编程
const user = { profile: { name: "Alice" } };
console.log(user?.profile?.name); // "Alice"
console.log(user?.address?.city); // undefined 而非报错
const config = {
timeout: null,
retries: undefined
};
const timeout = config.timeout ?? 5000; // 5000
const retries = config.retries ?? 3; // 3
function greet(name = "Guest") {
console.log(`Hello, ${name}`);
}
function divide(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('参数必须为数字');
}
if (b === 0) {
throw new RangeError('除数不能为零');
}
return a / b;
}
四、总结与最佳实践
'use strict'