鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - JavaScript 作用域

解析 JavaScript 中的作用域机制、变量传递方式,以及立即执行函数(IIFE)的应用。掌握块级作用域、函数作用域、经典 for 循环问题与解决方案。

影片縮圖

懒得看文章?那就来看视频吧

作用域是什么?

作用域就像是一个变量的「活动范围」,决定了变量在哪里可以被使用。

三种作用域

  1. 1.
    块级作用域:用 {} 包起来的区域
  2. 2.
    函数作用域:函数内部的区域
  3. 3.
    全局作用域:最外层的区域
// 块级作用域
{
  let x = 1;  // 只能在这个大括号内使用
}
console.log(x); // 错误!x 不存在

// 函数作用域
function test() {
  var y = 2;  // 只能在这个函数内使用
}
console.log(y); // 错误!y 不存在

// 全局作用域
let z = 3;  // 到处都可以使用

变量传递的两种方式

1. 传值(基本类型)

  • 复制一份新的值
  • 修改新变量不会影响原变量

2. 传参考(对象类型)

  • 复制的是「地址」
  • 修改新变量会影响原变量
// 传值
let a = 1;
let b = a;  // 复制一份 1 给 b
b = 2;      // 修改 b 不会影响 a
console.log(a); // 还是 1

// 传参考
let obj1 = { x: 1 };
let obj2 = obj1;  // 复制的是地址
obj2.x = 2;       // 修改 obj2 会影响 obj1
console.log(obj1.x); // 变成 2 了!

IIFE 是什么?

IIFE(立即执行函数)就是一个马上执行的函数,常用来:

  • 保护变量不被外面使用
  • 解决 for 循环的问题
// 基本用法
(function() {
  let secret = '不能说的秘密';
  console.log(secret); // 可以印出
})();
console.log(secret); // 错误!外面看不到

🔥 常见面试题目

(一) let var 差在哪?

解答:主要差异有:

  • let 一定要先声明才能用,var 可以先用后声明 (hoisting提升,用let可以及时发现bug)
  • let 在声明前使用会直接报错,让你知道写错了
  • let 不会变成全局变量(不会挂在 window 上)

(二)经典的 for 循环面试题 - 以下输出为何?

for (var i = 0; i < 3; i++) {
   setTimeout(() => console.log(i), 1000);
}

解答:会是 3, 3, 3

for (var i = 0; i < 3; i++) {
   setTimeout(() => console.log(i), 1000);
}
// 输出:3, 3, 3

为什么会这样?

var 没有块级作用域

setTimeout 执行时,循环已经跑完了

此时 i 的值已经是 3

所以三个 setTimeout 都印出 3

解决方式

1. 使用 IIFE 的解法
for (var i = 0; i < 3; i++) {
   (function(j) {
     setTimeout(() => console.log(j), 1000);
   })(i);
}
// 输出:0, 1, 2
为什么这样可以?

IIFE 创造了一个新的作用域

每次循环都把当下的 i 值传进去

每个 setTimeout 都有自己的 j

所以可以正确印出 0, 1, 2

2. 使用 let 的解法(最推荐)
for (let i = 0; i < 3; i++) {
   setTimeout(() => console.log(i), 1000);
}
// 输出:0, 1, 2
为什么这样可以?

let 有块级作用域

每次循环都会创建一个新的 i

每个 setTimeout 都记住自己那次 i

所以可以正确印出 0, 1, 2

(三)为什么要用 IIFE?

解答:IIFE 创造了一个新的作用域,可以:

  • 保护变量不被外面修改
  • 让代码更安全、更模块化