다음 프로그램의 결과는 무엇입니까?
다음과 같이 코드 코드를 복사합니다.
var foo = 1;
함수 바() {
만약 (!foo) {
var foo = 10;
}
경고(foo);
}
술집();
결과는 10입니다.
이건 어때요?
다음과 같이 코드 코드를 복사합니다.
var a = 1;
함수 b() {
a = 10;
반품;
함수 a() {}
}
비();
경고(a);
결과는 1입니다.
겁이 나나요? 무슨 일이에요? 이는 이상하고 위험하며 혼란스러울 수 있지만 실제로는 매우 유용하고 인상적인 JavaScript 언어 기능이기도 합니다. 이 동작에 대한 표준 이름이 있는지는 모르겠지만 저는 "호이스팅"이라는 용어를 좋아합니다. 이 기사에서는 이 메커니즘에 대한 소개적인 설명을 제공하지만 먼저 JavaScript의 범위에 대해 필요한 몇 가지 이해를 해보겠습니다.
자바스크립트의 범위
Javascript 초보자에게 가장 혼란스러운 영역 중 하나는 실제로 범위입니다. 이는 단지 초보자만이 아닙니다. 나는 숙련된 JavaScript 프로그래머들을 만났지만 그들은 범위를 깊이 이해하지 못했습니다. JavaScript 범위가 혼란스러운 이유는 프로그램 구문 자체가 다음 C 프로그램과 같이 C 계열 언어처럼 보이기 때문입니다.
다음과 같이 코드 코드를 복사합니다.
#include <stdio.h>
정수 메인() {
정수 x = 1;
printf("%d, ", x); // 1
만약 (1) {
정수 x = 2;
printf("%d, ", x); // 2
}
printf("%d/n", x); // 1
}
출력 결과는 1 2 1 입니다. C 계열 언어에는 블록 범위가 있기 때문입니다. if 블록과 같이 프로그램 제어가 블록에 들어가면 블록 외부의 효과에는 영향을 주지 않고 블록에만 영향을 미치는 변수를 선언할 수 있습니다. 도메인. 하지만 자바스크립트에서는 이것이 작동하지 않습니다. 아래 코드를 살펴보세요.
다음과 같이 코드 코드를 복사합니다.
var x = 1;
console.log(x); // 1
만약 (참) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
결과는 1 2 2 입니다. 자바스크립트는 함수 범위이기 때문입니다. 이것이 C 계열 언어와의 가장 큰 차이점입니다. 이 프로그램의 if는 새 범위를 생성하지 않습니다.
많은 C, C++ 및 Java 프로그래머의 경우 이는 기대하거나 환영하는 것이 아닙니다. 다행히도 JavaScript 함수의 유연성으로 인해 이 문제를 해결할 수 있는 방법이 있습니다. 임시 범위를 만들어야 하는 경우 다음과 같이 수행하십시오.
다음과 같이 코드 코드를 복사합니다.
함수 foo() {
var x = 1;
만약 (x) {
(기능 () {
var x = 2;
// 다른 코드
}());
}
// x는 여전히 1입니다.
}
이 방법은 유연하며 임시 범위를 생성하려는 모든 위치에서 사용할 수 있습니다. 블록 내에서만이 아닙니다. 그러나 JavaScript 범위 지정을 이해하는 데 시간을 할애하는 것이 좋습니다. 이는 매우 유용하며 제가 가장 좋아하는 JavaScript 기능 중 하나입니다. 범위를 이해한다면 변수 호이스팅이 더 이해가 될 것입니다.
변수 선언, 이름 지정 및 승격
JavaScript에는 변수가 범위에 들어가는 4가지 기본 방법이 있습니다.
•1 내장 언어: 모든 범위에는 이것과 인수가 있습니다(번역자 참고 사항: 테스트 후에는 인수가 전역 범위에 표시되지 않습니다).
•2 형식 매개변수: 함수의 형식 매개변수는 함수 본문 범위의 일부가 됩니다.
•3 함수 선언: 다음 형식과 같습니다: function foo(){};
•4 변수 선언: 다음과 같습니다: var foo;
함수 선언과 변수 선언은 항상 인터프리터에 의해 메서드 본문의 맨 위로 조용히 "올려집니다". 이는 다음과 같은 코드를 의미합니다.
다음과 같이 코드 코드를 복사합니다.
함수 foo() {
술집();
var x = 1;
}
실제로는 다음과 같이 해석됩니다.
다음과 같이 코드 코드를 복사합니다.
함수 foo() {
var x;
술집();
x = 1;
}
변수가 정의된 블록의 실행 가능 여부와 관계없습니다. 다음 두 기능은 실제로 동일합니다.
다음과 같이 코드 코드를 복사합니다.
함수 foo() {
만약 (거짓) {
var x = 1;
}
반품;
var y = 1;
}
함수 foo() {
변수 x, y;
만약 (거짓) {
x = 1;
}
반품;
와이 = 1;
}
변수 할당은 호이스팅되지 않고 단지 선언이라는 점에 유의하십시오. 그러나 함수 선언이 조금 다르며 함수 본문도 승격됩니다. 그러나 함수를 선언하는 방법에는 두 가지가 있습니다.
다음과 같이 코드 코드를 복사합니다.
함수 테스트() {
foo(); // TypeError "foo는 함수가 아닙니다."
bar(); // "이것이 실행됩니다!"
var foo = function () { // 변수는 함수 표현식을 가리킵니다.
Alert("실행되지 않습니다!");
}
function bar() { // bar라는 이름의 함수 선언 함수
Alert("이것이 실행됩니다!");
}
}
시험();
이 예에서는 함수 선언만 함수 본문과 함께 호이스팅됩니다. foo 선언은 호이스팅되지만 그것이 가리키는 함수 본문은 실행 중에만 할당됩니다.
위 내용은 부스팅의 기본 사항 중 일부를 다루고 있으며 그다지 혼란스러워 보이지 않습니다. 그러나 일부 특수한 시나리오에서는 여전히 어느 정도의 복잡성이 존재합니다.
변수 구문 분석 순서
명심해야 할 가장 중요한 것은 가변 해상도 순서입니다. 앞서 제공한 범위에 이름 지정이 들어가는 4가지 방법을 기억하시나요? 변수가 구문 분석되는 순서는 내가 나열한 순서입니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
함수 a(){
}
var a;
Alert(a);//a의 함수 본문을 인쇄합니다.
</script>
<스크립트>
var a;
함수 a(){
}
Alert(a);//a의 함수 본문을 인쇄합니다.
</script>
//그러나 다음 두 가지 작성 방법의 차이점에 주의하세요.
<스크립트>
var a=1;
함수 a(){
}
Alert(a);//1을 출력
</script>
<스크립트>
함수 a(){
}
var a=1;
Alert(a);//1을 출력
</script>
여기에는 3가지 예외가 있습니다.
1 내장된 이름 인수가 이상하게 동작합니다. 함수 형식 매개변수 뒤에 선언해야 하지만 함수 선언 전에 선언해야 하는 것 같습니다. 즉, 형식 매개변수에 인수가 있으면 내장 매개변수보다 우선순위가 높습니다. 이는 매우 나쁜 기능이므로 형식 매개변수에 인수를 사용하지 마십시오.
2 이 변수를 어디에서나 정의하면 구문 오류가 발생하는데 이는 좋은 기능입니다.
3. 여러 개의 형식 매개변수가 동일한 이름을 갖는 경우 실제 작동 중에 해당 값이 정의되지 않은 경우에도 마지막 매개변수가 우선순위를 갖습니다.
명명된 함수
함수에 이름을 지정할 수 있습니다. 그렇다면 함수 선언이 아니며 함수 본문 정의에 지정된 함수 이름(있는 경우 아래 스팸, 번역자 메모)은 승격되지 않고 무시됩니다. 이해를 돕기 위한 몇 가지 코드는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
foo(); // TypeError "foo는 함수가 아닙니다."
바(); // 유효함
baz(); // TypeError "baz는 함수가 아닙니다."
spam(); // ReferenceError "스팸이 정의되지 않았습니다."
var foo = function () {}; // foo는 익명 함수를 가리킵니다.
function bar() {}; // 함수 선언
var baz = function spam() {}; // 명명된 함수, baz만 승격되고 스팸은 승격되지 않습니다.
foo(); // 유효함
바(); // 유효함
baz(); // 유효함
spam(); // ReferenceError "스팸이 정의되지 않았습니다."
코드 작성 방법
이제 범위 지정과 변수 호이스팅을 이해했으므로 이것이 JavaScript 코딩에 어떤 의미가 있습니까? 가장 중요한 것은 항상 var를 사용하여 변수를 정의하는 것입니다. 그리고 이름의 경우 범위에 항상 하나의 var 선언만 있어야 한다고 강력히 권장합니다. 이렇게 하면 범위 및 변수 호이스팅 문제가 발생하지 않습니다.
언어 사양이란 무엇을 의미합니까?
저는 ECMAScript 참조 문서가 항상 유용하다고 생각합니다. 범위 및 변수 호이스팅에 대해 내가 찾은 내용은 다음과 같습니다.
함수 본문 클래스에 변수가 선언된 경우 해당 변수는 함수 범위입니다. 그렇지 않으면 전역 범위가 지정됩니다(global 속성으로). 실행이 범위에 들어가면 변수가 생성됩니다. 블록은 새로운 범위를 정의하지 않으며 함수 선언과 프로시저(번역자는 전역 코드 실행이라고 생각함)만이 새로운 범위를 생성합니다. 변수는 생성될 때 정의되지 않은 상태로 초기화됩니다. 변수 선언문에 대입 연산이 있는 경우 변수 생성 시가 아니라 실행 시에만 대입 연산이 발생합니다.
이 글이 자바스크립트에 대해 혼란스러워하는 프로그래머들에게 한 줄기 빛을 가져다주기를 바랍니다. 저 역시 더 이상의 혼란을 야기하지 않도록 최선을 다하겠습니다. 제가 말을 잘못했거나 간과한 부분이 있으면 알려주세요.
번역가의 보충 자료
친구가 IE의 전역 범위에서 명명된 함수의 승격 문제에 대해 상기시켜주었습니다.
기사를 번역할 때 테스트한 방법은 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
함수t(){
스팸();
var baz = function spam() {alert('스팸입니다')};
}
티();
</script>
이러한 작성 방식, 즉 비전역 범위에서 명명된 함수를 승격하는 것은 ie 및 ff에서 동일한 성능을 갖습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
스팸();
var baz = function spam() {alert('스팸입니다')};
</script>
그러면 스팸은 ie에서는 실행될 수 있지만 ff에서는 실행될 수 없습니다. 이는 브라우저마다 이 세부 사항을 다르게 처리한다는 것을 보여줍니다.
이 질문은 또한 두 가지 다른 질문에 대해 생각하게 했습니다. 1: 전역 범위를 갖는 변수의 경우 var와 non-var 사이에 차이가 있습니다. var가 없으면 변수가 승격되지 않습니다. 예를 들어, 다음 두 프로그램 중 두 번째 프로그램은 오류를 보고합니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
경고(a);
var a=1;
</script>
다음과 같이 코드 코드를 복사합니다.
<스크립트>
경고(a);
a=1;
</script>
2: eval에서 생성된 지역 변수는 승격되지 않습니다(승격할 수 있는 방법이 없습니다).
다음과 같이 코드 코드를 복사합니다.
<스크립트>
var a = 1;
함수 t(){
경고(a);
eval('var a = 2');
경고(a);
}
티();
경고(a);
</script>