前端技术栈全解 - JavaScript 核心篇
本文为前端技术栈系列第一篇,完整覆盖 JavaScript 核心知识点,包含原理、用法、场景、替代方案、优劣势、优化方案、问题排查,可直接用于博客发布、学习笔记、面试复习。
目录
- JavaScript 基础语法与数据类型
- 函数(普通函数 / 箭头函数 / 闭包 / 高阶函数)
- 作用域与执行上下文
- 原型与原型链
- 异步编程(回调 / Promise/async-await)
- 事件循环(Event Loop)
- 数组常用方法与性能
- 深拷贝与浅拷贝
- 防抖与节流
- this 指向与绑定规则
- ES6+ 核心新特性
- JavaScript 内存管理与垃圾回收
- 常见报错与解决方案
1. JavaScript 基础语法与数据类型
核心定义
JavaScript 是一门弱类型、动态类型的解释型脚本语言,分为原始类型和引用类型。
分类
原始类型(7 种)
Undefined/Null/Boolean/Number/String/Symbol/BigInt- 存储方式:栈内存,值直接存储,赋值是值拷贝
引用类型
Object/Array/Function/Date等- 存储方式:栈存地址,堆存数据,赋值是引用拷贝
用法
js
// 原始类型
let a = 10;
let b = a;
b = 20;
console.log(a); // 10(互不影响)
// 引用类型
let obj1 = { name: "js" };
let obj2 = obj1;
obj2.name = "ts";
console.log(obj1.name); // ts(指向同一内存)
使用场景
- 页面交互逻辑
- 数据处理与判断
- 表单验证
- DOM 操作
- 异步请求处理
可替代方案
- TypeScript(JS 超集,强类型)
- Dart、Kotlin/JS(编译到 JS)
优劣势
优势
- 无需编译,直接运行
- 动态灵活,开发效率高
- 全端可用(浏览器 / Node.js/ 小程序 / 桌面端)
劣势
- 弱类型容易产生隐式转换错误
- 运行时才能发现类型问题
- 大型项目难以维护
优化方式
- 使用
const>let>var - 避免隐式类型转换
- 优先使用原始类型提升性能
- 使用 TS 做类型约束
常见问题
问题 1:== 和 === 区别?为什么用 ===?
==会进行隐式类型转换,容易出错===严格比较类型 + 值- 解决:永远优先使用
===
问题 2:0.1 + 0.2 !== 0.3
- 原因:JS 采用 IEEE 754 双精度浮点数,二进制无法精确表示部分小数
- 解决:乘以 10 转整数计算后再除回
js
(0.1 * 10 + 0.2 * 10) / 10 === 0.3
2. 函数(普通 / 箭头 / 闭包 / 高阶)
原理
函数是一等公民,可以作为参数、返回值、变量赋值,本质是可调用的对象。
分类与用法
1)普通函数
js
function fn() {}
const fn = function() {}
- 有自己的
this - 有
arguments - 可以作为构造函数
2)箭头函数
js
const fn = () => {}
- 无自己的 this,继承外层作用域
- 无
arguments - 不能用作构造函数
- 不能使用
yield
3)闭包
js
function outer() {
let a = 1;
return function inner() {
return a++;
}
}
- 原理:函数能够访问定义时的词法作用域,即使在外部执行
- 场景:模块化、缓存、防抖节流
4)高阶函数
- 接收函数为参数 或 返回函数
- 例:
map/filter/reduce
使用场景
- 逻辑封装与复用
- 模块化
- 异步流程控制
- 设计模式实现
可替代方案
无直接替代,函数是 JS 核心;可使用 Class 替代部分场景。
优劣势
优势
- 灵活、可组合
- 支持函数式编程
- 闭包可实现私有变量
劣势
- 闭包滥用会造成内存泄漏
- 嵌套过深产生回调地狱
- this 指向混乱
优化
- 避免过度使用闭包
- 箭头函数简化回调
- 函数单一职责
- 提取公共工具函数
常见问题
问题:闭包导致内存泄漏
- 原因:闭包引用的变量无法被垃圾回收
- 解决:手动解除引用
fn = null
3. 作用域与执行上下文
原理
- 作用域:变量可访问范围
- 执行上下文:函数运行时的环境(变量、this、作用域链)
分类
- 全局作用域
- 函数作用域
- 块级作用域(let/const)
作用域链
内部作用域 -> 父级 -> ... -> 全局,逐级查找变量。
使用场景
- 变量隔离
- 避免全局污染
- 模块化开发
优化
- 减少全局变量
- 使用块级作用域
- 使用立即执行函数(IIFE)/ESModule
4. 原型与原型链
原理
JS 基于原型链实现继承,每个对象都有 __proto__ 指向构造函数的 prototype。
核心公式
js
对象.__proto__ === 构造函数.prototype
构造函数.prototype.constructor === 构造函数
用法
js
function Person() {}
Person.prototype.say = () => console.log('hello')
const p = new Person()
p.say()
使用场景
- 继承
- 方法共享(节省内存)
- 框架底层(Vue/React 源码大量使用)
可替代方案
- ES6 class(语法糖,底层仍是原型)
- 组合继承、寄生继承
优劣势
优势
- 内存高效
- 灵活的继承机制
劣势
- 理解成本高
- 继承关系不直观
常见问题
问题:instanceof 判断不准
- 解决:使用
Object.prototype.toString.call(val)
5. 异步编程(回调 / Promise /async-await)
原理
JS 是单线程,异步用于不阻塞主线程处理耗时任务。
用法
1)回调函数
js
setTimeout(() => {}, 1000)
- 问题:回调地狱
2)Promise
js
new Promise((resolve) => resolve())
.then()
.catch()
3)async-await(语法糖,基于 Promise)
js
async function fetchData() {
const res = await axios.get('/api')
}
使用场景
- 网络请求
- 定时器
- 文件操作
- I/O 操作
可替代方案
- Generator
- RxJS(响应式编程)
优劣势
- 回调:简单但难维护
- Promise:解决地狱,链式优雅
- async-await:最直观,同步写法
优化
- 并行请求用
Promise.all - 避免滥用 await 造成串行
常见问题
问题:异步错误捕获
- 解决:
try/catch包裹 await
6. 事件循环(Event Loop)
原理
JS 单线程 -> 任务队列 -> 事件循环调度宏任务 / 微任务。
执行顺序
- 同步代码
- 微任务(Promise.then、queueMicrotask)
- 宏任务(setTimeout、setInterval、IO)
使用场景
- 理解 JS 执行机制
- 面试必考
- 解决异步执行顺序问题
优化
- 避免长任务阻塞渲染
- 拆分大量计算为 chunk
7. 数组常用方法与性能
高频方法
forEach/map/filter/reduce/find/some/everyslice/splice/concat/flat
性能
- 原生 for 循环 > forEach > map
优化
- 避免频繁修改数组长度
- 大数据量使用原生循环
- 使用
Set去重
8. 深拷贝与浅拷贝
浅拷贝
只拷贝第一层
Object.assign- 展开运算符
{...obj}
深拷贝
递归拷贝所有层级
js
JSON.parse(JSON.stringify(obj)) // 缺点:丢失函数、Date、RegExp、undefined
完美深拷贝:手写递归 + 类型判断
使用场景
- 状态管理
- 对象复制不污染原数据
9. 防抖与节流
防抖(debounce)
触发后 N 秒内只执行最后一次
- 场景:搜索框输入、窗口 resize
节流(throttle)
N 秒内只执行一次
- 场景:滚动、鼠标移动、高频点击
优化
- 用于减少不必要请求与计算
- 提升页面流畅度
10. this 指向
绑定规则
- 默认绑定:全局 /undefined(严格模式)
- 隐式绑定:对象调用
- 显式绑定:call /apply/bind
- new 绑定
- 箭头函数:继承外层 this
解决 this 混乱
- 优先箭头函数
- 使用 bind 固定 this
11. ES6+ 核心特性
- let / const
- 解构赋值
- 展开 / 剩余运算符
- Promise / async-await
- 类 / 模块化
- Map / Set
优势
代码更简洁、可读性更高、工程化更强
12. 内存管理与垃圾回收
垃圾回收机制
- 标记清除
- 引用计数
内存泄漏场景
- 意外全局变量
- 被遗忘的定时器
- 闭包滥用
- DOM 引用未清除
解决
- 及时清除定时器
- 解除事件监听
- 闭包后手动置 null
13. 常见报错与解决方案
1. Cannot read property 'xxx' of undefined
- 原因:访问了 undefined 上的属性
- 解决:可选链
obj?.xxx
2. Maximum call stack size exceeded
- 原因:递归死循环
- 解决:加终止条件
3. Promise 未捕获异常
- 解决:加 catch / 全局监听 unhandledrejection
4. 变量未定义
- 解决:检查作用域 + 声明变量
总结
本文完整覆盖 JavaScript 核心知识点,从基础语法、函数、作用域、原型、异步、事件循环、性能优化、错误处理全维度详解,适合前端学习、面试、博客发布。
后续将更新:TypeScript、HTML/CSS、Vue、React、工程化、浏览器原理、网络请求、性能优化等全套前端技术栈文档。