>  기사  >  웹 프론트엔드  >  JS 범위에 관한 5가지 함정(요약)

JS 범위에 관한 5가지 함정(요약)

青灯夜游
青灯夜游앞으로
2020-12-25 17:32:065127검색

JS 범위에 관한 5가지 함정(요약)

JavaScript에서는 코드, 함수 또는 모듈 블록이 변수의 범위를 만듭니다. 예를 들어, if 코드 블록은 message 변수에 대한 범위를 생성합니다. if 代码块为变量 message 创建作用域:

if (true) {
  const message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError

if 代码块作用域内可以访问 message。但是在作用域之外,该变量不可访问。

好的,这是作用域的简短介绍。如果你想了解更多信息,建议阅读我的文章用简单的词解释 JavaScript 作用域

以下是 5 种有趣的情况,其中 JavaScript 作用域的行为与你预期的不同。你可能会研究这些案例以提高对作用域的了解,或者只是为面试做准备。

1. for 循环内的 var 变量

思考以下代码片段:

const colors = ['red', 'blue', 'white'];

for (let i = 0, var l = colors.length; i < l; i++) {
  console.log(colors[i]); // &#39;red&#39;, &#39;blue&#39;, &#39;white&#39;
}
console.log(l); // ???
console.log(i); // ???

当你打印 li 变量时会发生什么?

答案

console.log(l) 输出数字 3 ,而 console.log(i) 则抛出 ReferenceError

l 变量是使用 var 语句声明的。你可能已经知道,var 变量仅受函数体作用域限制而并非代码块。

相反,变量 i 使用 let 语句声明。因为 let 变量是块作用域的,所以 i 仅在 for 循环作用域内才可访问。

修复

l 声明从 var l = colors.length 改为 const l = colors.length。现在变量 l 被封装在 for 循环体内。

2. 代码块中的函数声明

在以下代码段中:

// ES2015 env
{
  function hello() {
    return &#39;Hello!&#39;;
  }
}

hello(); // ???

调用 hello() 会怎样? (代码段在 ES2015 环境中执行)

答案

因为代码块为函数声明创建了作用域,所以在 ES2015 环境中调用 hello() 会引发  ReferenceError: hello is not defined

有趣的是,在 ES2015 之前的环境中,在执行上述代码段时不会抛出错误。 你知道为什么吗?请在下面的评论中写下你的答案!

3. 你可以在哪里导入模块?

你可以在代码块中导入模块吗?

if (true) {
  import { myFunc } from &#39;myModule&#39;; // ???
  myFunc();
}

答案

上面的脚本将触发错误: 'import' and 'export' may only appear at the top-level

你只能在模块文件的最顶级作用域(也称为模块作用域)中导入模块。

修复

始终从模块作用域导入模块。另外一个好的做法是将 import 语句放在源文件的开头:

import { myFunc } from &#39;myModule&#39;;

if (true) {
  myFunc();
}

ES2015 的模块系统是静态的。通过分析 JavaScript 源代码而不是执行代码来确定模块的依赖关系。所以在代码块或函数中不能包含 import 语句,因为它们是在运行时执行的。

4. 函数参数作用域

思考以下函数:

let p = 1;

function myFunc(p = p + 1) {
  return p;
}

myFunc(); // ???

调用 myFunc() 会发生什么?

答案

当调用函数 myFunc() 时,将会引发错误: ReferenceError: Cannot access 'p' before initialization

发生这种情况是因为函数的参数具有自己的作用域(与函数作用域分开)。参数 p = p + 1 等效于 let p = p + 1

让我们仔细看看 p = p + 1

首先,定义变量 p。然后 JavaScript 尝试评估默认值表达式 p + 1,但此时绑定  p  已经创建但尚未初始化(不能访问外部作用域的变量  let p = 1)。因此抛出一个错误,即在初始化之前访问了 p

修复

为了解决这个问题,你可以重命名变量 let p = 1 ,也可以重命名功能参数 p = p + 1

让我们选择重命名函数参数:

let p = 1;

function myFunc(q = p + 1) {
  return q;
}

myFunc(); // => 2

函数参数从 p 重命名为 q。当调用 myFunc() 时,未指定参数,因此将参数 q 初始化为默认值 p + 1。为了评估 p +1,访问外部作用域的变量 pp +1 = 1 + 1 = 2

if (true) {
  function greet() {
    // function body
  }

  class Greeter {
    // class body
  }
}

greet();       // ???
new Greeter(); // ???

message 범위 내에서 액세스할 수 있습니다. if 코드 블록 >. 하지만 범위 밖에서는 변수에 액세스할 수 없습니다. 🎜🎜알겠습니다. 스코프에 대한 간략한 소개입니다. 더 알고 싶다면 JavaScript의 역할을 간단한 단어로 설명하는 기사를 읽어 보시기 바랍니다. 도메인. 🎜🎜다음은 JavaScript 범위가 예상과 다르게 동작하는 5가지 흥미로운 상황입니다. 범위에 대한 이해를 향상시키거나 단순히 인터뷰를 준비하기 위해 이러한 사례를 연구할 수 있습니다. 🎜

1. for 루프의 var 변수

🎜 다음 코드 조각: 🎜rrreee🎜 li 변수를 인쇄하면 어떻게 되나요? 🎜

답변

🎜console.log(l)3 숫자를 출력하는 반면 console.log(i )ReferenceError를 발생시킵니다. 🎜🎜l 변수는 var 문을 사용하여 선언됩니다. 이미 알고 계시겠지만 var 변수 코드 블록이 아닌 함수 본문의 범위에 의해서만 제한됩니다. 🎜🎜대신 let 문을 사용하여 변수 i가 선언됩니다. let 변수는 블록 범위이므로 ifor 루프 범위 내에서만 액세스할 수 있습니다. 🎜

수정됨

🎜 l 선언이 var l = colors.length에서 const l = colors 로 변경되었습니다. 길이. 이제 변수 lfor 루프 본문에 포함됩니다. 🎜

2. 코드 블록의 함수 선언

🎜다음 코드 부분에서: 🎜rrreee🎜hello() 호출 / 코드> 어떻게 되나요? <em>(코드 조각은 ES2015 환경에서 실행됩니다.)</em>🎜<h4><strong>답변</strong></h4>🎜코드 블록이 함수 선언에 대한 범위를 생성하기 때문에 호출됩니다. ES2015 환경에서 <code>hello()ReferenceError: hello가 정의되지 않았습니다를 발생시킵니다. 🎜🎜흥미롭게도 ES2015 이전 환경에서는 위 코드 조각을 실행할 때 오류가 발생하지 않습니다. 이유를 아시나요? 아래 댓글에 답을 적어주세요! 🎜

3. 모듈은 어디에서 가져올 수 있나요?

🎜코드 블록에서 모듈을 가져올 수 있나요? 🎜rrreee

답변

🎜위 스크립트는 오류를 발생시킵니다: '가져오기' 및 '내보내기'는 최상위 수준에만 나타날 수 있습니다. 🎜🎜이 작업은 모듈 파일의 최상위 범위에서만 수행할 수 있습니다(모듈 범위). 🎜

수정

🎜항상 모듈 범위에서 모듈을 가져오세요. 또 다른 좋은 방법은 import 문을 소스 파일 시작 부분에 배치하는 것입니다. 🎜rrreee🎜ES2015의 모듈 시스템은 정적입니다. 코드를 실행하는 대신 JavaScript 소스 코드를 분석하여 모듈 종속성을 확인합니다. 따라서 import 문은 런타임에 실행되기 때문에 코드 블록이나 함수에 포함할 수 없습니다. 🎜

4. 함수 매개변수 범위

🎜다음 함수를 생각해 보세요. 🎜rrreee🎜myFunc() 호출 무슨 일이 일어났나요? 🎜

답변

🎜 myFunc() 함수를 호출하면 오류가 발생합니다: ReferenceError: 초기화 전에 'p'에 액세스할 수 없습니다. . 🎜🎜이는 함수의 매개변수가 자체 범위(함수 범위와 별개)를 갖기 때문에 발생합니다. <code>p = p + 1 매개변수는 let p = p + 1과 동일합니다. 🎜🎜 p = p + 1을 자세히 살펴보겠습니다. 🎜🎜먼저 p 변수를 정의합니다. 그런 다음 JavaScript는 기본 값 표현식 p + 1을 평가하려고 시도하지만 이 시점에서 바인딩 p가 생성되었지만 아직 초기화되지 않았습니다(외부 범위 p = 1로 설정). 따라서 초기화 전에 p에 액세스했다는 오류가 발생합니다. 🎜

수정

🎜이 문제를 해결하려면 변수 let p = 1의 이름을 바꾸거나 함수 매개변수 p의 이름을 바꿀 수 있습니다. = p + 1. 🎜🎜함수 매개변수의 이름을 바꾸도록 선택하겠습니다. 🎜rrreee🎜함수 매개변수의 이름이 p에서 q로 바뀌었습니다. myFunc()가 호출되면 매개변수가 지정되지 않으므로 q 매개변수가 기본값 p + 1로 초기화됩니다. p +1을 평가하려면 외부 범위의 p 변수(p +1 = 1 + 1 = 2)에 액세스하세요. 🎜

5. 函数声明与类声明

以下代码在代码块内定义了一个函数和一个类:

if (true) {
  function greet() {
    // function body
  }

  class Greeter {
    // class body
  }
}

greet();       // ???
new Greeter(); // ???

是否可以在块作用域之外访问 greetGreeter(考虑 ES2015 环境)

答案

functionclass 声明都是块作用域的。所以在代码块作用域外调用函数 greet() 和构造函数 new Greeter() 就会抛出 ReferenceError

6. 总结

必须注意 var 变量,因为它们是函数作用域的,即使是在代码块中定义的。

由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import 语法(以及 export)。

函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。

在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。

希望这些陷阱能够帮你巩固作用域知识!

英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/

作者:Dmitri Pavlutin

更多编程相关知识,请访问:编程入门!!

위 내용은 JS 범위에 관한 5가지 함정(요약)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제