this 到底指向谁?严格模式和作用域那些坑全讲明白了

理解运行机制,比掌握语法更重要。

引言

JavaScript 作为前端开发的核心语言,看似简单,实则隐藏着不少运行时的"小机关"。

你可能遇到过这些困惑:

  • varlet 到底差在哪?
  • this 到底指向谁?
  • "use strict" 到底严格在哪?

这篇文章就用 例子 + 原理,带你搞懂 JS 的运行底层逻辑,从变量声明、作用域,到 this 指向和严格模式。


1️⃣ 变量声明:varletconst 真不同

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

使用 letconst 声明的变量,不会挂到全局对象上,而是局部存在于当前块级作用域中。

此外:

  • 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); // 张三

但是这会让全局空间变得非常容易污染。

所以 letconst 就成为更好的选择,它们不会绑定到全局对象:

js 复制代码
let score = 99;
console.log(window.score); // undefined

总结回顾

概念 特性/行为
var 声明 会挂到 window,作用域为函数级
let / const 块级作用域,不挂 window,有 TDZ
this 运行时决定,严格模式下默认是 undefined
严格模式 更安全,限制更多,调试更容易

写在最后

JavaScript 是一门看起来随和,实则套路很多的语言。this 指向、作用域规则、严格模式这些东西不难,但很容易忽略。

它们藏在一些看似"正常"的代码背后,很多 bug 其实都是对这些机制理解不到位导致的。

希望这篇文章能帮你把一些模糊地带捋清楚。真正理解它们,写 JS 才会越来越稳。


如果你觉得有帮助,欢迎点赞、收藏或分享这篇文章!

相关推荐
前端风云志3 分钟前
TypeScript实用类型之Omit
javascript
烛阴15 分钟前
Puppeteer入门指南:掌控浏览器,开启自动化新时代
前端·javascript
全宝1 小时前
🖲️一行代码实现鼠标换肤
前端·css·html
小小小小宇1 小时前
前端模拟一个setTimeout
前端
萌萌哒草头将军1 小时前
🚀🚀🚀 不要只知道 Vite 了,可以看看 Farm ,Rust 编写的快速且一致的打包工具
前端·vue.js·react.js
芝士加2 小时前
Playwright vs MidScene:自动化工具“双雄”谁更适合你?
前端·javascript
Carlos_sam3 小时前
OpenLayers:封装一个自定义罗盘控件
前端·javascript
前端南玖4 小时前
深入Vue3响应式:手写实现reactive与ref
前端·javascript·vue.js
wordbaby4 小时前
React Router 双重加载器机制:服务端 loader 与客户端 clientLoader 完整解析
前端·react.js
itslife4 小时前
Fiber 架构
前端·react.js