首页 • 关于本站 • 联系我们
系统梳理 · 精准解析 · 实战提升 · 从容应对技术面试

一、JavaScript核心概念解析

1.1 变量提升与作用域

在JavaScript中,变量提升(Hoisting)是ES3时代遗留的重要机制。它指变量声明(var)和函数声明会被“提升”到其作用域顶部,但赋值操作不会被提升。

⚠️ 注意:ES6引入的let和const虽也有提升,但存在“暂时性死区”(TDZ),在声明之前访问会报错。
var a = 'global'; function test() { console.log(a); // undefined(不是global) var a = 'local'; } test();

上述代码等价于:

var a = 'global'; function test() { var a; // 声明被提升,但赋值未提升 console.log(a); // undefined a = 'local'; } test();

作用域链是JavaScript实现词法作用域的核心机制。当访问一个变量时,引擎会从当前作用域开始,逐级向上查找,直到全局作用域。

  • 全局作用域(Global Scope)
  • 函数作用域(Function Scope)
  • 块级作用域(Block Scope):ES6引入,通过let/const实现

1.2 this指向机制

this的指向是面试高频考点,其值取决于函数的调用方式,而非定义位置。主要规则如下:

默认绑定
隐式绑定
显式绑定
new绑定

独立函数调用时,this指向全局对象(浏览器中为window,Node.js中为global)。严格模式下为undefined。

function foo() { console.log(this); } foo(); // window(非严格) / undefined(严格模式)

作为对象方法调用时,this指向该对象。

const obj = { name: '考ACCA', getName: function() { return this.name; } }; console.log(obj.getName()); // '考ACCA'
⚠️ 注意:方法赋值后丢失上下文,如const fn = obj.getName; fn()会指向全局对象。

通过call、apply、bind可显式指定this。

function greet(greeting, punctuation) { return this.name + ',' + greeting + punctuation; } const person = { name: 'JavaScript' }; greet.call(person, '你好', '!'); // 'JavaScript,你好!' greet.apply(person, ['你好', '!']); // 同上 const boundGreet = greet.bind(person, '你好'); boundGreet('!'); // 同上

通过new调用构造函数时,this指向新创建的对象实例。

function Person(name) { this.name = name; } const p = new Person('考ACCA'); console.log(p.name); // '考ACCA'
💡 构造函数返回对象时,new表达式返回该对象;返回基本类型则忽略,仍返回实例。

1.3 闭包(Closure)

闭包是函数与其词法环境的组合。它允许内部函数访问外部函数的变量,即使外部函数已执行完毕。

常见用途:数据私有化、函数柯里化、延迟执行。

function createCounter() { let count = 0; // 私有变量 return function() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2
⚠️ 闭包可能导致内存泄漏(旧版IE),需注意及时解除引用。

常见陷阱:循环中创建闭包时,变量值共享问题。

var buttons = []; for (var i = 0; i < 3; i++) { buttons[i] = function() { console.log(i); }; } buttons[0](); // 3(非0) buttons[1](); // 3 buttons[2](); // 3

解决方案:使用let(块级作用域)或IIFE。

for (let i = 0; i < 3; i++) { buttons[i] = function() { console.log(i); }; } buttons[0](); // 0

1.4 原型与原型链

JavaScript采用基于原型的继承机制。每个对象都有一个隐式原型(__proto__),指向其构造函数的prototype属性;构造函数的prototype对象有constructor属性指回构造函数。

function Animal(name) { this.name = name; } const dog = new Animal('Dog'); console.log(dog.__proto__ === Animal.prototype); // true console.log(Animal.prototype.constructor === Animal); // true

原型链是对象属性查找的路径:当访问对象属性时,若自身无该属性,则沿__proto__向上查找,直至null。

console.log(dog.toString()); // '[object Object]'(来自Object.prototype)
💡 Object.getPrototypeOf(obj)是获取原型的标准方式(避免直接使用__proto__)。

二、ES6+ 核心新特性

2.1 let / const 与块级作用域

ES6引入let和const,解决var的变量提升与作用域问题:

  • 块级作用域:由{}界定,避免变量泄露
  • 禁止重复声明:同一作用域内不能重复声明同名变量
  • 暂时性死区(TDZ):在声明前访问报错
  • const声明常量,但对象属性可修改(非深冻结)
if (true) { let a = 1; const b = { x: 10 }; b.x = 20; // 允许 const c = 1; c = 2; // TypeError } console.log(a); // ReferenceError

2.2 解构赋值

从数组或对象中提取值,赋给变量,提升代码简洁性。

// 数组解构 const [first, , third] = ['A', 'B', 'C']; console.log(first, third); // 'A' 'C' // 对象解构 const { name, age = 18 } = { name: '考ACCA' }; console.log(name, age); // '考ACCA' 18 // 函数参数解构 function fetchData({ url, method = 'GET' }) { return { url, method }; } fetchData({ url: '/api' }); // { url: '/api', method: 'GET' }
💡 解构时可设置默认值,避免undefined错误。

2.3 箭头函数

箭头函数不绑定自己的this、arguments、super,而是继承外层作用域。

const obj = { name: '考ACCA', getName: function() { setTimeout(() => { console.log(this.name); // '考ACCA'(继承外层this) }, 0); } }; obj.getName();

适用场景:回调函数、简短函数表达式

不适用场景:需要动态this的构造函数、事件处理器(需显式绑定)

2.4 模板字符串与扩展运算符

// 模板字符串 const name = '考ACCA'; console.log(`Hello, ${name}!`); // 'Hello, 考ACCA!' // 扩展运算符(展开/合并) const arr1 = [1, 2]; const arr2 = [3, 4]; const merged = [0, ...arr1, ...arr2, 5]; console.log(merged); // [0, 1, 2, 3, 4, 5] // 对象展开 const obj1 = { a: 1 }; const obj2 = { b: 2 }; const mergedObj = { ...obj1, ...obj2, c: 3 };

2.5 Set / Map 数据结构

ES6新增的集合类型,解决对象键只能为字符串的问题。

// Set:去重、成员检查 const set = new Set([1, 2, 2, 3]); console.log(set.size); // 3 console.log(set.has(2)); // true // Map:任意类型键 const map = new Map(); const keyObj = { id: 1 }; map.set(keyObj, 'value'); console.log(map.get(keyObj)); // 'value'
💡 Set/Map是可迭代对象,支持for...of遍历。

2.6 Proxy 与 Reflect

Proxy可拦截并自定义对象操作(如属性读写、函数调用)。

const target = { name: '考ACCA', age: 3 }; const handler = { get(obj, prop) { if (!(prop in obj)) { return `属性 ${prop} 不存在`; } return obj[prop]; }, set(obj, prop, value) { if (prop === 'age' && value < 0) { throw new Error('年龄不能为负数'); } obj[prop] = value; return true; } }; const proxy = new Proxy(target, handler); console.log(proxy.age); // 3 console.log(proxy.address); // '属性 address 不存在' proxy.age = -1; // 抛出错误

Reflect提供统一的操作对象API,配合Proxy使用更优雅。

const obj = {}; Reflect.defineProperty(obj, 'name', { value: '考ACCA' }); console.log(obj.name); // '考ACCA'

三、异步编程与事件循环

3.1 回调函数与回调地狱

早期异步方案,通过函数嵌套实现顺序执行,但易导致代码可读性差(“回调地狱”)。

fs.readFile('a.txt', 'utf8', (err, dataA) => { if (err) return console.error(err); fs.readFile('b.txt', 'utf8', (err, dataB) => { if (err) return console.error(err); fs.readFile('c.txt', 'utf8', (err, dataC) => { if (err) return console.error(err); console.log(dataA, dataB, dataC); }); }); });

3.2 Promise

Promise将异步操作表示为状态机(pending → fulfilled / rejected),支持链式调用。

const readFile = (path) => new Promise((resolve, reject) => { fs.readFile(path, 'utf8', (err, data) => { if (err) reject(err); else resolve(data); }); }); readFile('a.txt') .then(dataA => readFile('b.txt').then(dataB => ({ dataA, dataB }))) .then(({ dataA, dataB }) => readFile('c.txt').then(dataC => ({ dataA, dataB, dataC }))) .then(result => console.log(result)) .catch(err => console.error(err));
💡 .then()返回新Promise,实现链式;.catch()捕获整个链路的错误。

3.3 async / await

ES2017引入的语法糖,使异步代码写法接近同步,提升可读性。

const readFileAsync = async (path) => { try { const data = await readFile(path); return data; } catch (err) { throw err; } }; const processFiles = async () => { try { const dataA = await readFileAsync('a.txt'); const dataB = await readFileAsync('b.txt'); const dataC = await readFileAsync('c.txt'); console.log(dataA, dataB, dataC); } catch (err) { console.error(err); } }; processFiles();
⚠️ await必须在async函数内;多个独立异步任务可用Promise.all并行执行。

3.4 事件循环(Event Loop)机制

浏览器通过事件循环处理异步任务,分为:

  • 调用栈(Call Stack):执行同步代码
  • 任务队列(Task Queue):宏任务(macrotask),如setTimeout、setInterval
  • 微任务队列(Microtask Queue):微任务,如Promise.then、queueMicrotask
console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); queueMicrotask(() => console.log(4)); console.log(5);

输出顺序:1 → 5 → 3 → 4 → 2

💡 执行顺序:同步代码 → 微任务队列清空 → 宏任务队列取一个执行

3.5 防抖(Debounce)与节流(Throttle)

防抖:多次触发只执行最后一次(如输入框搜索)

function debounce(fn, delay) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }

节流:多次触发只执行第一次或每隔n秒执行一次(如滚动加载)

function throttle(fn, delay) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= delay) { fn.apply(this, args); lastTime = now; } }; }

四、JavaScript运行机制

4.1 内存管理与垃圾回收

JavaScript自动管理内存,通过垃圾回收(GC)释放无用对象:

  • 标记清除(主流):从根对象出发,遍历所有可达对象,标记存活,清除未标记
  • 引用计数:记录对象被引用次数,为0时回收(易导致循环引用问题)
let obj1 = { x: 1 }; let obj2 = { x: 2 }; obj1.ref = obj2; obj2.ref = obj1; obj1 = null; obj2 = null; // 循环引用,但引用计数GC可能无法回收(现代浏览器已优化)
💡 避免全局变量、未清理的定时器/事件监听,防止内存泄漏。

4.2 执行上下文与执行栈

每次函数调用时创建执行上下文(Execution Context),包含:

  • 变量对象(VO)/活动对象(AO)
  • 作用域链
  • this值

执行栈(Call Stack)管理上下文生命周期:进入函数→创建上下文→压栈;函数返回→出栈。

4.3 模块化(ESM vs CommonJS)

ES Module
CommonJS

静态导入,编译时分析依赖,支持Tree Shaking。

// math.js export const add = (a, b) => a + b; export const multiply = (a, b) => a * b; export default function subtract(a, b) { return a - b; } // main.js import subtract, { add, multiply } from './math.js';

动态导入,运行时加载,Node.js传统方案。

// math.js const add = (a, b) => a + b; const multiply = (a, b) => a * b; module.exports = { add, multiply, subtract: (a, b) => a - b }; // main.js const { add, multiply, subtract } = require('./math.js');

五、进阶拓展与实战技巧

5.1 深浅拷贝

// 浅拷贝 const obj1 = { a: 1, b: { c: 2 } }; const shallowCopy = { ...obj1 }; shallowCopy.b.c = 99; console.log(obj1.b.c); // 99(原对象被修改) // 深拷贝 function deepClone(obj, hash = new WeakMap()) { if (typeof obj !== 'object' || obj === null) return obj; if (hash.has(obj)) return hash.get(obj); const clone = Array.isArray(obj) ? [] : {}; hash.set(obj, clone); for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key], hash); } } return clone; } const deepCopy = deepClone(obj1); deepCopy.b.c = 100; console.log(obj1.b.c); // 99(原对象未修改)
💡 现代方案:structuredClone(obj)(浏览器原生支持)。

5.2 高阶函数与函数式编程

高阶函数:接受函数作为参数或返回函数。

// map / filter / reduce const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(n => n * 2); const evens = numbers.filter(n => n % 2 === 0); const sum = numbers.reduce((acc, n) => acc + n, 0); console.log(doubled, evens, sum); // [2,4,6,8,10] [2,4] 15
💡 函数式编程原则:纯函数(无副作用)、不可变数据、组合函数。

5.3 类型系统:TypeScript 与 JSDoc

TypeScript是JavaScript的超集,添加静态类型,提升代码可维护性。

interface User { id: number; name: string; isActive?: boolean; } const user: User = { id: 1, name: '考ACCA', isActive: true }; function greet(user: User): string { return `Hello, ${user.name}!`; }

JSDoc可在原生JS中添加类型注解(需配合VS Code等工具)。

function add(a, b) { return a + b; }

5.4 性能优化要点

  • 减少重排重绘:批量操作DOM、使用transform代替top/left
  • 虚拟DOM:React/Vue等框架的核心优化
  • 懒加载:图片、组件按需加载
  • Web Worker:将计算密集型任务移至后台线程
  • 内存优化:避免全局变量、及时清理事件监听
💡 工具推荐:Performance面板、Lighthouse、Chrome DevTools。
斌知网
蜀ICP备2026017620号-5