Skip to content

变量、作用域与内存

原始值与引用值

1. 原始值

原始值是最基本的数据,包括:undefined、null、boolean、number、string、symbol 和 bigint。

javascript
// 原始值示例
let str = "hello";          // string
let num = 100;             // number
let bool = true;           // boolean
let n = null;              // null
let u = undefined;         // undefined
let sym = Symbol();        // symbol
let bigInt = 9007199254740991n; // bigint

// 原始值的特点
let name1 = "John";
let name2 = name1;  // 复制值
name1 = "Jane";     // name2仍然是"John"

2. 引用值

引用值是由多个值构成的对象,保存在内存中。

javascript
// 引用值示例
let obj1 = { name: "John" };
let obj2 = obj1;           // 复制引用
obj1.name = "Jane";        // obj2.name 也变成 "Jane"

// 常见的引用类型
let array = [1, 2, 3];     // Array
let date = new Date();     // Date
let regexp = /\d+/g;       // RegExp
let error = new Error();   // Error

执行上下文与作用域

1. 执行上下文类型

javascript
// 全局执行上下文
var globalVar = "全局变量";

// 函数执行上下文
function exampleFunction() {
    var localVar = "局部变量";
    console.log(localVar);  // 可访问
    console.log(globalVar); // 可访问全局变量
}

// eval执行上下文(不推荐使用)
eval("var evalVar = 42;");

2. 作用域链

javascript
let global = "全局变量";

function outer() {
    let outerVar = "外层变量";
    
    function inner() {
        let innerVar = "内层变量";
        // 作用域链查找顺序:inner -> outer -> global
        console.log(innerVar);  // 内层变量
        console.log(outerVar);  // 外层变量
        console.log(global);    // 全局变量
    }
    
    inner();
}

3. 块级作用域

javascript
// let 和 const 的块级作用域
{
    let blockVar = "块级变量";
    const BLOCK_CONST = "块级常量";
    
    console.log(blockVar);     // 可访问
    console.log(BLOCK_CONST);  // 可访问
}
// console.log(blockVar);      // ReferenceError
// console.log(BLOCK_CONST);   // ReferenceError

// if 块级作用域
if (true) {
    let ifVar = "if内部变量";
}
// console.log(ifVar);         // ReferenceError

// for 循环块级作用域
for (let i = 0; i < 3; i++) {
    let forVar = "循环内变量";
}
// console.log(i);             // ReferenceError
// console.log(forVar);        // ReferenceError

变量声明

1. var 声明

javascript
// 变量提升
console.log(varVariable);   // undefined
var varVariable = "var变量";

// 函数作用域
function varScope() {
    var functionVar = "函数作用域";
}
// console.log(functionVar); // ReferenceError

// 可重复声明
var repeat = "first";
var repeat = "second";     // 允许

2. let 声明

javascript
// 不存在变量提升
// console.log(letVariable); // ReferenceError
let letVariable = "let变量";

// 块级作用域
{
    let blockLet = "块级作用域";
}
// console.log(blockLet);    // ReferenceError

// 不可重复声明
let unique = "first";
// let unique = "second";    // SyntaxError

3. const 声明

javascript
// 声明时必须初始化
const CONSTANT = "常量";
// const EMPTY;             // SyntaxError

// 不能修改绑定
const PI = 3.14159;
// PI = 3.14;              // TypeError

// 对象属性可以修改
const user = { name: "John" };
user.name = "Jane";        // 允许
// user = {};              // TypeError

内存管理

1. 内存生命周期

javascript
// 1. 内存分配
let person = {             // 分配内存
    name: "John",
    age: 30
};

// 2. 内存使用
console.log(person.name);  // 使用内存

// 3. 内存释放
person = null;            // 解除引用

2. 垃圾回收机制

javascript
// 标记清除算法
function process() {
    let local = {};       // 分配内存
    // 函数结束后,local不可访问,将被回收
}

// 引用计数算法(循环引用问题)
function createCycle() {
    let obj1 = {};
    let obj2 = {};
    
    obj1.ref = obj2;     // obj1 引用 obj2
    obj2.ref = obj1;     // obj2 引用 obj1
    
    obj1 = null;         // 断开一个引用
    obj2 = null;         // 断开另一个引用
}

3. 内存泄漏

javascript
// 1. 意外的全局变量
function leak() {
    leakedVar = "我会泄漏";  // 没有声明就使用
}

// 2. 闭包导致的泄漏
function createLeak() {
    let largeData = new Array(1000000);
    
    return function() {
        console.log(largeData[0]);  // largeData 一直被引用
    };
}

// 3. 定时器导致的泄漏
function setTimer() {
    let data = { /* 大量数据 */ };
    setInterval(() => {
        console.log(data);  // data 无法被回收
    }, 1000);
}

性能优化

1. 变量优化

javascript
// 使用解构赋值
const { name, age } = person;

// 使用对象属性简写
const x = 1, y = 2;
const point = { x, y };

// 使用展开运算符
const newArray = [...oldArray];
const newObject = { ...oldObject };

2. 内存优化

javascript
// 对象池模式
const objectPool = {
    _pool: [],
    acquire() {
        return this._pool.pop() || {};
    },
    release(obj) {
        this._pool.push(obj);
    }
};

// 使用 WeakMap/WeakSet
const cache = new WeakMap();
let key = { id: 1 };
cache.set(key, "数据");
key = null;  // 缓存的数据可以被回收

// 分批处理大数据
function processLargeData(items) {
    const chunk = 1000;
    const process = (start) => {
        const end = Math.min(start + chunk, items.length);
        // 处理 items.slice(start, end)
        if (end < items.length) {
            setTimeout(() => process(end), 0);
        }
    };
    process(0);
}

最佳实践

  1. 优先使用 const,其次是 let,避免使用 var
  2. 及时释放不再使用的引用
  3. 避免意外的全局变量
  4. 注意闭包可能导致的内存问题
  5. 使用块级作用域限制变量的生命周期
  6. 对大数据集合进行分批处理
  7. 使用弱引用(WeakMap/WeakSet)存储可能被清理的对象

注意事项

  • 理解原始值和引用值的区别
  • 注意变量声明的作用域
  • 警惕内存泄漏的常见原因
  • 合理使用垃圾回收机制

练习题

  1. 说明原始值和引用值的区别,并举例说明。
  2. 解释变量提升的概念,以及为什么要避免使用 var。
  3. 描述闭包可能导致的内存问题,并给出解决方案。
  4. 实现一个对象池模式,用于优化频繁创建和销毁对象的场景。

扩展阅读

本站内容采用 "署名-非商业性使用-禁止演绎 4.0 国际许可协议" 进行许可