轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

    • 关于this关键字的魔幻现实
    • JavaScript历代版本新特性
    • JavaScript极简入门
    • JavaScript高级特性详解
      • 一、作用域与作用域链
        • 作用域类型
        • 词法作用域
        • 作用域链
        • 变量遮蔽
      • 二、闭包(Closure)
        • 闭包定义
        • 闭包的应用
        • 1. 数据封装和私有变量
        • 2. 模块模式
        • 3. 柯里化(Currying)
        • 4. 延迟执行
        • 闭包陷阱
        • 循环中的闭包
        • 内存泄漏
      • 三、原型与原型链
        • 原型基础
        • 原型链
        • 继承实现
        • 1. 原型链继承
        • 2. ES6 类继承
        • 原型方法
      • 四、执行上下文与调用栈
        • 执行上下文
        • 执行上下文的组成
        • 执行上下文的生命周期
        • 1. 创建阶段
        • 2. 执行阶段
        • 调用栈
        • 栈溢出
      • 五、事件循环(Event Loop)
        • 单线程模型
        • 任务队列
        • 1. 宏任务(Macro Task)
        • 2. 微任务(Micro Task)
        • 事件循环流程
        • async/await 与事件循环
        • Node.js 事件循环
      • 六、内存管理
        • 内存生命周期
        • 垃圾回收机制
        • 1. 引用计数(Reference Counting)
        • 2. 标记-清除(Mark-and-Sweep)
        • 内存泄漏常见场景
        • 1. 全局变量
        • 2. 定时器未清除
        • 3. 事件监听器未移除
        • 4. 闭包引用大对象
        • 5. DOM 引用
        • 内存优化技巧
        • 1. 对象池(Object Pool)
        • 2. 避免大数组操作
        • 3. 使用 WeakMap/WeakSet
      • 七、变量提升(Hoisting)
        • var 提升
        • 函数声明提升
        • let/const 暂时性死区
      • 八、this 绑定进阶
        • 箭头函数与普通函数的 this 差异
        • 显式绑定的优先级
      • 九、模块化进阶
        • ES6 模块的静态特性
        • 模块循环依赖
      • 十、性能优化进阶
        • 惰性求值(Lazy Evaluation)
        • 函数记忆化(Memoization)
        • 尾调用优化(TCO)
      • 十一、总结
  • TypeScript

  • Node.js

  • Vue.js

  • 工程化

  • 浏览器与Web API

  • 前端
  • JavaScript
轩辕李
2025-03-26
目录

JavaScript高级特性详解

本文深入探讨 JavaScript 的高级特性,包括闭包、作用域链、原型链、事件循环、内存管理、执行上下文等核心概念。这些知识是理解 JavaScript 运行机制、编写高质量代码的关键。

# 一、作用域与作用域链

# 作用域类型

JavaScript 有三种作用域:

// 1. 全局作用域
var globalVar = 'global';
const globalConst = 'global const';

function test() {
    // 2. 函数作用域
    var functionVar = 'function';
    
    if (true) {
        // 3. 块级作用域(let/const)
        let blockVar = 'block';
        const blockConst = 'block const';
        var functionVar2 = 'function2'; // var 无块级作用域
    }
    
    console.log(functionVar2); // 'function2'(可访问)
    // console.log(blockVar);  // 错误:blockVar 未定义
}

# 词法作用域

JavaScript 采用词法作用域(静态作用域),函数的作用域在定义时确定,而非调用时:

let name = 'global';

function outer() {
    let name = 'outer';
    
    function inner() {
        console.log(name); // 'outer'(词法作用域)
    }
    
    return inner;
}

function caller() {
    let name = 'caller';
    const fn = outer();
    fn(); // 'outer'(不是 'caller')
}

caller();

# 作用域链

当访问变量时,JavaScript 引擎会沿着作用域链查找:

let a = 'global a';

function outer() {
    let b = 'outer b';
    
    function inner() {
        let c = 'inner c';
        console.log(a); // 沿作用域链向上查找:inner → outer → global
        console.log(b); // 沿作用域链向上查找:inner → outer
        console.log(c); // 当前作用域
    }
    
    inner();
}

outer();

作用域链:内部作用域 → 外部作用域 → 全局作用域

# 变量遮蔽

内层作用域的变量会遮蔽外层同名变量:

let x = 10;

function test() {
    let x = 20; // 遮蔽全局 x
    console.log(x); // 20
}

test();
console.log(x); // 10

# 二、闭包(Closure)

# 闭包定义

闭包是指函数能够访问其词法作用域外的变量,即使该函数在其词法作用域之外执行。

function createCounter() {
    let count = 0; // 私有变量
    
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

// count 无法从外部直接访问
// console.log(count); // 错误:count 未定义

闭包的三个条件:

  1. 函数嵌套(内部函数)
  2. 内部函数引用外部函数的变量
  3. 内部函数被返回或传递到外部执行

# 闭包的应用

# 1. 数据封装和私有变量

function createPerson(name) {
    let age = 0; // 私有变量
    
    return {
        getName() {
            return name;
        },
        getAge() {
            return age;
        },
        setAge(newAge) {
            if (newAge >= 0) {
                age = newAge;
            }
        }
    };
}

const person = createPerson('Alice');
console.log(person.getName()); // 'Alice'
person.setAge(30);
console.log(person.getAge());  // 30
// console.log(person.age);    // undefined(无法直接访问)

# 2. 模块模式

const calculator = (function() {
    let result = 0; // 私有变量
    
    return {
        add(n) {
            result += n;
            return this;
        },
        subtract(n) {
            result -= n;
            return this;
        },
        getResult() {
            return result;
        },
        reset() {
            result = 0;
            return this;
        }
    };
})();

calculator.add(10).add(5).subtract(3);
console.log(calculator.getResult()); // 12

# 3. 柯里化(Currying)

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return function(...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
}

function sum(a, b, c) {
    return a + b + c;
}

const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3));      // 6
console.log(curriedSum(1, 2)(3));      // 6
console.log(curriedSum(1)(2, 3));      // 6

# 4. 延迟执行

function debounce(func, delay) {
    let timeoutId; // 闭包保存 timeoutId
    
    return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}

const handleInput = debounce((value) => {
    console.log('Search:', value);
}, 500);

handleInput('a');
handleInput('ab');
handleInput('abc'); // 只会执行这次

# 闭包陷阱

# 循环中的闭包

// ❌ 错误:所有函数共享同一个 i
for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i); // 5, 5, 5, 5, 5
    }, 100);
}

// ✅ 解决方案 1:使用 let(块级作用域)
for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i); // 0, 1, 2, 3, 4
    }, 100);
}

// ✅ 解决方案 2:IIFE(立即执行函数)
for (var i = 0; i < 5; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j); // 0, 1, 2, 3, 4
        }, 100);
    })(i);
}

# 内存泄漏

function createHeavyObject() {
    const largeData = new Array(1000000).fill('data');
    
    return function() {
        // 即使不使用 largeData,闭包也会持有引用
        console.log('Function executed');
    };
}

const fn = createHeavyObject(); // largeData 无法被垃圾回收

// ✅ 解决方案:手动解除引用
function createHeavyObject() {
    let largeData = new Array(1000000).fill('data');
    
    return function() {
        const result = largeData.length; // 使用 largeData
        largeData = null; // 解除引用
        return result;
    };
}

# 三、原型与原型链

# 原型基础

每个对象都有一个内部属性 [[Prototype]](可通过 __proto__ 访问),指向其原型对象。

function Person(name) {
    this.name = name;
}

Person.prototype.greet = function() {
    console.log(`Hello, I'm ${this.name}`);
};

const alice = new Person('Alice');

// 原型关系
console.log(alice.__proto__ === Person.prototype);        // true
console.log(Person.prototype.constructor === Person);     // true
console.log(alice.__proto__.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__);                  // null

# 原型链

当访问对象属性时,JavaScript 引擎会沿着原型链查找:

const obj = { a: 1 };

// 查找顺序:obj → Object.prototype → null
console.log(obj.toString); // 从 Object.prototype 继承
console.log(obj.a);        // 自身属性
console.log(obj.b);        // undefined(原型链上未找到)

# 继承实现

# 1. 原型链继承

function Animal(name) {
    this.name = name;
}

Animal.prototype.speak = function() {
    console.log(`${this.name} makes a sound`);
};

function Dog(name, breed) {
    Animal.call(this, name); // 继承属性
    this.breed = breed;
}

// 继承方法
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    console.log('Woof!');
};

const dog = new Dog('Rex', 'Labrador');
dog.speak(); // Rex makes a sound
dog.bark();  // Woof!

# 2. ES6 类继承

class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        console.log(`${this.name} makes a sound`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name); // 调用父类构造函数
        this.breed = breed;
    }
    
    bark() {
        console.log('Woof!');
    }
}

const dog = new Dog('Rex', 'Labrador');
dog.speak(); // Rex makes a sound
dog.bark();  // Woof!

# 原型方法

// 检查原型关系
Dog.prototype.isPrototypeOf(dog);        // true
Object.prototype.isPrototypeOf(dog);     // true

// 检查自身属性
dog.hasOwnProperty('name');              // true
dog.hasOwnProperty('speak');             // false

// 获取原型
Object.getPrototypeOf(dog) === Dog.prototype; // true

// 设置原型
const obj = {};
Object.setPrototypeOf(obj, Array.prototype);
obj.push(1); // 现在 obj 可以使用数组方法

# 四、执行上下文与调用栈

# 执行上下文

JavaScript 代码运行在执行上下文中,分为三种:

  1. 全局执行上下文:代码首次执行时创建
  2. 函数执行上下文:每次函数调用时创建
  3. Eval 执行上下文:eval() 函数执行时创建(不推荐使用)

# 执行上下文的组成

每个执行上下文包含三个重要组件:

  1. 变量对象(Variable Object, VO):存储变量、函数声明、参数
  2. 作用域链(Scope Chain):用于变量查找
  3. this 值:当前执行上下文的 this 绑定
function test(a, b) {
    var c = 10;
    function inner() {}
    
    // 执行上下文(简化):
    // VO = {
    //     arguments: { 0: 1, 1: 2, length: 2 },
    //     a: 1,
    //     b: 2,
    //     c: undefined → 10,
    //     inner: function reference
    // }
    // Scope Chain = [VO, Global VO]
    // this = window / undefined(严格模式)
}

test(1, 2);

# 执行上下文的生命周期

# 1. 创建阶段

function test() {
    console.log(a); // undefined(变量提升)
    console.log(b); // 错误:b 未定义(let/const 无提升)
    
    var a = 10;
    let b = 20;
}

// 创建阶段(简化):
// 1. 创建变量对象:
//    - 函数声明:完整提升
//    - var 声明:提升并初始化为 undefined
//    - let/const 声明:提升但不初始化(暂时性死区)
// 2. 建立作用域链
// 3. 确定 this 值

# 2. 执行阶段

function test() {
    var a; // 提升到顶部
    console.log(a); // undefined
    a = 10;
    console.log(a); // 10
}

test();

# 调用栈

JavaScript 使用调用栈(Call Stack) 管理执行上下文:

function first() {
    console.log('First function');
    second();
    console.log('First function end');
}

function second() {
    console.log('Second function');
    third();
    console.log('Second function end');
}

function third() {
    console.log('Third function');
}

first();

// 调用栈变化:
// 1. [Global]
// 2. [Global, first]
// 3. [Global, first, second]
// 4. [Global, first, second, third]
// 5. [Global, first, second]
// 6. [Global, first]
// 7. [Global]

# 栈溢出

调用栈有大小限制,递归过深会导致栈溢出:

function recursion() {
    recursion(); // 无限递归
}

// recursion(); // 错误:Maximum call stack size exceeded

// ✅ 解决方案:尾递归优化(严格模式 + 支持的引擎)
function factorial(n, acc = 1) {
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc); // 尾调用
}

console.log(factorial(5)); // 120

# 五、事件循环(Event Loop)

# 单线程模型

JavaScript 是单线程语言,但通过事件循环实现异步非阻塞:

console.log('Start');

setTimeout(() => {
    console.log('Timeout');
}, 0);

Promise.resolve().then(() => {
    console.log('Promise');
});

console.log('End');

// 输出顺序:
// Start
// End
// Promise
// Timeout

# 任务队列

JavaScript 有两种任务队列:

# 1. 宏任务(Macro Task)

  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O 操作
  • UI 渲染

# 2. 微任务(Micro Task)

  • Promise.then/catch/finally
  • MutationObserver
  • queueMicrotask()
  • process.nextTick()(Node.js,优先级最高)

# 事件循环流程

// 执行顺序:
// 1. 执行同步代码
// 2. 执行所有微任务
// 3. 执行一个宏任务
// 4. 执行所有微任务
// 5. 重复步骤 3-4

console.log('1');

setTimeout(() => {
    console.log('2');
    Promise.resolve().then(() => console.log('3'));
}, 0);

Promise.resolve().then(() => {
    console.log('4');
    setTimeout(() => console.log('5'), 0);
});

Promise.resolve().then(() => console.log('6'));

console.log('7');

// 输出:1 → 7 → 4 → 6 → 2 → 3 → 5

// 详细流程:
// 同步:1, 7
// 微任务队列:[Promise(4), Promise(6)]
//   执行 Promise(4):输出 4,添加 setTimeout(5) 到宏任务队列
//   执行 Promise(6):输出 6
// 宏任务队列:[setTimeout(2), setTimeout(5)]
//   执行 setTimeout(2):输出 2,添加 Promise(3) 到微任务队列
//   微任务队列:[Promise(3)]
//     执行 Promise(3):输出 3
//   执行 setTimeout(5):输出 5

# async/await 与事件循环

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end'); // 相当于 Promise.then()
}

async function async2() {
    console.log('async2');
}

console.log('script start');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

async1();

new Promise(resolve => {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
});

console.log('script end');

// 输出:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

# Node.js 事件循环

Node.js 的事件循环有 6 个阶段:

// Node.js 事件循环阶段:
// 1. timers: 执行 setTimeout/setInterval 回调
// 2. pending callbacks: 执行延迟到下一个循环的 I/O 回调
// 3. idle, prepare: 内部使用
// 4. poll: 获取新的 I/O 事件
// 5. check: 执行 setImmediate 回调
// 6. close callbacks: 执行 close 事件回调

setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));

// 输出顺序不确定(取决于进入事件循环的时机)
// 在 I/O 回调中,setImmediate 总是先于 setTimeout

# 六、内存管理

# 内存生命周期

  1. 分配内存:声明变量、创建对象
  2. 使用内存:读写变量
  3. 释放内存:垃圾回收(Garbage Collection, GC)
// 1. 分配内存
let obj = { name: 'Alice' };
let arr = new Array(1000);

// 2. 使用内存
console.log(obj.name);
arr.push(1);

// 3. 释放内存(自动)
obj = null; // 解除引用,等待 GC
arr = null;

# 垃圾回收机制

# 1. 引用计数(Reference Counting)

// ❌ 循环引用导致内存泄漏
function problem() {
    let obj1 = {};
    let obj2 = {};
    obj1.ref = obj2;
    obj2.ref = obj1; // 循环引用
}

problem(); // obj1 和 obj2 无法被回收(引用计数不为 0)

# 2. 标记-清除(Mark-and-Sweep)

现代浏览器使用标记-清除算法:

  1. 从根对象(全局对象)开始,标记所有可达对象
  2. 清除未标记的对象
function test() {
    let obj1 = {};
    let obj2 = {};
    obj1.ref = obj2;
    obj2.ref = obj1; // 循环引用
}

test(); // obj1 和 obj2 不可达,会被 GC 回收

# 内存泄漏常见场景

# 1. 全局变量

// ❌ 意外创建全局变量
function test() {
    name = 'Alice'; // 未声明,变为全局变量
}

test();
console.log(window.name); // 'Alice'

// ✅ 使用严格模式
'use strict';
function test() {
    name = 'Alice'; // 错误:name is not defined
}

# 2. 定时器未清除

// ❌ 定时器未清除
function startTimer() {
    const largeData = new Array(1000000);
    setInterval(() => {
        console.log(largeData.length); // largeData 无法被回收
    }, 1000);
}

startTimer();

// ✅ 清除定时器
function startTimer() {
    const largeData = new Array(1000000);
    const timerId = setInterval(() => {
        console.log(largeData.length);
    }, 1000);
    
    // 适当时机清除
    setTimeout(() => clearInterval(timerId), 10000);
}

# 3. 事件监听器未移除

// ❌ 事件监听器未移除
function addListener() {
    const element = document.querySelector('#button');
    element.addEventListener('click', function handler() {
        console.log('Clicked');
    });
    
    // element 移除后,handler 仍然存在
    element.remove();
}

// ✅ 移除事件监听器
function addListener() {
    const element = document.querySelector('#button');
    function handler() {
        console.log('Clicked');
    }
    
    element.addEventListener('click', handler);
    
    // 清理
    element.removeEventListener('click', handler);
    element.remove();
}

# 4. 闭包引用大对象

// ❌ 闭包持有大对象引用
function createClosure() {
    const largeData = new Array(1000000).fill('data');
    
    return function() {
        console.log(largeData[0]); // largeData 无法被回收
    };
}

const fn = createClosure();

// ✅ 只保存需要的数据
function createClosure() {
    const largeData = new Array(1000000).fill('data');
    const firstItem = largeData[0]; // 仅保存需要的数据
    
    return function() {
        console.log(firstItem);
    };
}

# 5. DOM 引用

// ❌ DOM 被移除后仍持有引用
const elements = [];

function storeElements() {
    const div = document.querySelector('#myDiv');
    elements.push(div);
    div.remove(); // DOM 已移除,但 elements 中仍有引用
}

// ✅ 及时清理引用
function storeElements() {
    const div = document.querySelector('#myDiv');
    elements.push(div);
    div.remove();
    
    // 清理引用
    const index = elements.indexOf(div);
    elements.splice(index, 1);
}

# 内存优化技巧

# 1. 对象池(Object Pool)

class ObjectPool {
    constructor(createFn, resetFn, initialSize = 10) {
        this.createFn = createFn;
        this.resetFn = resetFn;
        this.pool = [];
        
        for (let i = 0; i < initialSize; i++) {
            this.pool.push(this.createFn());
        }
    }
    
    acquire() {
        return this.pool.length > 0 
            ? this.pool.pop() 
            : this.createFn();
    }
    
    release(obj) {
        this.resetFn(obj);
        this.pool.push(obj);
    }
}

// 使用示例
const bulletPool = new ObjectPool(
    () => ({ x: 0, y: 0, active: false }),
    (bullet) => { bullet.active = false; },
    50
);

const bullet = bulletPool.acquire();
bullet.active = true;
// 使用 bullet...
bulletPool.release(bullet);

# 2. 避免大数组操作

// ❌ 创建大量临时数组
const result = arr
    .filter(x => x > 0)
    .map(x => x * 2)
    .reduce((sum, x) => sum + x, 0);

// ✅ 单次遍历
let result = 0;
for (let x of arr) {
    if (x > 0) {
        result += x * 2;
    }
}

# 3. 使用 WeakMap/WeakSet

// WeakMap:键必须是对象,且弱引用(对象被回收时自动删除)
const cache = new WeakMap();

function process(obj) {
    if (!cache.has(obj)) {
        const result = expensiveOperation(obj);
        cache.set(obj, result);
    }
    return cache.get(obj);
}

// 当 obj 被回收时,cache 中的条目也会被自动删除

# 七、变量提升(Hoisting)

# var 提升

console.log(x); // undefined
var x = 10;
console.log(x); // 10

// 等价于:
var x;
console.log(x); // undefined
x = 10;
console.log(x); // 10

# 函数声明提升

greet(); // 'Hello'(函数声明完全提升)

function greet() {
    console.log('Hello');
}

// 函数表达式不提升
// sayHi(); // 错误:sayHi is not a function
var sayHi = function() {
    console.log('Hi');
};

# let/const 暂时性死区

// console.log(x); // 错误:Cannot access 'x' before initialization
let x = 10;

// 暂时性死区(Temporal Dead Zone, TDZ)
function test() {
    // TDZ 开始
    console.log(a); // 错误
    let a = 10;
    // TDZ 结束
}

# 八、this 绑定进阶

关于 this 的基础内容,详见关于this关键字的魔幻现实。这里补充一些进阶用法。

# 箭头函数与普通函数的 this 差异

const obj = {
    name: 'obj',
    regularFn: function() {
        console.log(this.name);
    },
    arrowFn: () => {
        console.log(this.name);
    }
};

obj.regularFn(); // 'obj'(this 指向 obj)
obj.arrowFn();   // undefined(箭头函数继承外层 this,即 window)

const regular = obj.regularFn;
const arrow = obj.arrowFn;

regular(); // undefined(普通函数 this 丢失)
arrow();   // undefined(箭头函数 this 始终继承外层)

# 显式绑定的优先级

function test() {
    console.log(this.value);
}

const obj1 = { value: 1 };
const obj2 = { value: 2 };

const boundFn = test.bind(obj1);
boundFn();              // 1
boundFn.call(obj2);     // 1(bind 优先级高于 call)

const newInstance = new boundFn(); // undefined(new 优先级最高)

# 九、模块化进阶

# ES6 模块的静态特性

// ❌ 错误:不能在运行时导入
if (condition) {
    import module from './module.js'; // 语法错误
}

// ✅ 动态导入
if (condition) {
    import('./module.js').then(module => {
        module.doSomething();
    });
}

// ✅ async/await
async function loadModule() {
    const module = await import('./module.js');
    module.doSomething();
}

# 模块循环依赖

// a.js
import { b } from './b.js';
export const a = 'a';
console.log(b); // undefined(b.js 尚未执行完)

// b.js
import { a } from './a.js';
export const b = 'b';
console.log(a); // undefined(a.js 尚未执行完)

// ✅ 解决方案:使用函数延迟访问
// a.js
import { getB } from './b.js';
export const a = 'a';
console.log(getB()); // 'b'

// b.js
import { a } from './a.js';
export const b = 'b';
export function getB() {
    return b;
}

# 十、性能优化进阶

# 惰性求值(Lazy Evaluation)

class LazyArray {
    constructor(arr) {
        this.arr = arr;
        this.operations = [];
    }
    
    map(fn) {
        this.operations.push({ type: 'map', fn });
        return this;
    }
    
    filter(fn) {
        this.operations.push({ type: 'filter', fn });
        return this;
    }
    
    value() {
        return this.arr.reduce((acc, item) => {
            let current = item;
            for (let op of this.operations) {
                if (op.type === 'map') {
                    current = op.fn(current);
                } else if (op.type === 'filter') {
                    if (!op.fn(current)) return acc;
                }
            }
            acc.push(current);
            return acc;
        }, []);
    }
}

const result = new LazyArray([1, 2, 3, 4, 5])
    .map(x => x * 2)
    .filter(x => x > 5)
    .map(x => x + 1)
    .value(); // [7, 9, 11](仅遍历一次)

# 函数记忆化(Memoization)

function memoize(fn) {
    const cache = new Map();
    
    return function(...args) {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    };
}

// 斐波那契数列
const fibonacci = memoize(function(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log(fibonacci(40)); // 很快(缓存结果)

# 尾调用优化(TCO)

// ❌ 非尾调用(会导致栈溢出)
function factorial(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1); // 调用后还有乘法操作
}

// ✅ 尾调用优化(严格模式 + 支持的引擎)
function factorial(n, acc = 1) {
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc); // 最后一步是函数调用
}

console.log(factorial(100000)); // 不会栈溢出(如果引擎支持 TCO)

# 十一、总结

JavaScript 的高级特性是深入理解语言运行机制的关键:

  • 作用域与闭包:理解词法作用域、作用域链、闭包的应用与陷阱
  • 原型与继承:掌握原型链、继承模式、ES6 类
  • 执行上下文:理解变量提升、调用栈、执行流程
  • 事件循环:掌握宏任务、微任务、async/await 的执行顺序
  • 内存管理:了解垃圾回收、避免内存泄漏、优化内存使用
  • 性能优化:惰性求值、记忆化、尾调用优化

这些概念环环相扣,理解它们能够帮助你编写更高效、更健壮的 JavaScript 代码。

祝你变得更强!

编辑 (opens new window)
#JavaScript
上次更新: 2025/11/07
JavaScript极简入门
TypeScript极简入门

← JavaScript极简入门 TypeScript极简入门→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code 最佳实践(个人版)
08-01
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式