-
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.
eval
和with
(破坏静态作用域)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
)是动态绑定的。这种设计使得既有可预测的作用域结构,又能灵活处理运行时状态。