자바 스크립트에서 변수의 범위는 무엇입니까? 함수 외부와 반대로 내부와 동일한 범위를 가지고 있습니까? 아니면 중요합니까? 또한 전역 적으로 정의 된 변수는 어디에 저장됩니까?
답변
TLDR
JavaScript에는 어휘 (정적이라고도 함) 범위 지정 및 클로저가 있습니다. 즉, 소스 코드를 보면 식별자의 범위를 알 수 있습니다.
네 가지 범위는 다음과 같습니다.
- 글로벌-모든 것으로 표시
- 기능-기능 (및 해당 하위 기능 및 블록) 내에서 표시
- 블록-블록 (및 해당 하위 블록)에 표시
- 모듈-모듈 내에서 표시
전역 및 모듈 범위의 특수한 경우를 제외하고 변수는 var
(함수 범위), let
(블록 범위) 및 const
(블록 범위)를 사용하여 선언됩니다 . 대부분의 다른 형태의 식별자 선언은 엄격 모드에서 블록 범위를 갖습니다.
개요
범위는 식별자가 유효한 코드베이스의 영역입니다.
어휘 환경은 식별자 이름과 관련된 값 사이의 매핑입니다.
범위는 어휘 환경의 연결된 중첩으로 구성되며, 중첩의 각 레벨은 상위 실행 컨텍스트의 어휘 환경에 해당합니다.
이러한 연결된 어휘 환경은 범위 “체인”을 형성합니다. 식별자 확인은이 체인을 따라 일치하는 식별자를 검색하는 프로세스입니다.
식별자 해상도는 한 방향으로 만 발생합니다 : 바깥 쪽. 이러한 방식으로 외부 어휘 환경은 내부 어휘 환경을 “볼”수 없습니다.
JavaScript 에서 식별자 의 범위 를 결정하는 데는 세 가지 관련 요소가 있습니다 .
식별자를 선언 할 수있는 몇 가지 방법 :
var
,let
및const
- 기능 매개 변수
- 캐치 블록 매개 변수
- 함수 선언
- 명명 된 함수 표현식
- 전역 객체에 대해 암시 적으로 정의 된 속성 (예 :
var
엄격하지 않은 모드에서는 누락 ) import
진술eval
위치 식별자 중 일부를 선언 할 수 있습니다.
- 글로벌 컨텍스트
- 기능 바디
- 보통 블록
- 제어 구조의 상단 (예 : 루프, if, while 등)
- 제어 구조체
- 모듈
선언 스타일
var
전역 컨텍스트에서 직접 선언 된 경우를 제외하고 사용하여 선언 된 식별자 에는 전역 범위에서 특성으로 추가되고 전역 var
범위가있는 함수 범위가 있습니다 . eval
기능에 사용하기위한 별도의 규칙이 있습니다.
let과 const
식별자를 사용하여 선언 let
과 const
블록 범위를 가지고 는 그들이 글로벌 범위를 가지고있는 경우에 국제적인 맥락에서 직접 선언 할 때 그렇다.
참고 : let
, const
및 var
모든 게양된다 . 이는 논리적 정의 위치가 둘러싸는 범위 (블록 또는 기능)의 최상위임을 의미합니다. 그러나 변수는 useing 선언 let
및 const
읽거나 제어 소스 코드에 선언의 지점을 통과 할 때까지 할당 할 수 없습니다. 임시 기간은 임시 데드 존이라고합니다.
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
함수 매개 변수 이름
함수 매개 변수 이름은 함수 본문의 범위입니다. 이것에는 약간의 복잡성이 있습니다. 기본 인수로 선언 된 함수는 함수 본문이 아닌 매개 변수 목록 위에 닫힙니다 .
함수 선언
함수 선언은 엄격 모드의 블록 범위와 엄격하지 않은 모드의 함수 범위를 갖습니다. 참고 : 엄격하지 않은 모드는 서로 다른 브라우저의 기발한 역사적 구현을 기반으로하는 복잡한 규칙입니다.
명명 된 함수 표현식
명명 된 함수 표현식은 그 자체로 범위가 지정됩니다 (예 : 재귀 목적으로).
전역 객체에 대해 암시 적으로 정의 된 속성
엄격하지 않은 모드에서는 전역 개체가 범위 체인의 최상위에 위치하기 때문에 전역 개체에 대해 암시 적으로 정의 된 속성에 전역 범위가 있습니다. 엄격 모드에서는 허용되지 않습니다.
평가
에서는 eval
문자열 변수 사용 선언 var
한다면, 현재 범위에 배치된다 또는 eval
간접적으로 사용되는 전역 객체 건물.
예
다음은 이름 때문에 ReferenceError가 발생합니다 x
, y
그리고 z
함수의 의미의 외부가 없습니다 f
.
function f() {
var x = 1
let y = 1
const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)
다음은 의 가시성 이 블록에 의해 제한되지 않기 때문에 y
및 z
에 대해서는 ReferenceError를 발생 시키지 않습니다. 제어 구조의 시체를 정의 블록 좋아 , 와 유사하게 동작합니다.x
x
if
for
while
{
var x = 1
let y = 1
const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope
다음에서는 함수 범위가 x
있으므로 루프 외부에서 볼 수 있습니다 var
.
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
…이 동작으로 인해 var
루프에서 사용하여 선언 된 변수를 닫을 때주의해야합니다 . x
여기에 선언 된 변수 인스턴스는 하나 뿐이며 논리적으로 루프 외부에 있습니다.
다음은 5
을 다섯 번 인쇄 한 다음 루프 외부 5
에 대해 여섯 번째 로 인쇄 console.log
합니다.
for(var x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop
다음 은 블록 범위 undefined
이므로 인쇄 x
됩니다. 콜백은 하나씩 비동기 적으로 실행됩니다. 새로운 행동 let
각 익명 함수라는 이름의 다른 변수에 대해 닫혀 있음을 변수 수단 x
(이 함께 할 것이다 달리 var
) 및 정수 그래서 0
를 통해 4
인쇄 :
for(let x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined
다음은 블록 ReferenceError
의 가시성이 x
제한되지 않기 때문에를 던지지 않습니다. 그러나, 명령문 undefined
때문에 변수가 초기화되지 않았기 때문에 인쇄 if
됩니다.
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
를 for
사용하여 루프 의 맨 위에 선언 된 변수는 루프 let
의 본문으로 범위가 지정됩니다.
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
ReferenceError
가시성은 x
블록에 의해 제한 되기 때문에 다음이 발생 합니다.
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
변수로 선언 var
, let
또는 const
모든 모듈에 범위가 있습니다 :
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
var
전역 컨텍스트 내에서 사용하여 선언 된 변수가 전역 개체에 속성으로 추가되므로 다음은 전역 개체에 속성을 선언 합니다.
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
및 const
글로벌 맥락에서 전역 객체에 속성을 추가 할 수 있지만 여전히 글로벌 범위가 없습니다 :
let x = 1
console.log(window.hasOwnProperty('x')) // false
함수 매개 변수는 함수 본문에서 선언 된 것으로 간주 될 수 있습니다.
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
캐치 블록 매개 변수는 캐치 블록 본체에 적용됩니다.
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
명명 된 함수 표현식은 표현식 자체에만 적용됩니다.
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
엄격하지 않은 모드에서는 전역 개체에 대해 암시 적으로 정의 된 속성의 범위가 전역 적으로 지정됩니다. 엄격 모드에서는 오류가 발생합니다.
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
엄격하지 않은 모드에서 함수 선언에는 함수 범위가 있습니다. 엄격 모드에서는 블록 범위가 있습니다.
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
후드 아래에서 작동하는 방법
범위는 식별자가 유효한 코드 의 어휘 영역으로 정의됩니다 .
JavaScript에서 모든 function-object에는 숨겨진 컨텍스트 가 있으며 실행 컨텍스트 (스택 프레임) [[Environment]]
의 어휘 환경 에 대한 참조 입니다.
함수를 호출하면 숨겨진 [[Call]]
메소드가 호출됩니다. 이 메소드는 새 실행 컨텍스트를 작성하고 새 실행 컨텍스트와 함수 오브젝트의 어휘 환경 사이에 링크를 설정합니다. [[Environment]]
함수 객체 의 값을 새 실행 컨텍스트의 어휘 환경에 있는 외부 참조 필드에 복사하여이를 수행합니다.
새로운 실행 컨텍스트와 함수 객체의 어휘 환경 사이의이 링크를 클로저 라고합니다 .
따라서 JavaScript에서 범위는 외부 참조에 의해 “체인”으로 함께 연결된 어휘 환경을 통해 구현됩니다. 이 어휘 환경 체인을 스코프 체인이라고하며, 식별자 확인 은 일치하는 식별자를 찾기 위해 체인 을 검색하여 발생합니다 .
자세한 내용을 알아보십시오 .
답변
Javascript는 범위 체인을 사용하여 지정된 함수의 범위를 설정합니다. 일반적으로 하나의 전역 범위가 있으며 정의 된 각 함수에는 자체 중첩 범위가 있습니다. 다른 함수 내에 정의 된 모든 함수에는 외부 함수에 연결된 로컬 범위가 있습니다. 항상 범위를 정의하는 소스의 위치입니다.
스코프 체인의 요소는 기본적으로 상위 범위에 대한 포인터가있는 맵입니다.
변수를 해결할 때 javascript는 가장 안쪽 범위에서 시작하여 바깥쪽으로 검색합니다.
답변
전역 적으로 선언 된 변수에는 전역 범위가 있습니다. 함수 내에서 선언 된 변수는 해당 함수로 범위가 지정되고 동일한 이름의 전역 변수를 음영 처리합니다.
(나는 확실히 실제 자바 스크립트 프로그래머는 다른 답변에서 지적 할 수있을 것으로 많은 미묘한이있다하고 있습니다. 특히 내가 건너 온에서 이 페이지 정확히 무엇에 대해 this
언제든지 의미합니다. 희망은 이 더 소개 링크가 당신이 생각 시작할 수 있도록 충분하다 .)
답변
구식 JavaScript
일반적으로 JavaScript는 실제로 두 가지 유형의 범위 만 있습니다.
차이점을 설명하는 다른 많은 답변이 이미 있기 때문에 이에 대해 자세히 설명하지 않습니다.
현대 JavaScript
가장 최근의 자바 스크립트 사양은 이제 세 번째 범위를 허용 :
- 블록 범위 : 식별자는 자신이 선언 한 범위의 상단에서 “알려져” 있지만 선언이 끝날 때까지 식별자를 할당하거나 역 참조 (읽기) 할 수 없습니다. 이 기간을 “임시 데드 존”이라고합니다.
블록 범위 변수를 작성하는 방법
전통적으로 다음과 같이 변수를 만듭니다.
var myVariable = "Some text";
블록 범위 변수는 다음과 같이 생성됩니다.
let myVariable = "Some text";
기능 범위와 블록 범위의 차이점은 무엇입니까?
기능 범위와 블록 범위의 차이점을 이해하려면 다음 코드를 고려하십시오.
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
여기에서 변수 j
는 첫 번째 for 루프에서만 알려져 있지만 이전과 이후에는 알려지지 않았습니다. 그러나 우리의 변수 i
는 전체 기능에 알려져 있습니다.
또한 블록 범위 변수는 게양되지 않았기 때문에 선언되기 전에 알 수 없습니다. 또한 동일한 블록 내에서 동일한 블록 범위 변수를 다시 선언 할 수 없습니다. 이렇게하면 블록 범위 변수가 전역 또는 기능 범위 변수보다 오류가 덜 발생합니다.이 변수는 게양되어 여러 선언의 경우 오류가 발생하지 않습니다.
오늘날 블록 범위 변수를 사용하는 것이 안전합니까?
오늘날 사용하기에 안전한지 여부는 환경에 따라 다릅니다.
-
서버 측 JavaScript 코드 ( Node.js )를
let
작성 하는 경우 명령문을 안전하게 사용할 수 있습니다 . -
클라이언트 측 JavaScript 코드를 작성하고 Traceur 또는 babel-standalone 과 같은 브라우저 기반 변환기를 사용하는 경우이
let
명령문을 안전하게 사용할 수 있지만 코드는 성능 측면에서 최적 일 수 있습니다. -
클라이언트 측 JavaScript 코드를 작성하고 traceur 쉘 스크립트 또는 Babel 과 같은 노드 기반 변환기를 사용하는 경우
let
명령문을 안전하게 사용할 수 있습니다 . 그리고 브라우저는 변환 된 코드에 대해서만 알기 때문에 성능 단점이 제한되어야합니다. -
클라이언트 측 JavaScript 코드를 작성 중이고 트랜스 파일러를 사용하지 않는 경우 브라우저 지원을 고려해야합니다.
이들은 전혀 지원하지 않는 일부 브라우저입니다
let
.- 인터넷 익스플로러 10 이하
- Firefox 43 이하
- 사파리 9 이하
- 안드로이드 브라우저 4 이하
- 오페라 27 이하
- Chome 40 이하
- 모든 버전의 Opera Mini & Blackberry Browser
브라우저 지원을 추적하는 방법
let
이 답변을 읽을 당시의 진술을 지원하는 브라우저에 대한 최신 개요는 이 Can I Use
페이지를 참조 하십시오 .
(*) JavaScript 변수가 들어 있기 때문에 전역 및 기능적으로 범위가 지정된 변수를 선언하기 전에 초기화하고 사용할 수 있습니다 . 이것은 선언이 항상 범위의 맨 위에 있다는 것을 의미합니다.
답변
예를 들면 다음과 같습니다.
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
클로저를 조사하고 클로저를 사용하여 비공개 회원 을 만드는 방법을 조사해야합니다 .
답변
내가 이해하는 열쇠는 Javascript가 기능 수준 범위 지정보다 일반적인 C 블록 범위 지정이라는 것입니다.
답변
“Javascript 1.7″(Mozilla의 Javascript 확장)에서 다음 과 같이 let
statement -scope 변수를 선언 할 수 있습니다 .
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4