作用域与变量提升

35 阅读3分钟

进来刷题!

题目 1: 变量提升与暂时性死区

console.log(a);
let a = 5;
var b = 10;
console.log(b);

问题:这段代码的输出是什么?为什么?

题目 2: 块级作用域陷阱

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

问题:两个循环的输出分别是什么?解释差异原因。

题目 3: 函数作用域链

var x = 10;
function outer() {
  console.log(x);
  var x = 20;
  function inner() {
    console.log(x);
  }
  inner();
}
outer();

问题:这段代码的输出顺序是什么?解释每个输出值的来源。

题目 4: const 的意外行为

const obj = { a: 1 };
obj.a = 2;
obj.b = 3;
console.log(obj);

const arr = [1];
arr.push(2);
console.log(arr);

问题:这段代码会报错吗?输出结果是什么?解释 const 的这种行为。

题目 5: 闭包与循环

function createFunctions() {
  var result = [];
  for (var i = 0; i < 3; i++) {
    result[i] = function() {
      return i;
    };
  }
  return result;
}
var funcs = createFunctions();
console.log(funcs[0](), funcs[1](), funcs[2]());

问题:输出结果是什么?如何修改才能输出 0,1,2?

题目 6: 全局变量污染

function foo() {
  a = 1;
  var b = 2;
}
foo();
console.log(a);
console.log(b);

问题:这段代码的输出是什么?为什么?

题目 7: 箭头函数的作用域

var name = 'global';
const obj = {
  name: 'object',
  say: () => {
    console.log(this.name);
  }
};
obj.say();

问题:输出结果是什么?解释箭头函数的 this 绑定规则。

题目 8: 变量遮蔽

let x = 1;
{
  let x = 2;
  console.log(x);
}
console.log(x);

问题:输出结果是什么?解释 let 的块级作用域特性。

题目 9: 函数声明 vs 函数表达式

foo();
bar();
function foo() {
  console.log('foo');
}
var bar = function() {
  console.log('bar');
};

问题:这段代码的执行结果是什么?解释函数声明和函数表达式的区别。

题目 10: 动态作用域陷阱

var a = 1;
function outer() {
  var a = 2;
  inner();
}
function inner() {
  console.log(a);
}
outer();

问题:输出结果是什么?JavaScript 是静态作用域还是动态作用域?

  1. 报错,10
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);  // 输出3次3
}
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100);  // 输出0,1,2
}

解析var 声明的 i 是函数级作用域,循环结束后值为3,所有回调都引用同一个 i, let 声明的 j 是块级作用域,每次循环都创建新的 j,回调捕获各自的 j 值, 这是经典的循环+闭包问题,let 解决了 var 的作用域缺陷

var x = 10;
function outer() {
  console.log(x);  // undefined
  var x = 20;
  function inner() {
    console.log(x);  // 20
  }
  inner();
}
outer();
//1.  第一个 `console.log` 输出 `undefined`:由于函数内 `var x` 提升,但未赋值
1.  `inner()` 输出 `20`:沿作用域链找到最近的 `x`(outer函数内的 `x`1.  全局 `x=10` 被遮蔽,未被使用
  1. {a:2,b:3},[1,2].解释:是引用不可变,不是值不可变 5.3,3,3
//**解决方案**:
//1.  **使用IIFE创建闭包**:
    for (var i = 0; i < 3; i++) {
      (function(i) {
        result[i] = function() { return i; };
      })(i);
    }

//1.  **改用let**(最佳方案):
    for (let i = 0; i < 3; i++) {
      result[i] = function() { return i; };
    }
function foo() {
  a = 1;    // 意外创建全局变量
  var b = 2; // 局部变量
}
foo();
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined

//解析:
//1.  未声明直接赋值的变量(`a=1`)会成为全局变量(严格模式下会报错)
//1.  `var b` 是函数局部变量,外部不可访问
  1. global
1.  箭头函数的 `this` 绑定定义时的上下文(此处是全局作用域)
1.  普通函数的 `this` 指向调用对象(若用 `function(){}` 会输出 'object')
箭头函数this绑定规则 **继承自定义时的外围作用域**(词法作用域)
 **在定义时就已经确定**,且**永远不会改变**
  1. 2,1
foo();  // 'foo'
bar();  // TypeError: bar is not a function
function foo() {
 console.log('foo');
}
var bar = function() {
 console.log('bar');
};
1.  函数声明(`function foo`)会整体提升,可在声明前调用
1.  函数表达式(`var bar = function`)只有变量声明提升,赋值不提升
  1. 1
 JavaScript 采用**静态作用域**(词法作用域)(即:函数的作用域在函数定义时确定,与函数在哪里调用无关(动态作用域则与调用位置相关))
`inner` 的 `a` 在定义时绑定到全局 `a`,而非调用位置的 `a`
 动态作用域语言会输出2,但JavaScript不是动态作用域