1. 项目概述
"白纸编程js笔记 07~09课"是一套面向JavaScript初学者的系列教程笔记,主要涵盖JavaScript基础语法、核心概念和常见应用场景。这三节课的内容承上启下,从变量、函数等基础概念过渡到更高级的异步编程和DOM操作,是新手掌握JavaScript的关键阶段。
作为从零开始学习JavaScript的笔记,这个系列特别适合:
- 刚接触编程的纯小白
- 有其他语言基础但想学习JavaScript的开发者
- 需要系统性复习JS基础的中级开发者
我整理这份笔记时特别注重两点:一是用最直白的语言解释概念,二是提供大量可立即运行的代码示例。下面我就带大家详细拆解这三节课的核心内容。
2. 核心概念解析
2.1 变量与作用域
JavaScript的变量声明经历了从var到let/const的演变。在07课中,重点讲解了这三种声明方式的区别:
javascript复制// var存在变量提升问题
console.log(a); // undefined
var a = 1;
// let/const不存在提升,会报错
console.log(b); // ReferenceError
let b = 2;
作用域链是理解JavaScript执行环境的关键。当访问一个变量时,JS引擎会沿着作用域链向上查找:
- 先在当前函数作用域查找
- 如果没有,向上一级作用域查找
- 直到全局作用域
- 如果都没找到则报错
注意:使用let/const时要注意暂时性死区(TDZ),即在声明前访问变量会报错,这与var的undefined不同。
2.2 函数进阶
08课深入讲解了函数的多种形式和特性:
javascript复制// 函数声明
function sayHello() {
console.log('Hello');
}
// 函数表达式
const sayHi = function() {
console.log('Hi');
}
// 箭头函数
const add = (a, b) => a + b;
特别需要注意的是this的指向问题:
- 普通函数:this指向调用者
- 箭头函数:this继承自外层作用域
javascript复制const obj = {
name: 'Object',
regularFunc: function() {
console.log(this.name); // Object
},
arrowFunc: () => {
console.log(this.name); // undefined (浏览器中可能是window)
}
}
2.3 异步编程
09课开始接触JavaScript的核心特性 - 异步编程:
javascript复制// 回调地狱示例
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
console.log(c);
});
});
});
// Promise解决方案
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => console.log(c))
.catch(err => console.error(err));
// async/await终极方案
async function processData() {
try {
const a = await getData();
const b = await getMoreData(a);
const c = await getMoreData(b);
console.log(c);
} catch (err) {
console.error(err);
}
}
3. 实战应用技巧
3.1 DOM操作最佳实践
虽然现代前端框架减少了直接DOM操作,但理解底层原理仍然很重要:
javascript复制// 查询元素
const btn = document.getElementById('myButton');
// 事件监听
btn.addEventListener('click', function() {
console.log('Button clicked');
// 修改样式
this.style.backgroundColor = 'red';
// 创建新元素
const newElement = document.createElement('div');
newElement.textContent = 'New content';
document.body.appendChild(newElement);
});
重要提示:频繁操作DOM会导致重排(reflow)和重绘(repaint),严重影响性能。批量修改DOM时,可以先创建文档片段(document fragment),最后一次性插入。
3.2 数组高阶函数
JavaScript数组提供了强大的高阶函数,可以替代传统的for循环:
javascript复制const numbers = [1, 2, 3, 4, 5];
// map - 转换数组
const doubled = numbers.map(n => n * 2);
// filter - 筛选数组
const evens = numbers.filter(n => n % 2 === 0);
// reduce - 聚合计算
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 链式调用
const result = numbers
.map(n => n * 3)
.filter(n => n > 5)
.reduce((a, b) => a + b, 0);
3.3 错误处理策略
健壮的错误处理是专业开发的标志:
javascript复制// 同步错误处理
try {
JSON.parse('invalid json');
} catch (err) {
console.error('解析失败:', err.message);
}
// 异步错误处理
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.catch(err => {
console.error('请求失败:', err);
});
// 自定义错误类型
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
function validateInput(input) {
if (!input) {
throw new ValidationError('输入不能为空');
}
}
4. 常见问题与解决方案
4.1 闭包与内存泄漏
闭包是JavaScript的强大特性,但也容易导致内存泄漏:
javascript复制// 闭包示例
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
常见内存泄漏场景:
- 意外的全局变量
- 遗忘的定时器
- DOM引用未清理
- 闭包持有大对象
解决方案:
- 使用严格模式('use strict')避免意外全局变量
- 清除不再需要的定时器
- 移除事件监听器
- 必要时手动解除引用
4.2 异步编程陷阱
初学者常遇到的异步问题:
- 回调地狱:
javascript复制// 反模式
getData(function(a) {
getMoreData(a, function(b) {
// 嵌套越来越深
});
});
// 解决方案:Promise链式调用
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b));
- Promise错误处理不当:
javascript复制// 错误方式 - 可能遗漏错误
somePromise.then(handleSuccess);
// 正确方式 - 总是添加catch
somePromise
.then(handleSuccess)
.catch(handleError);
- async/await中的并行优化:
javascript复制// 顺序执行 - 效率低
const a = await getA();
const b = await getB();
// 并行执行 - 更高效
const [a, b] = await Promise.all([getA(), getB()]);
4.3 this指向问题
JavaScript中this的指向常常令人困惑:
javascript复制const obj = {
name: 'My Object',
printName: function() {
console.log(this.name);
},
printNameArrow: () => {
console.log(this.name);
}
};
obj.printName(); // 'My Object'
obj.printNameArrow(); // undefined (浏览器中可能是window)
// 解决方案:bind或箭头函数
const boundFunc = obj.printName.bind(obj);
setTimeout(boundFunc, 100);
5. 现代JavaScript特性
5.1 ES6+新特性
这些新特性极大提升了开发体验:
- 解构赋值:
javascript复制const person = { name: 'Alice', age: 25 };
const { name, age } = person;
const numbers = [1, 2, 3];
const [first, second] = numbers;
- 展开运算符:
javascript复制const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2];
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = { ...obj1, ...obj2 };
- 可选链(?.)和空值合并(??):
javascript复制const user = { profile: { name: 'Bob' } };
console.log(user?.profile?.name); // 'Bob'
console.log(user?.address?.street); // undefined
const value = null;
const defaultValue = value ?? 'default'; // 'default'
5.2 模块化开发
现代JavaScript使用ES Modules组织代码:
javascript复制// math.js
export function add(a, b) {
return a + b;
}
export const PI = 3.14159;
// app.js
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
模块化的优势:
- 避免全局污染
- 明确依赖关系
- 代码更易维护
- 支持tree shaking优化
5.3 TypeScript基础
虽然这三节课主要讲JavaScript,但TypeScript作为JS的超集值得了解:
typescript复制interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello, ${user.name}`;
}
const alice: User = { name: 'Alice', age: 25 };
console.log(greet(alice));
TypeScript的主要优点:
- 静态类型检查
- 更好的代码提示
- 更早发现错误
- 改善代码可维护性
6. 性能优化技巧
6.1 减少重排和重绘
浏览器渲染流程中的性能瓶颈:
- 使用transform和opacity实现动画,它们不会触发重排
- 使用document fragment批量插入DOM
- 避免在循环中读取布局属性(如offsetWidth)
- 使用will-change提示浏览器优化
javascript复制// 不好的做法
for (let i = 0; i < 100; i++) {
element.style.left = i + 'px';
}
// 好的做法 - 使用requestAnimationFrame
function animate() {
// 动画逻辑
requestAnimationFrame(animate);
}
animate();
6.2 内存管理
JavaScript的垃圾回收是自动的,但我们可以优化:
- 避免不必要的全局变量
- 及时清除不再需要的定时器和事件监听器
- 使用WeakMap和WeakSet处理弱引用
- 对于大型数据,考虑分页或懒加载
javascript复制// 事件监听器清理
function setup() {
const button = document.getElementById('myButton');
button.addEventListener('click', onClick);
// 清理函数
return () => {
button.removeEventListener('click', onClick);
};
}
const cleanup = setup();
// 当不再需要时调用cleanup()
6.3 代码分割与懒加载
现代前端应用应该按需加载代码:
javascript复制// 动态导入实现懒加载
const loadModule = async () => {
const module = await import('./module.js');
module.doSomething();
};
// Webpack等打包工具会自动代码分割
button.addEventListener('click', loadModule);
优化策略:
- 路由级代码分割
- 组件级懒加载
- 预加载关键资源
- 使用service worker缓存
7. 调试与测试
7.1 Chrome开发者工具技巧
高效使用开发者工具可以事半功倍:
- 条件断点:右键点击行号设置条件
- 日志点:不暂停执行的情况下输出日志
- 性能分析:记录和分析运行时性能
- 内存快照:查找内存泄漏
- 网络限速:模拟慢速网络
javascript复制// 使用debugger语句
function problematicFunction() {
debugger; // 执行到这里会暂停
// 问题代码
}
7.2 单元测试基础
即使初学者也应该培养测试意识:
javascript复制// 使用Jest测试框架示例
function sum(a, b) {
return a + b;
}
describe('sum function', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('handles negative numbers', () => {
expect(sum(-1, -1)).toBe(-2);
});
});
测试金字塔原则:
- 大量单元测试(快速、隔离)
- 适量集成测试(组件交互)
- 少量E2E测试(用户流程)
7.3 错误监控
生产环境错误监控至关重要:
- 全局错误捕获:
javascript复制window.addEventListener('error', (event) => {
// 发送错误到服务器
logError(event.error);
});
- Promise未捕获异常:
javascript复制window.addEventListener('unhandledrejection', (event) => {
// 发送Promise错误
logError(event.reason);
});
- 使用Sentry等专业监控工具:
javascript复制import * as Sentry from '@sentry/browser';
Sentry.init({ dsn: 'your-dsn' });
try {
riskyOperation();
} catch (err) {
Sentry.captureException(err);
}
8. 项目结构与工具链
8.1 现代JavaScript项目结构
合理的项目结构能提高可维护性:
code复制my-project/
├── src/
│ ├── components/ # 可复用组件
│ ├── pages/ # 页面组件
│ ├── utils/ # 工具函数
│ ├── styles/ # 全局样式
│ ├── assets/ # 静态资源
│ └── index.js # 入口文件
├── public/ # 静态文件
├── tests/ # 测试代码
├── .eslintrc.js # ESLint配置
├── .prettierrc # 代码格式化配置
└── package.json # 项目配置
8.2 必备开发工具
提高效率的工具链:
-
代码编辑器:VS Code + 插件
- ESLint
- Prettier
- Debugger for Chrome
- JavaScript (ES6) code snippets
-
构建工具:
- Webpack/Rollup/Vite
- Babel (转译ES6+代码)
- ESLint (代码质量检查)
- Prettier (代码格式化)
-
包管理:
- npm/yarn/pnpm
- 理解语义化版本(^ ~ *)
8.3 调试配置示例
VS Code的launch.json配置:
json复制{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src"
},
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": ["--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
9. 学习资源与进阶路线
9.1 推荐学习资源
-
官方文档:
- MDN JavaScript指南
- ECMAScript规范
- Node.js文档
-
在线课程:
- freeCodeCamp JavaScript课程
- Codecademy JavaScript课程
- 现代JavaScript教程(zh.javascript.info)
-
书籍:
- 《JavaScript高级程序设计》(红宝书)
- 《你不知道的JavaScript》系列
- 《Eloquent JavaScript》
9.2 技能进阶路线
JavaScript学习路线建议:
-
基础阶段(1-2个月):
- 语法基础
- DOM操作
- 异步编程
- ES6+新特性
-
中级阶段(3-6个月):
- 设计模式
- 性能优化
- 测试驱动开发
- 工具链配置
-
高级阶段(6个月+):
- 框架原理(Vue/React)
- 工程化实践
- 类型系统(TypeScript)
- 函数式编程
9.3 实战项目建议
通过实际项目巩固知识:
-
初级项目:
- 待办事项应用
- 天气预报应用
- 简单游戏(如井字棋)
-
中级项目:
- 博客系统(前后端分离)
- 电商网站前端
- 数据可视化仪表盘
-
高级项目:
- 全栈应用(如社交平台)
- 浏览器插件
- 微前端架构应用
我在实际教学中发现,很多初学者在07-09课这个阶段会遇到"知识高原期" - 感觉学了很多但不知道如何应用。这时候最好的突破方法就是动手做项目,哪怕是从最简单的开始。遇到问题就查阅文档、搜索解决方案,这种"学以致用"的方式往往最有效。
