이전에는 JavaScript 엔진의 구문 분석 메커니즘을 통해 JavaScript의 작동 원리를 살펴보았습니다. 이제 페이지에서 JavaScript 코드의 실행 순서를 설명하기 위해 보다 생생한 예를 사용합니다. JavaScript 엔진의 작동 메커니즘이 기본 동작에 속하기 때문에 상대적으로 심오하다면 JavaScript 코드의 실행 순서는 더 생생합니다. 물론 JavaScript 코드의 실행 순서를 직관적으로 느낄 수 있기 때문입니다. 더 복잡하므로 JavaScript 언어를 살펴보기 전에 프로파일링하는 것도 필요합니다.
1.1 HTML 문서 흐름 순서대로 JavaScript 코드 실행
우선, 독자는 브라우저에서 HTML 문서의 파싱 과정이 다음과 같다는 것을 알아야 합니다. 브라우저는 문서 흐름에 따라 페이지 구조와 정보를 위에서 아래로 점진적으로 파싱합니다. 내장된 스크립트인 JavaScript 코드도 HTML 문서의 구성 요소로 간주되므로 로드 중 JavaScript 코드의 실행 순서도 스크립트 태그 <script>가 나타나는 순서에 따라 결정됩니다. 예를 들어 아래 문서 페이지를 탐색하면 코드가 위에서 아래로 단계별로 구문 분석되는 것을 볼 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
Alert("상위 스크립트");
</script>
<html><헤드>
<스크립트>
Alert("헤드 스크립트");
</script>
<제목></제목>
</head>
<본문>
<스크립트>
Alert("페이지 스크립트");
</script>
</body></html>
<스크립트>
Alert("하단 스크립트");
</script>
스크립트 태그 <script>의 src 속성을 통해 외부 JavaScript 파일 스크립트를 가져오는 경우 해당 명령문이 나타나는 순서대로 실행되며 실행 프로세스는 문서 로딩의 일부입니다. 외부 JavaScript 파일이므로 실행이 지연되지 않습니다. 예를 들어 위 문서의 헤드와 본문 영역에 있는 스크립트를 외부 자바스크립트 파일로 옮긴 후 src 속성을 통해 가져옵니다. 계속해서 페이지 문서를 미리 보면 동일한 실행 순서를 볼 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
Alert("상위 스크립트");
</script>
<html>
<머리>
<script src="//www.VeVB.COm/head.js"></script>
<제목></제목>
</head>
<본문>
<script src="//www.VeVB.COm/body.js"></script>
</body>
</html>
<스크립트>
Alert("하단 스크립트");
</script>
1.2 사전 컴파일과 실행 순서의 관계
Javascript에서 함수는 Javascript의 첫 번째 유형입니다. 함수를 작성할 때 실제로는 함수 유형의 엔터티를 만드는 것입니다.
다음과 같은 형식으로 작성할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
함수안녕하세요()
{
Alert("안녕하세요");
}
안녕하세요();
varHello = 함수()
{
Alert("안녕하세요");
}
안녕하세요();
사실, 그들은 모두 동일합니다. 하지만 함수를 수정하면 매우 이상한 문제를 발견하게 됩니다.
다음과 같이 코드 코드를 복사합니다.
<scripttype="텍스트/자바스크립트">
함수안녕하세요() {
Alert("안녕하세요");
}
안녕하세요();
함수안녕하세요() {
Alert("안녕하세요 월드");
}
안녕하세요();
</script>
다음과 같은 결과를 볼 수 있습니다. Hello World가 연속으로 두 번 출력됩니다.
우리가 상상했던 Hello, Hello World가 아닌.
이는 Javascript가 순서대로 완전히 해석되고 실행되지 않기 때문입니다. 대신 Javascript는 해석 전에 "사전 컴파일"됩니다. 사전 컴파일 과정에서는 정의된 함수가 먼저 실행되고 모든 var 변수가 생성되므로 기본값은 정의되지 않습니다. 프로그램 실행 효율성.
즉, 위의 코드 조각은 실제로 JS 엔진에 의해 다음 형식으로 사전 컴파일됩니다.
다음과 같이 코드 코드를 복사합니다.
<scripttype="텍스트/자바스크립트">
varHello = 함수() {
Alert("안녕하세요");
}
안녕하세요 = 함수() {
Alert("안녕하세요 월드");
}
안녕하세요();
안녕하세요();
</script>
위의 코드를 보면 함수도 데이터이면서 변수라는 것을 분명히 알 수 있습니다. "함수"에 값을 할당(재할당)할 수도 있습니다.
물론 이러한 상황을 방지하기 위해 다음과 같이 할 수도 있습니다.
다음과 같이 코드 코드를 복사합니다.
<scripttype="텍스트/자바스크립트">
함수안녕하세요() {
Alert("안녕하세요");
}
안녕하세요();
</script>
<scripttype="텍스트/자바스크립트">
함수안녕하세요() {
Alert("안녕하세요 월드");
}
안녕하세요();
</script>
이런 식으로 프로그램은 두 개의 섹션으로 나뉘며 JS 엔진은 두 섹션을 하나로 묶지 않습니다.
JavaScript 엔진은 스크립트를 구문 분석할 때 사전 컴파일 중에 선언된 모든 변수와 함수를 처리합니다.
다음을 수행하십시오.
1. 실행 전에 "precompilation"과 유사한 작업이 수행됩니다. 먼저 현재 실행 환경에 활성 개체가 생성되고 var로 선언된 변수가 활성 개체의 속성으로 설정되지만 이때는 , 이러한 변수의 할당은 정의되지 않으며 function으로 정의된 함수도 활성 개체의 속성으로 추가되며 해당 값은 정확히 함수의 정의입니다.
2. 해석 및 실행 단계에서 변수를 구문 분석해야 할 경우 현재 실행 환경의 활성 개체에서 해당 변수를 먼저 검색하고 실행 환경 소유자가 프로토타입 속성을 가지고 있는 경우 해당 변수를 찾습니다. 프로토타입 체인에서 검색됩니다. 그렇지 않으면 범위 체인에 따라 검색됩니다. var a = ...와 같은 문을 만나면 해당 변수에 값이 할당됩니다(참고: 변수 할당은 해석 및 실행 단계에서 완료됩니다. 이 전에 변수를 사용하면 해당 값이 정의되지 않음) 따라서 JavaScript 인터프리터가 다음 스크립트를 실행할 때 오류가 보고되지 않는 것으로 나타납니다.
다음과 같이 코드 코드를 복사합니다.
경고(a); // 정의되지 않은 값을 반환합니다.
var a =1;
경고(a); // 반환 값 1
변수 선언은 사전 컴파일 시 처리되므로 실행 중에 모든 코드에 표시됩니다. 그러나 위 코드를 실행하면 프롬프트된 값이 1이 아니라 정의되지 않은 것을 볼 수 있습니다. 이는 변수 초기화 과정이 사전 컴파일이 아닌 실행 중에 발생하기 때문입니다. 실행 중에 JavaScript 인터프리터는 코드를 순서대로 구문 분석합니다. 이전 코드 줄에서 변수에 값이 할당되지 않으면 JavaScript 인터프리터는 기본값인 정의되지 않음을 사용합니다. 두 번째 줄에서는 변수 a에 값이 할당되므로 코드의 세 번째 줄에서는 변수 a의 값이 정의되지 않은 것이 아니라 1이라는 메시지가 표시됩니다.
마찬가지로 다음 예제에서는 함수가 선언되기 전에 함수를 호출하는 것이 적법하고 올바르게 구문 분석될 수 있으므로 반환 값은 1입니다.
다음과 같이 코드 코드를 복사합니다.
f(); // 함수 호출, 값 1 반환
함수 f(){
경고(1);
}
그러나 함수가 다음과 같이 정의되면 JavaScript 인터프리터는 구문 오류를 표시합니다.
다음과 같이 코드 코드를 복사합니다.
f(); // 함수 호출 및 반환 구문 오류
var f = 함수(){
경고(1);
}
위의 예에서 정의한 함수는 변수 f에만 값으로 할당되기 때문입니다. 따라서 사전 컴파일 기간 동안 JavaScript 인터프리터는 변수 f의 선언만 처리할 수 있고 변수 f의 값만 처리할 수 있습니다. 실행 기간 동안 계속해서 할당을 수행하면 자연스럽게 구문 오류가 발생하여 객체 f를 찾을 수 없습니다.
몇 가지 예를 들어보세요.
다음과 같이 코드 코드를 복사합니다.
<스크립트 유형="텍스트/자바스크립트">
/*프리컴파일 과정에서 func는 윈도우 환경의 활성 객체에 있는 속성이고, 값은 정의되지 않은 값을 커버하는 함수입니다*/
경고(func); //함수 func(){alert("안녕하세요!")}
var func = "이것은 변수입니다"
함수 함수(){
경고("안녕하세요!")
}
/*실행 중에 var가 발견되어 "this is avariable"에 다시 할당되었습니다.*/
Alert(func); //변수입니다.
</script>
다음과 같이 코드 코드를 복사합니다.
<스크립트 유형="텍스트/자바스크립트">
var 이름 = "feng"; 함수 func()
{
/*먼저 func 환경에서 undefine에 name을 할당한 후, 실행 시 func 환경에서 active 객체의 name 속성을 찾아봅니다. 이때 값은 undefine으로 미리 컴파일되어 출력이 undefine이 됩니다. 펭 */
경고(이름); //정의되지 않음 var 이름 = "JSF";
경고(이름); //JSF
}
기능();
경고(이름);
//펑
</script>
변수 및 함수 선언은 문서의 어느 위치에나 있을 수 있지만 모든 JavaScript 코드 전에 전역 변수 및 함수를 선언하고 변수를 초기화하고 할당하는 것이 좋습니다. 함수 내에서 변수는 먼저 선언된 다음 참조됩니다.
1.3 블록 단위로 JavaScript 코드 실행
소위 코드 블록은 <script> 태그로 구분된 코드 세그먼트입니다. 예를 들어 아래 두 개의 <script> 태그는 두 개의 JavaScript 코드 블록을 나타냅니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
// 자바스크립트 코드 블록 1
var a =1;
</script>
<스크립트>
// 자바스크립트 코드 블록 2
함수 f(){
경고(1);
}
</script>
JavaScript 인터프리터는 스크립트를 실행할 때 블록 단위로 실행합니다. 평신도의 관점에서 보면 브라우저가 HTML 문서 스트림을 구문 분석할 때 <script> 태그를 발견하면 JavaScript 인터프리터는 코드 블록이 로드될 때까지 기다렸다가 먼저 코드 블록을 사전 컴파일한 다음 실행합니다. 실행 후 브라우저는 아래 HTML 문서 스트림을 계속 구문 분석하고 JavaScript 인터프리터는 다음 코드 블록을 처리할 준비가 됩니다.
자바스크립트는 블록 단위로 실행되기 때문에 자바스크립트 블록 내에서 다음 블록에 선언된 변수나 함수를 호출하면 구문 오류가 발생합니다. 예를 들어 JavaScript 인터프리터가 다음 코드를 실행하면 변수 a가 정의되지 않았고 객체 f를 찾을 수 없다는 구문 오류가 표시됩니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
// 자바스크립트 코드 블록 1
경고(a);
에프();
</script>
<스크립트>
// 자바스크립트 코드 블록 2
var a =1;
함수 f(){
경고(1);
}
</script>
자바스크립트는 블록 단위로 실행되지만, 서로 다른 블록이 동일한 전역 범위에 속하므로 블록 간 변수와 함수를 공유할 수 있습니다.
1.4 이벤트 메커니즘을 사용하여 JavaScript 실행 순서 변경
JavaScript는 코드를 청크로 처리하고 HTML 문서 흐름의 구문 분석 순서를 따르기 때문에 위 예에서 이러한 구문 오류를 볼 수 있습니다. 그러나 문서 스트림을 로드한 후 다시 액세스하면 이러한 오류가 발생하지 않습니다. 예를 들어, 두 번째 코드 블록의 변수 및 함수에 접근하는 코드를 페이지 초기화 이벤트 함수에 배치하면 구문 오류가 발생하지 않습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트>
// 자바스크립트 코드 블록 1
window.onload = function(){ // 페이지 초기화 이벤트 처리 함수
경고(a);
에프();
}
</script>
<스크립트>
// 자바스크립트 코드 블록 2
var a =1;
함수 f(){
경고(1);
}
</script>
보안상의 이유로 일반적으로 페이지가 초기화된 후에만 JavaScript 코드 실행을 허용합니다. 이를 통해 JavaScript 실행에 대한 네트워크 속도의 영향을 피할 수 있으며 HTML 문서 흐름으로 인한 JavaScript 실행 제한도 피할 수 있습니다.
알아채다
페이지에 여러 개의 windows.onload 이벤트 핸들러가 있는 경우 마지막 핸들러만 유효합니다. 이 문제를 해결하려면 모든 스크립트 또는 호출 함수를 동일한 onload 이벤트 핸들러에 넣을 수 있습니다. 예를 들면 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
window.onload = 함수(){
f1();
f2();
f3();
}
그리고 이와 같이 onload 이벤트 핸들러에서 함수 호출 순서를 간단히 조정함으로써 함수의 실행 순서를 변경할 수 있습니다.
페이지 초기화 이벤트 외에도 마우스 이벤트, 키보드 이벤트, 시계 트리거 등과 같은 다양한 대화형 이벤트를 통해 JavaScript 코드의 실행 순서를 변경할 수도 있습니다. 자세한 설명은 14장을 참조하세요.
1.5 JavaScript 출력 스크립트의 실행 순서
JavaScript 개발에서는 문서 객체의 write() 메서드를 사용하여 JavaScript 스크립트를 출력하는 경우가 많습니다. 그렇다면 이러한 동적 출력 스크립트는 어떻게 실행됩니까? 예를 들어:
다음과 같이 코드 코드를 복사합니다.
document.write('<script type="text/javascript">');
document.write('f();');
document.write('function f(){');
document.write('alert(1);');
document.write('}');
document.write('</script>');
위 코드를 실행하면 다음과 같은 사실을 알 수 있습니다. document.write() 메서드는 먼저 스크립트가 있는 문서 위치에 출력 스크립트 문자열을 씁니다. document.write()가 있는 문서의 내용을 구문 분석한 후 브라우저는 계속해서 document.write() 출력 콘텐츠를 구문 분석한 다음 후속 HTML 문서를 순서대로 구문 분석합니다. 즉, JavaScript 스크립트에 의해 출력된 코드 문자열은 출력 직후에 실행됩니다.
document.write() 메소드를 사용한 JavaScript 스크립트 문자열 출력은 동시에 출력되는 <script> 태그에 배치되어야 합니다. 그렇지 않으면 JavaScript 해석기가 이러한 합법적인 JavaScript 코드를 인식할 수 없으며 페이지 문서에 일반 문자열로 표시됩니다. 예를 들어 다음 코드는 JavaScript 코드를 실행하는 대신 표시합니다.
다음과 같이 코드 코드를 복사합니다.
document.write('f();');
document.write('function f(){');
document.write('alert(1);');
document.write(');');
그러나 document.write() 메서드를 통해 스크립트를 출력하고 실행하는 데에는 특정 위험이 있습니다. 왜냐하면 서로 다른 JavaScript 엔진이 서로 다른 순서로 스크립트를 실행하고 구문 분석 중에 서로 다른 브라우저에서 버그가 발생할 수 있기 때문입니다.
Ø 문제 1: document.write() 메서드를 통해 가져온 외부 JavaScript 파일에 선언된 변수 또는 함수를 찾을 수 없습니다. 예를 들어 아래 샘플 코드를 살펴보세요.
다음과 같이 코드 코드를 복사합니다.
document.write('<script type="text/javascript" src="//www.VeVB.COm/test.js">
</script>');
document.write('<script type="text/javascript">');
document.write('alert(n);'); // IE는 변수 n을 찾을 수 없다는 메시지를 표시합니다.
document.write('</script>');
Alert(n+1); // 모든 브라우저는 변수 n을 찾을 수 없다는 메시지를 표시합니다.
외부 JavaScript 파일(test.js)의 코드는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
var n = 1;
다른 브라우저에서 테스트하면 구문 오류가 발생하고 변수 n을 찾을 수 없습니다. 즉, 이 코드 블록의 document.write() 메서드를 사용하여 스크립트 출력으로 가져온 외부 JavaScript 파일에 포함된 변수에 JavaScript 코드 블록에 액세스하면 구문 오류가 표시됩니다. 동시에 IE 브라우저의 경우 스크립트뿐만 아니라 출력 스크립트에서도 외부 JavaScript 파일로 가져온 출력 변수를 찾을 수 없다는 메시지가 표시됩니다(표현이 약간 길고 복잡합니다. 이해하지 못하는 독자는 위 코드를 실행해 보면 이해할 수 있습니다.)
Ø 질문 2: JavaScript 엔진마다 외부 가져오기 스크립트 출력에 대한 실행 순서가 약간 다릅니다. 예를 들어 아래 샘플 코드를 살펴보세요.
다음과 같이 코드 코드를 복사합니다.
<스크립트 유형="텍스트/자바스크립트">
document.write('<script type="text/javascript" src="http://shaozhuqing.com/test1.js">
</script>');
document.write('<script type="text/javascript">');
document.write('alert(2);')
document.write('alert(n+2);');
document.write('</script>');
</script>
<스크립트 유형="텍스트/자바스크립트">
경고(n+3);
</script>
외부 JavaScript 파일(test1.js)에 대한 코드는 아래와 같습니다.
다음과 같이 코드 코드를 복사합니다.
var n = 1;
경고(n);
IE 브라우저의 실행 순서는 그림 1-6과 같습니다.
그림 1-6 IE 7 브라우저에서 나타나는 실행 순서 및 구문 오류
DOM 표준을 준수하는 브라우저의 실행 순서는 IE 브라우저의 실행 순서와 다르며, 구문 오류가 없습니다. 그림 1-7은 Firefox 3.0 브라우저의 실행 순서를 보여줍니다.
그림 1-7 Firefox 3 브라우저 실행 순서 및 구문 오류 메시지
다양한 브라우저의 다양한 실행 순서와 발생 가능한 버그를 해결하세요. 출력 스크립트를 사용하여 가져온 모든 외부 파일을 독립적인 코드 블록에 넣을 수 있으므로 위에서 소개한 JavaScript 코드 블록의 실행 순서에 따라 이 문제를 피할 수 있습니다. 예를 들어 위의 예에서는 다음과 같이 디자인할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
<스크립트 유형="텍스트/자바스크립트">
document.write('<script type="text/javascript" src="//www.VeVB.COm/test1.js"></script>');
</script>
<스크립트 유형="텍스트/자바스크립트">
document.write('<script type="text/javascript">');
document.write('alert(2);') // 팁 2
document.write('alert(n+2);'); // 팁 3
document.write('</script>');
경고(n+3); // 팁 4
</script>
<스크립트 유형="텍스트/자바스크립트">
경고(n+4); // 팁 5
</script>
이런 식으로 위의 코드는 다양한 브라우저에서 순서대로 실행될 수 있으며, 출력 순서는 1, 2, 3, 4, 5가 됩니다. 문제의 원인은 가져온 스크립트 출력과 현재 JavaScript 코드 블록 간의 모순입니다. 별도로 출력하면 충돌이 발생하지 않습니다.