범위 지정
Javascript 초보자에게 가장 혼란스러운 것 중 하나는 사실 범위입니다. 나는 숙련된 JavaScript 프로그래머들을 만났지만 그들은 범위를 깊이 이해하지 못했습니다. JavaScript 범위가 혼란스러운 이유는 프로그램 구문 자체가 C 계열 언어와 비슷하기 때문입니다. 범위에 대한 나의 이해는 특정 범위에만 영향을 미치고 외부 세계에는 영향을 미치지 않는 닫힌 공간입니다. 이러한 공간 중 일부에서는 내부 변수는 외부에서 액세스할 수 없지만 외부 변수는 내부에서 액세스할 수 있습니다.
C 언어의 변수는 전역 변수와 지역 변수로 구분됩니다. 전역 변수의 범위는 모든 파일 및 함수에서 액세스할 수 있습니다(물론 변수가 아닌 정의가 있는 다른 C 파일의 경우 extern 키워드 선언, 사용 static 키워드는 범위를 현재 파일로 제한할 수도 있으며 지역 변수의 범위는 선언부터 가장 가까운 중괄호까지의 블록 수준 범위입니다. Java에는 전역 변수가 없지만 클래스 변수, 멤버 변수 및 로컬 변수가 있습니다. 범위의 범위는 공개, 보호, 개인 및 기타 액세스 권한에 따라 다릅니다. 여기서는 자세히 설명하지 않겠습니다.
JS의 범위는 어떻게 되나요?
ES5에서 js에는 전역 범위와 함수 범위라는 두 가지 형태의 범위만 있습니다.
전역 범위는 실제로 전역 개체의 범위이며, 어디에서나 액세스할 수 있습니다(함수 범위에 포함되지 않는 경우).
함수 개체 범위는 c의 지역 변수 범위와 다릅니다. 함수 내 어디에서 선언되었는지 여부에 관계없이 범위는 전체 함수 범위입니다! 이를 호이스팅(hoisting)이라고 하는데, 이는 가변 호이스팅(variable hoisting)의 개념이다. 하지만 걱정하지 마세요. 아래에서 호이스팅에 대해 구체적으로 설명하겠습니다.
그러나 ES6에는 새로운 블록 수준 범위(가장 가까운 중괄호로 둘러싸인 범위)가 있지만, 이는 let 메소드에 선언된 변수로 제한됩니다.
Scope 데모:
변수를 정의할 때 i=0과 같이 var를 쓰지 않으면 다음과 같이 정의됩니다. 전역 변수. 범위는 전역 범위이고, 그렇지 않으면 지역 변수이고 범위는 함수 범위입니다. 위 첫 번째 줄의 var i=0은 전역 영역에 선언되었기 때문에 함수 범위에 포함되지 않으므로 전역 변수라고 하므로 i=0과 같습니다.
왜 결과가 이런지는 읽어보시면 아실 겁니다.
선언형
변수 선언:
함수 선언:
변수 호이스팅(Hoisting)
질문 남기기
다음 코드는 무엇을 출력할까요?
이 질문에 대해 많은 분들과 인터뷰를 했는데, 대부분 출력이 날짜라고 하더군요. 그러나 실제 결과는 정의되지 않았습니다. 왜 이런가요? 호이스팅(hoisting)이란 중국어로 가변 호이스팅(variable hoisting)을 뜻하는 개념이다. MDN의 변수 호이스팅에 대한 설명은 다음과 같습니다.
var hoisting
변수 선언(및 일반적인 선언)은 코드가 실행되기 전에 처리되기 때문에 코드 내 어디에서나 변수를 선언하는 것은 이는 변수가 선언되기 전에 사용되는 것처럼 보일 수 있음을 의미합니다. 변수 선언이 함수 또는 전역 코드의 맨 위로 이동되는 것처럼 보이기 때문입니다.
이 구절을 번역하면
변수 선언은 코드가 실행되기 전에 처리되기 때문에 변수는 코드 영역과 시작 부분(상단) 어디에서나 선언됩니다. 동일합니다. 즉, 변수가 선언되기 전에 사용할 수 있는 것처럼 보입니다! 이러한 행위를 "호이스팅(hoisting)", 즉 변수 호이스팅(variable hoisting)이라고 하는데, 변수의 선언이 자동으로 함수나 전역 코드의 맨 위로 이동되는 것처럼 보입니다.
참고: 선언만 업그레이드되고 정의는 업그레이드되지 않습니다.
이렇게 하면 위의 코드는 실제로는 다음과 같은 형태가 됩니다.
그래서 콘솔에서 tmp를 출력할 때, 변수는 선언만 되었을 뿐 정의되지 않았으므로 출력은 정의되지 않아야 합니다.
여기서 모든 선언(ES5의 var, function 및 ES6의 함수 *, let, const, class 포함)이 승격되지만 var, function, function * 및 let, const, class는 개선되지 않는다는 점에 유의해야 합니다. 똑같다! 구체적인 이유는 여기에서 확인할 수 있습니다. (일반적인 의미는 let, const, class도 승격되지만 초기화되지 않는다는 것입니다. 이때 액세스하면 ReferenceError 예외가 보고됩니다. 때까지 기다려야 합니다. 명령문이 실행되며 초기화 전 상태를 임시 데드존(Temporal Dead Zone)이라고 합니다. 알아보기 위해 코드 조각을 살펴보겠습니다.
여기서 a는 승격되지만 나중에 정의되므로 undefound가 출력됩니다.
a는 여기에서 홍보했는데 인용 오류가 보고되었습니다!
이유는
이런 이유로 변수 도메인 선언 시에는 모든 변수를 스코프(전역 스코프나 함수 스코프)에 쓰는 것이 좋습니다. 코드가 더 명확해 보이고 어떤 변수가 함수 범위에서 왔는지, 어떤 변수가 범위 체인에서 왔는지 확인하기가 더 쉬울 것입니다(이 기사에서는 이에 대해 더 자세히 설명하지 않으므로 Baidu에서 직접 읽어보시기 바랍니다. 기회가 생기면 더 자세한 내용을 추가하세요).
문장을 반복하세요
위 출력은 실제로 1 2 2입니다. 위에서 언급했듯이 x가 두 번 선언된 것처럼 보이지만 js에는 전역 범위와 함수 범위라는 두 가지 유형의 var 변수만 있고 선언이 승격되므로 실제로 x는 맨 위에만 선언됩니다. , var x=2 선언은 무시되고 할당에만 사용됩니다. 즉, 위의 코드는 실제로 다음과 일치합니다.
함수와 변수의 동시 승격 문제
함수형과 변수형을 동시에 선언하면 어떻게 될까요? 아래 코드를 보세요
위 출력은 실제로 함수 내용인 function foo(){}입니다.
이러한 형식이라면 어떨까요?
출력은 다음과 같습니다: 정의되지 않음
왜일까요?
함수 승격은 두 가지 상황으로 나뉘는 것으로 나타났습니다.
하나: 함수 선언. 위의 A는
형식의 function foo(){}입니다. 또 다른: 함수 표현식입니다. 위의 B는
형식의 var foo=function(){}입니다. 두 번째 형식은 실제로 var 변수의 선언 정의이므로 위의 B의 출력 결과는 다음과 같이 이해되어야 합니다. 정의되지 않았습니다.
함수 정의 부분을 포함하여 함수 선언의 첫 번째 형태는 프로모션 중에 완전히 업그레이드됩니다! 따라서 A는 다음 방법과 동일합니다!
이유는 다음과 같습니다. 1. 함수 선언이 맨 위로 승격됩니다. 2. 선언은 한 번만 수행되므로 후속 선언은 var foo='i am입니다. 텍스트'는 무시됩니다.
그리고 함수 선언이 변수 선언보다 우선하므로 다음 형식의 출력도 함수 내용입니다.
요약
JS의 범위와 호이스팅을 완전히 이해하려면 다음 세 가지 사항을 기억하세요.
1. 모든 선언은 범위의 최상위로 승격됩니다.
2. 동일한 변수 선언은 한 번만 이루어지므로 다른 선언은 무시됩니다
3. 함수 선언은 변수 선언보다 우선하며 함수 선언은 정의와 함께 승격됩니다
참고:
with 문을 사용하면 런타임 컨텍스트의 범위 체인을 임시로 변경할 수 있습니다. 이때 var가 정의되지 않은 변수에 액세스하면 with에 있는 개체의 속성에 먼저 액세스한 다음 이동합니다. 범위를 확장하세요. 체인에서 이 속성을 확인하세요.