• 19:20 #JavaScript/运行时 编译

    运行时和编译时确定的特性

    确认那些事情是在编译的时候就定好了的 那些事情是在调用的时候在确定的

    例如this是在调用的时候在能知道


    在 JavaScript 中,编译阶段(解析阶段)和执行阶段的处理机制有本质区别。以下是编译时确定和运行时确定的典型场景对比:


    一、编译时确定的特性(代码解析阶段处理)

    1. 词法作用域(Lexical Scope)

    function outer() {
      let x = 10;
      function inner() {
        console.log(x); // 作用域链在编译时确定
      }
      inner();
    }
    • 作用域链的嵌套关系在代码解析时已确定
    • 变量的查找路径在代码书写阶段固化

    2. 变量声明提升(Hoisting)

    console.log(a); // undefined (变量提升)
    var a = 5;
     
    foo(); // "Hello" (函数声明提升)
    function foo() {
      console.log("Hello");
    }
    • var 和函数声明在编译阶段被提升
    • let/const 的 TDZ(暂时性死区)机制也在编译时处理

    3. 函数声明绑定

    function createFunc() {
      return function inner() { /* ... */ }; // inner函数的作用域在编译时确定
    }

    4. 语法错误检测

    function broken() {
      var x = ; // 编译阶段直接报语法错误(SyntaxError)
    }

    二、运行时确定的特性(代码执行阶段处理)

    1. this 绑定

    const obj = {
      name: "Alice",
      sayName() {
        console.log(this.name); // this 在调用时确定
      }
    };
     
    const func = obj.sayName;
    func(); // 此时 this 指向全局(非严格模式)
    • this 的值由调用方式决定(直接调用、方法调用、new 调用等)
    • 箭头函数的 this 在编译时捕获外层 this(属于例外)

    2. 闭包变量值

    function createCounter() {
      let count = 0; // 闭包变量
      return function() {
        return ++count; // count 的值在运行时确定
      };
    }
     
    const counter = createCounter();
    console.log(counter()); // 1(运行时访问闭包变量)

    3. 动态属性访问

    const obj = { x: 1, y: 2 };
    const key = "x";
    console.log(obj[key]); // 运行时解析属性名

    4. 原型链查找

    function Person(name) {
      this.name = name;
    }
    Person.prototype.sayHello = function() {
      console.log("Hello, " + this.name);
    };
     
    const bob = new Person("Bob");
    bob.sayHello(); // 运行时沿着原型链查找方法

    5. 动态类型转换

    function add(a, b) {
      return a + b; // 类型转换在运行时发生
    }
     
    console.log(add(1, "2")); // "12"(运行时决定类型)

    三、特殊边界场景

    1. evalwith(破坏静态作用域)

    function dynamicScope() {
      const x = 10;
      eval('var y = 20'); // 在运行时修改作用域
      console.log(y); // 20(破坏词法作用域)
    }
    • 导致引擎无法在编译时优化

    2. ES6 模块的静态解析

    // 编译时确定模块依赖关系
    import { foo } from './module.js';
    export const bar = foo * 2;

    四、核心对比表格

    特性编译时确定运行时确定
    作用域链
    变量提升✅ (var/函数声明)
    this 绑定❌(箭头函数除外)
    闭包变量值
    动态属性访问
    原型链查找
    类型转换
    模块依赖关系✅(ES6模块)

    五、实战验证方法

    1. 查看作用域链(编译时特性):

    function outer() {
      const x = 10;
      function inner() {
        debugger; // 在浏览器调试器查看闭包作用域
      }
      inner();
    }
    outer();

    2. 验证 this 动态性(运行时特性):

    const obj1 = { name: "Alice", log() { console.log(this.name) } };
    const obj2 = { name: "Bob" };
     
    obj1.log(); // "Alice"
    obj1.log.call(obj2); // "Bob"(运行时改变this)

    理解这些机制的关键点:JavaScript 是词法作用域(静态作用域)语言,但执行上下文(包括 this)是动态绑定的。这种设计使得既有可预测的作用域结构,又能灵活处理运行时状态。