Javascript 作用域与执行机制

作用域本质

  • Javascript 的作用域的本质就是词法环境(静态作用域),作用域在代码编写阶段确定的,而非执行阶段确定,这就决定了变量的查找规则是找定义时候的上层作用域,而非执行时的上层作用域

  • 底层执行模型、上下文执行栈(ECS)和变量对象(VO/AO)

    • 执行上下文:JS 运行时候的环境单元,分为全局执行上下文(GEC)和函数执行上下文(FEC),ECS 负责的是管理执行上下文的EC入栈和出栈的任务吧

    • 变量对象(VO): GEC 中存储的是全局变量和函数,FEC 中就是活动对象(AO),存储函数参数,局部变量、函数声明吧

    • 作用域链:当前的EC 的 VO 所有的上层 EC 的 VO 组成的,变量的查找规则就是沿着这个作用域链向上,一个一个的进行查找实现讷,直到到全局作用域为止吧

闭包

  • 闭包的本质是函数执行后,其之哟用于没有被销毁,内部函数任能访问外层函数的变量

  • 这也是作用域链持久化的一种体现吧

import { useState, useEffcet } from "react";

const Counter = () => {
    const [count, setCount] = useState(0);

    useEffcet(() => {
        const timer = setInterval(() => {
            setCount(count + 1);
        }, 1000)
        return () => {
            clearInterval(timer)
        }
    }, [count])

    return <div>{count}</div>
}

this 的绑定

  • JS 中的 this 绑定是运行时决定的讷(除了函数),绑定的规则优先级是:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

事件循环

  • JS是一个单线程的语言,核心是为了避免多线程下的操作DOM出现的冲突问题,异步能力完全依赖于事件循环来实现吧,浏览器和Nodejs 的事件循环机制存在很大的差异化,底层分别是用浏览器和libuv 库进行底层支持的讷

浏览器的事件循环

  • 核心认为宏任务和微任务,以及宏任务队列和微任务队列的区分吧

  • 执行的时候,先实现的是我们的没有在事件循环中的代码执行完把,这个就是同步代码把

  • 然后检查是否含有我们的微任务队列,检查内部是否具备具体的task 需要执行,有的话就去执行该任务,队列特性是先进先出

  • 然后就是进行对应的浏览器的渲染绘制把

  • 最后就是执行宏任务,最后回归到主线程进行执行同步代码,依次按照上诉流程进行执行实现吧

  • 微任务:优先级高,包括Promise.then/catch/finallyasync/awaitqueueMicrotaskMutationObserver

  • 宏任务:优先级低,包括setTimeout/setIntervalUI事件网络请求script标签

性能优化:利用微任务减少渲染阻塞

  • 微任务在执行前进行渲染,避免微任务长导致渲染的卡顿出现吧

// 错误:长耗时操作放在微任务,阻塞渲染
Promise.resolve().then(() => {
  let sum = 0;
  for (let i = 0; i < 1e8; i++) sum += i; // 长耗时计算
  console.log(sum);
});

// 优化:拆分微任务,利用requestIdleCallback
const calculate = async () => {
  let sum = 0;
  const chunk = 1e6;
  let i = 0;
  
  const processChunk = () => {
    return new Promise(resolve => {
      // 利用浏览器空闲时间执行
      requestIdleCallback((deadline) => {
        while (i < 1e8 && deadline.timeRemaining() > 0) {
          sum += i;
          i += chunk;
        }
        resolve();
      });
    });
  };
  
  while (i < 1e8) {
    await processChunk();
  }
  console.log(sum);
};
calculate();
  • 核心的优化就是:

    • 1. 让复杂的计算逻辑在主线程空闲的时候执行,不影响主线程的执行,也就是使用 requestIdleCallback 来使用吧

    • 2. 让复杂的计算在子线程中执行,就是使用我们的 webworker api 来实现吧

Nodejs 事件循环

  • Nodejs 的事件循环和python 的 asyncio 的特性都是基于我们的 libuv 库来实现吧,核心是分为了六个阶段吧

timer 定时器阶段 --> pending callbacks 延迟回调阶段 --> idle/prepare 内部阶段 --> poll 轮询阶段 --> check(setImmediate) 阶段 --》 close callbacks 关闭回调阶段
// process.nextTick不属于事件循环阶段,优先级高于所有微任务
console.log('同步代码');

setTimeout(() => {
  console.log('timers阶段'); // 第二阶段
}, 0);

setImmediate(() => {
  console.log('check阶段'); // 第五阶段
});

process.nextTick(() => {
  console.log('process.nextTick'); // 同步代码后立即执行,优先级最高
});

Promise.resolve().then(() => {
  console.log('微任务Promise'); // nextTick后,事件循环前执行
});

// 输出顺序(Node.js环境):
// 同步代码 → process.nextTick → 微任务Promise → timers阶段 → check阶段

性能优化

  • Nodejs 的 poll 阶段依赖于底层的 libuv 的线程池,默认是4个,此时为了处理 IO DNS 操作的时候,密集型任务需要调用线程池的大小

// 在Node.js入口文件顶部设置(必须先于其他代码)
process.env.UV_THREADPOOL_SIZE = '8'; // 调整为8个线程

// 密集型文件处理案例
import fs from 'fs/promises';

async function batchReadFiles(files: string[]) {
  // 并行读取,线程池扩容后效率提升
  const promises = files.map(file => fs.readFile(file, 'utf8'));
  const results = await Promise.all(promises);
  return results;
}

异步编程理解

  • 异步编程本质:单线程 + 事件循环

    • JS 单线程决定了 “耗时操作必须异步”,异步的底层是:耗时操作交由浏览器 / Node.js 内核处理,完成后将回调放入任务队列,事件循环轮询执行

Promise 原理

// TS版Promise A+核心实现
type PromiseState = 'pending' | 'fulfilled' | 'rejected';
type Resolve<T> = (value: T | PromiseLike<T>) => void;
type Reject = (reason?: any) => void;
type Executor<T> = (resolve: Resolve<T>, reject: Reject) => void;
type OnFulfilled<T, R> = (value: T) => R | PromiseLike<R>;
type OnRejected<R> = (reason: any) => R | PromiseLike<R>;

class MyPromise<T> {
  private state: PromiseState = 'pending';
  private value: T | undefined;
  private reason: any;
  private onFulfilledCallbacks: Array<OnFulfilled<T, any>> = [];
  private onRejectedCallbacks: Array<OnRejected<any>> = [];

  constructor(executor: Executor<T>) {
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e);
    }
  }

  private resolve(value: T | PromiseLike<T>) {
    if (this.state !== 'pending') return;
    // 处理Promise嵌套
    if (value instanceof MyPromise) {
      value.then(this.resolve.bind(this), this.reject.bind(this));
      return;
    }
    this.state = 'fulfilled';
    this.value = value;
    // 执行成功回调队列
    this.onFulfilledCallbacks.forEach(cb => cb(this.value!));
  }

  private reject(reason?: any) {
    if (this.state !== 'pending') return;
    this.state = 'rejected';
    this.reason = reason;
    // 执行失败回调队列
    this.onRejectedCallbacks.forEach(cb => cb(this.reason));
  }

  then<R1 = T, R2 = never>(
    onFulfilled?: OnFulfilled<T, R1>,
    onRejected?: OnRejected<R2>
  ): MyPromise<R1 | R2> {
    onFulfilled = onFulfilled || ((v) => v as R1);
    onRejected = onRejected || ((r) => { throw r; });

    return new MyPromise((resolve, reject) => {
      // 状态已完成,异步执行回调(符合A+规范)
      if (this.state === 'fulfilled') {
        queueMicrotask(() => {
          try {
            const result = onFulfilled!(this.value!);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });
      }

      if (this.state === 'rejected') {
        queueMicrotask(() => {
          try {
            const result = onRejected!(this.reason);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });
      }

      // 状态pending,存入回调队列
      if (this.state === 'pending') {
        this.onFulfilledCallbacks.push((value) => {
          try {
            const result = onFulfilled!(value);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push((reason) => {
          try {
            const result = onRejected!(reason);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
  }

  catch<R = never>(onRejected?: OnRejected<R>): MyPromise<T | R> {
    return this.then(undefined, onRejected);
  }

  finally(onFinally?: () => void): MyPromise<T> {
    return this.then(
      (value) => {
        onFinally?.();
        return value;
      },
      (reason) => {
        onFinally?.();
        throw reason;
      }
    );
  }
}

// 测试案例
new MyPromise<number>((resolve) => {
  setTimeout(() => resolve(10), 100);
})
  .then(v => v * 2)
  .then(v => console.log(v)) // 输出20
  .catch(e => console.error(e));

Async/Await

  • 本质就是Generator + 自动执行器的语法糖吧

// TS案例:手动实现async/await的自动执行器
function myAsync<T>(generator: Generator): Promise<T> {
  return new Promise((resolve, reject) => {
    const next = (result: any) => {
      let res;
      try {
        res = generator.next(result);
      } catch (e) {
        return reject(e);
      }
      if (res.done) {
        return resolve(res.value);
      }
      // 将yield后的Promise转为thenable
      Promise.resolve(res.value).then(next, (err) => generator.throw(err));
    };
    next(undefined);
  });
}

// 使用自定义执行器
function* gen() {
  const res1 = yield fetch('https://api.example.com/data1');
  const data1 = yield res1.json();
  const res2 = yield fetch(`https://api.example.com/data2?id=${data1.id}`);
  const data2 = yield res2.json();
  return data2;
}

// 等价于async/await
myAsync(gen()).then(data => console.log(data)).catch(e => console.error(e));

// 原生async/await版本(语法糖)
async function fetchData() {
  const res1 = await fetch('https://api.example.com/data1');
  const data1 = await res1.json();
  const res2 = await fetch(`https://api.example.com/data2?id=${data1.id}`);
  const data2 = await res2.json();
  return data2;
}

前端工程化

模块化规范

打包工具

webpack

// webpack.config.ts(TS版配置)
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import CompressionPlugin from 'compression-webpack-plugin';

export default {
  entry: './src/index.tsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js', // 内容哈希,强缓存
    clean: true,
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
    alias: {
      // 路径别名,减少解析耗时
      '@': path.resolve(__dirname, 'src'),
    },
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  optimization: {
    minimizer: [
      new TerserPlugin({
        // 压缩优化:删除注释、简化变量名
        terserOptions: {
          compress: { drop_console: process.env.NODE_ENV === 'production' },
        },
      }),
    ],
    splitChunks: {
      // 代码分割:抽离公共模块、第三方库
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
    runtimeChunk: 'single', // 抽离运行时代码,避免缓存失效
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      minify: process.env.NODE_ENV === 'production',
    }),
    new CompressionPlugin({
      // 生成gzip压缩包,减少传输体积
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
    }),
  ],
  mode: process.env.NODE_ENV || 'development',
  devtool: process.env.NODE_ENV === 'production' ? 'source-map' : 'inline-source-map',
};

vite

  • 原生 esmodule + 按需编译支持

  • 按需打包,性能更强

// vite.config.ts(React+TS配置)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'; // SWC替代Babel,编译更快
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
  build: {
    rollupOptions: {
      // 生产环境代码分割
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        },
      },
    },
    minify: 'esbuild', // ESBuild压缩,比Terser快
  },
  server: {
    open: true,
    port: 3000,
  },
});

包管理工具

浏览器核心原理:渲染+v8+多进程架构

浏览器渲染原理

  • 核心需要优化的点是

    • 减少回流(reflow)和重绘(repaint)

      • 回流:布局变化,代价高(如修改 width/height/position);

      • 重绘:样式变化(不影响布局),代价低(如修改 color/background);

      • 合成:仅修改图层位置,代价最低(如 transform/opacity)

import { useState, useRef, useEffect } from 'react';

// 错误:频繁修改width导致回流
const BadComponent = () => {
  const [width, setWidth] = useState(100);
  useEffect(() => {
    const timer = setInterval(() => {
      setWidth(w => w + 1); // 每次修改触发回流
    }, 16);
    return () => clearInterval(timer);
  }, []);
  return <div style={{ width: `${width}px`, height: '100px' }}>Bad</div>;
};

// 优化:使用transform(仅触发合成)
const GoodComponent = () => {
  const [scale, setScale] = useState(1);
  const ref = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    const timer = setInterval(() => {
      setScale(s => s + 0.01); // transform仅触发合成
    }, 16);
    return () => clearInterval(timer);
  }, []);
  
  return (
    <div
      ref={ref}
      style={{
        width: '100px',
        height: '100px',
        transform: `scale(${scale})`,
        willChange: 'transform', // 告诉浏览器提前优化
      }}
    >
      Good
    </div>
  );
};

V8 引擎

  • V8 引擎是浏览器和nodejs 的核心吧

解析器(Ignition) -->  字节码 --> 编译器(TurboFan) --》 机器码
  • Ignition:生成字节码(比机器码小,跨平台),解释执行;

  • TurboFan:对热点代码(频繁执行)编译为机器码,提升性能;

  • 垃圾回收(GC):分代回收(新生代 Scavenge 算法,老生代 Mark-Sweep/Mark-Compact)

// 错误:每次循环创建新对象
function badLoop() {
  const arr = [];
  for (let i = 0; i < 1e6; i++) {
    arr.push({ value: i }); // 临时对象频繁创建
  }
}

// 优化:复用对象
function goodLoop() {
  const arr = [];
  const temp = { value: 0 }; // 复用对象
  for (let i = 0; i < 1e6; i++) {
    temp.value = i;
    arr.push(temp);
  }
}

浏览器过进程和多线程结构

  • 浏览器主进程:负责窗口管理、进程调度;

  • 渲染进程:每个标签页一个,沙箱隔离,包含 JS 线程 / 渲染线程(互斥);

  • GPU 进程:负责 3D 渲染、合成图层;

  • 进程间通信(IPC):所有进程通过主进程通信,保证安全

react concurrent mode 关联

React 的 Concurrent Mode(并发模式)利用浏览器的 “时间切片”,将长任务拆分为小任务,在 JS 线程空闲时执行,避免阻塞渲染线程

import { createRoot } from 'react-dom/client';
import App from './App';

// 启用Concurrent Mode(React 18+)
const root = createRoot(document.getElementById('root')!);
root.render(<App />);

// 组件中使用useDeferredValue延迟渲染
import { useState, useDeferredValue } from 'react';

const SearchComponent = () => {
  const [query, setQuery] = useState('');
  // 延迟更新query,优先保证输入响应
  const deferredQuery = useDeferredValue(query, { timeoutMs: 200 });
  
  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="搜索..."
      />
      {/* 耗时的搜索结果渲染,使用延迟值 */}
      <SearchResults query={deferredQuery} />
    </div>
  );
};

Nodejs 底层扩展

libuv

  • 是 nodejs 的跨平台异步 IO 库

    • 事件循环实现;

    • 线程池(处理文件 I/O、DNS、加密等);

    • 跨平台 API(统一 Windows/Linux/macOS 的 I/O 接口)

利用 libuv 处理密集型任务

// Node.js+TS案例:调整线程池处理图片压缩
import sharp from 'sharp'; // 底层使用libuv线程池
import fs from 'fs/promises';

// 提前设置线程池大小
process.env.UV_THREADPOOL_SIZE = '8';

async function batchCompressImages(inputDir: string, outputDir: string) {
  const files = await fs.readdir(inputDir);
  const imageFiles = files.filter(file => /\.(jpg|png)$/.test(file));
  
  // 并行压缩,线程池扩容后效率提升
  const promises = imageFiles.map(async (file) => {
    const inputPath = `${inputDir}/${file}`;
    const outputPath = `${outputDir}/${file}`;
    // sharp底层使用libuv线程池,非JS主线程
    await sharp(inputPath).resize(800, 600).toFile(outputPath);
  });
  
  await Promise.all(promises);
  console.log('压缩完成');
}

Native Addons

Native Addons 是用 C++/Rust 编写的 Node.js 扩展,用于处理 JS 不擅长的 “计算密集型任务”(如加密、图形处理)

CPP 编写 Nodejs 扩展(N-API)

// addon.cc(C++扩展)
#include <napi.h>
#include <cmath>

// 计算密集型任务:质数判断
bool isPrime(int n) {
  if (n <= 1) return false;
  if (n == 2) return true;
  if (n % 2 == 0) return false;
  for (int i = 3; i <= sqrt(n); i += 2) {
    if (n % i == 0) return false;
  }
  return true;
}

// N-API包装函数
Napi::Boolean IsPrime(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  if (info.Length() < 1 || !info[0].IsNumber()) {
    Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
    return Napi::Boolean::New(env, false);
  }
  int n = info[0].As<Napi::Number>().Int32Value();
  return Napi::Boolean::New(env, isPrime(n));
}

// 初始化扩展
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "isPrime"), Napi::Function::New(env, IsPrime));
  return exports;
}

NODE_API_MODULE(addon, Init)
// index.ts(Node.js调用C++扩展)
import { readFileSync } from 'fs';
import { join } from 'path';
import { load } from 'node-gyp-build';

// 加载编译后的扩展
const addon = load(join(__dirname, '.'));

// 测试:JS vs C++性能对比
function jsIsPrime(n: number): boolean {
  if (n <= 1) return false;
  if (n === 2) return true;
  if (n % 2 === 0) return false;
  for (let i = 3; i <= Math.sqrt(n); i += 2) {
    if (n % i === 0) return false;
  }
  return true;
}

// 测试1000003(大质数)
const n = 1000003;

console.time('JS');
console.log(jsIsPrime(n)); // true
console.timeEnd('JS'); // ~10ms

console.time('C++');
console.log(addon.isPrime(n)); // true
console.timeEnd('C++'); // ~0.1ms(性能提升100倍)

Rust 编写Nodejs 扩展(Neon)

// src/lib.rs(Rust扩展)
use neon::prelude::*;

// 质数判断
fn is_prime(n: i32) -> bool {
    if n <= 1 {
        return false;
    }
    if n == 2 {
        return true;
    }
    if n % 2 == 0 {
        return false;
    }
    (3..=((n as f64).sqrt() as i32))
        .step_by(2)
        .all(|i| n % i != 0)
}

// Neon包装函数
fn is_prime_js(mut cx: FunctionContext) -> JsResult<JsBoolean> {
    let n = cx.argument::<JsNumber>(0)?.value(&mut cx) as i32;
    Ok(cx.boolean(is_prime(n)))
}

// 初始化扩展
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("isPrime", is_prime_js)?;
    Ok(())
}
// index.ts(Node.js调用Rust扩展)
import { isPrime } from './native/index';

// 性能测试(Rust vs C++接近,内存更安全)
const n = 1000003;
console.time('Rust');
console.log(isPrime(n)); // true
console.timeEnd('Rust'); // ~0.1ms

wasm 跨语言执行

Wasm 是二进制指令集,可将 C++/Rust 编译为 Wasm,在浏览器 / Node.js 中执行,性能接近原生

// src/lib.rs(Rust编写Wasm)
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn calculate_fib(n: u32) -> u64 {
    if n <= 1 {
        return n as u64;
    }
    let mut a = 0;
    let mut b = 1;
    for _ in 2..=n {
        let c = a + b;
        a = b;
        b = c;
    }
    b
}
// fib.wasm.d.ts(TS类型声明)
declare module './fib.wasm' {
  export function calculate_fib(n: number): number;
  export function default(): {
    calculate_fib: (n: number) => number;
  };
}
// FibComponent.tsx(React+TS调用Wasm)
import { useState, useEffect } from 'react';
import init, { calculate_fib } from './fib.wasm';

const FibComponent = () => {
  const [result, setResult] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // 初始化Wasm
    init().then(() => console.log('Wasm loaded'));
  }, []);

  const handleCalculate = (n: number) => {
    setLoading(true);
    // Wasm执行斐波那契(比JS快10倍+)
    const start = performance.now();
    const res = calculate_fib(n);
    const end = performance.now();
    setResult(res);
    setLoading(false);
    console.log(`耗时:${end - start}ms`);
  };

  return (
    <div>
      <button onClick={() => handleCalculate(100000)} disabled={loading}>
        计算斐波那契第100000项
      </button>
      {result && <div>结果:{result}</div>}
    </div>
  );
};

export default FibComponent;

JS 深度原理

原型和原型链

JS 的继承基于原型链,React 类组件的extends React.Component本质是原型链继承

// 简化版React.Component
class Component {
  props: any;
  state: any;
  constructor(props: any) {
    this.props = props;
    this.state = {};
  }
  setState(partialState: any) {
    this.state = { ...this.state, ...partialState };
    // 触发重新渲染(简化版)
    this.render();
  }
  render() {}
}

// 自定义组件继承Component(原型链)
class MyComponent extends Component {
  constructor(props: any) {
    super(props);
    this.state = { count: 0 };
  }
  render() {
    return <div>{this.state.count}</div>;
  }
}

// 原型链验证
console.log(MyComponent.prototype.__proto__ === Component.prototype); // true

React Fiber 架构

  • 事件循环深度结合起来的讷

  • React Fiber 是为了解决 “长任务阻塞渲染” 的问题,核心是

    • 将虚拟 DOM 的 diff 过程拆分为 “小任务”;

    • 利用requestIdleCallback在浏览器空闲时执行任务;

    • 支持任务暂停 / 恢复,优先处理高优先级任务(如用户输入)

// 简化版Fiber调度
type Fiber = {
  type: string;
  props: any;
  child: Fiber | null;
  sibling: Fiber | null;
  alternate: Fiber | null;
};

function scheduleWork(fiber: Fiber) {
  // 利用requestIdleCallback调度任务
  requestIdleCallback((deadline) => {
    workLoop(deadline, fiber);
  });
}

function workLoop(deadline: IdleDeadline, fiber: Fiber | null) {
  let shouldYield = false;
  while (fiber && !shouldYield) {
    // 处理当前Fiber节点
    fiber = performUnitOfWork(fiber);
    // 检查是否需要暂停(浏览器需要渲染)
    shouldYield = deadline.timeRemaining() < 1;
  }
  // 未完成,继续调度
  if (fiber) {
    scheduleWork(fiber);
  }
}

function performUnitOfWork(fiber: Fiber): Fiber | null {
  // 简化版:创建子Fiber
  if (fiber.type === 'div') {
    fiber.child = { type: 'span', props: {}, child: null, sibling: null, alternate: null };
  }
  // 返回下一个任务(子节点优先,然后兄弟节点)
  return fiber.child || fiber.sibling;
}