理解运行机制,比掌握语法更重要。
引言
JavaScript 作为前端开发的核心语言,看似简单,实则隐藏着不少运行时的"小机关"。
你可能遇到过这些困惑:
var
和let
到底差在哪?this
到底指向谁?"use strict"
到底严格在哪?
这篇文章就用 例子 + 原理,带你搞懂 JS 的运行底层逻辑,从变量声明、作用域,到 this 指向和严格模式。
1️⃣ 变量声明:var
、let
和 const
真不同
var
:老派选手,默认上全局
js
var a = 1;
console.log(window.a); // 1
在浏览器环境中,var
声明的变量会挂在 window
对象上,相当于 window.a = 1
。
这就意味着,所有页面代码都可以访问和修改这个变量,容易产生污染或冲突。
let
/ const
:ES6 新时代的安全声明
js
let b = 2;
console.log(window.b); // undefined
使用 let
和 const
声明的变量,不会挂到全局对象上,而是局部存在于当前块级作用域中。
此外:
let
支持重新赋值,const
不支持;- 它们都有"暂时性死区"(TDZ):在声明前访问会报错。
2️⃣ 严格模式:让 JS 不再"睁一只眼闭一只眼"
启用方式:
js
"use strict";
在严格模式下:
- 禁止未声明变量直接赋值;
- 禁止删除变量名;
this
在普通函数中不再默认指向window
,而是undefined
;- 命名函数表达式的名字是只读的;
看看这个例子👇:
js
"use strict";
(function b() {
var b = 20;
console.log(b); // 20,而不是函数名
})();
你可能听说「函数名在严格模式下是只读的」,于是以为变量无法覆盖。但实际上 var b = 20
是合法的,它创建了一个新的局部变量,屏蔽了函数名变量 b
,所以输出的是 20
。
真正只读限制的例子如下:
js
"use strict";
(function b() {
b = 123; // ❌ TypeError:不能给函数名 b 重新赋值
})();
3️⃣ this:不是写在哪,而是怎么调用
this
的值,总是函数执行时决定的,而不是写代码的位置。
来看几个经典场景:
✅ 作为对象方法调用:
js
var obj = {
name: '娄老板',
say() {
console.log(this.name);
}
};
obj.say(); // 输出:娄老板
❌ 普通函数调用:
js
var fn = obj.say;
fn(); // 非严格模式下输出:window.name(如果有);严格模式下为 undefined
✅ 构造函数中,this 指向实例对象:
js
function Person(name) {
this.name = name;
}
const p = new Person('labula');
console.log(p.name); // labula
⚠️ 如果忘记 new
:
js
const p2 = Person('小明');
console.log(window.name); // 被污染了!this 指向 window
4️⃣ window 与全局变量:曾经的设计,现在的"坑"
早期 JS 设计时,所有全局变量都挂在 window
上,是为了方便浏览器访问。
这也让下面的行为成立:
js
var user = '张三';
console.log(window.user); // 张三
但是这会让全局空间变得非常容易污染。
所以 let
和 const
就成为更好的选择,它们不会绑定到全局对象:
js
let score = 99;
console.log(window.score); // undefined
总结回顾
概念 | 特性/行为 |
---|---|
var 声明 |
会挂到 window ,作用域为函数级 |
let / const |
块级作用域,不挂 window,有 TDZ |
this |
运行时决定,严格模式下默认是 undefined |
严格模式 | 更安全,限制更多,调试更容易 |
写在最后
JavaScript 是一门看起来随和,实则套路很多的语言。this
指向、作用域规则、严格模式这些东西不难,但很容易忽略。
它们藏在一些看似"正常"的代码背后,很多 bug 其实都是对这些机制理解不到位导致的。
希望这篇文章能帮你把一些模糊地带捋清楚。真正理解它们,写 JS 才会越来越稳。
如果你觉得有帮助,欢迎点赞、收藏或分享这篇文章!