우선 실행 환경과 범위는 전혀 다른 개념이라는 점을 알아야 합니다.
각 함수 호출에는 함수와 밀접하게 관련된 범위 및 실행 환경이 있습니다. 기본적으로 범위는 함수를 기반으로 하고 실행 환경은 개체를 기반으로 합니다(예: 전역 실행 환경은 창 개체입니다).
즉, 범위에는 호출된 함수의 변수 액세스가 포함되며 호출 시나리오에 따라 다릅니다. 실행 환경은 항상 현재 실행 중인 코드를 소유한 개체에 대한 참조인 this 키워드의 값입니다. 각 실행 환경에는 이와 관련된 변수 개체가 있으며, 환경에서 정의된 모든 변수와 함수는 이 개체에 저장됩니다. 우리가 작성하는 코드는 이 객체에 액세스할 수 없지만 파서는 데이터를 처리할 때 뒤에서 이를 사용합니다.
실행 환경(실행 컨텍스트라고도 함 - 실행 컨텍스트)
JavaScript 인터프리터가 실행 코드를 초기화하면 먼저 이 시점부터 기본적으로 전역 실행 환경으로 들어갑니다. 이 함수는 새로운 실행 환경을 생성합니다.
각 기능에는 고유한 실행 환경이 있습니다. 실행 흐름이 함수에 들어가면 함수의 환경이 실행 스택으로 푸시됩니다. 함수가 실행된 후 스택은 해당 환경을 팝하고 이전 실행 환경으로 제어를 반환합니다. ECMAScript 프로그램의 실행 흐름은 이 편리한 메커니즘으로 제어됩니다.
실행 환경은 생성 단계와 실행 단계로 나눌 수 있습니다. 생성 단계에서 파서는 먼저 변수, 함수 선언, 실행 환경에 정의된 매개변수로 구성된 변수 객체(활성화 객체라고도 함)를 생성합니다. 이 단계에서는 스코프 체인이 초기화되고 이 값이 확정됩니다. 실행 단계에서는 코드가 해석되고 실행됩니다.
데모:
<script type="text/javascript"> function Fn1(){ function Fn2(){ alert(document.body.tagName);//BODY //other code... } Fn2(); } Fn1(); //code here </script>
요약
브라우저에서 자바스크립트 코드를 로드할 때 가장 먼저 입력하는 코드는 다음과 같습니다. 기본 전역 실행 환경. 전역 실행 환경에서 함수가 호출되고 실행되면 프로그램 흐름은 호출된 함수에 진입합니다. 이때 JS 엔진은 해당 함수에 대한 새로운 실행 환경을 생성하고 이를 실행 환경 스택의 맨 위로 푸시합니다. 브라우저는 항상 현재 스택의 최상위에 있는 실행 환경을 실행하며, 실행이 완료되면 스택의 최상위에서 실행 환경이 팝된 후 그 아래의 실행 환경으로 들어가 코드를 실행합니다. 이러한 방식으로 스택의 실행 환경은 순차적으로 실행되고 전역 실행 환경으로 돌아갈 때까지 스택에서 팝됩니다.
또한 몇 가지 사항에 유의하세요.
단일 스레드
동기 실행
유일한 전역 실행 환경
로컬 실행 환경
개수에는 제한이 없습니다. 함수가 호출될 때마다 여러 번 호출되는 자체 함수(즉, 함수가 여러 번 호출되면 여러 다른 로컬 실행 환경이 생성됩니다.
Scope
환경에서 코드가 실행되면 변수 개체의 범위 체인이 생성됩니다. 범위 체인의 목적은 실행 환경에서 액세스할 수 있는 모든 변수 및 함수에 대한 순서화된 액세스를 보장하는 것입니다.
스코프 체인에는 실행 환경 스택의 각 실행 환경에 해당하는 변수 개체가 포함됩니다. 범위 체인을 통해 변수에 대한 액세스와 식별자 확인을 결정할 수 있습니다.
참고: 전역 실행 환경의 변수 개체는 항상 범위 체인의 마지막 개체입니다.
변수에 접근할 때 가시성 문제가 있을 것입니다(내부 환경은 외부 레이어의 변수와 함수에 접근할 수 있지만, 외부 환경은 내부 레이어의 변수와 함수에 접근할 수 없습니다). 더 깊이 들어가 보면, JavaScript 엔진은 변수에 접근하거나 함수를 호출할 때 규칙에 따라 다양한 실행 환경의 변수 객체로부터 연결된 목록을 구성합니다. 찾을 수 없으면 전역 실행 환경의 변수 개체, 즉 윈도우 개체를 찾을 때까지 두 번째 변수 개체를 계속 검색합니다. 이는 또한 스코프 체인(Scope Chain)의 개념을 형성했습니다.
스코프 체인 다이어그램은 실행 환경과 스코프 간의 관계(일대일 대응), 스코프 간의 관계(연결된 목록 구조, 하향식 관계)를 명확하게 표현합니다. ).
데모:
var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; // 这里可以访问color, anotherColor, 和 tempColor } // 这里可以访问color 和 anotherColor,但是不能访问 tempColor swapColors(); } changeColor(); // 这里只能访问color console.log("Color is now " + color);
위 코드에는 전역 실행 환경, ChangeColor( ), swapColors()의 로컬 실행 환경입니다.
전역 환경에는 가변 색상과 함수changecolor()가 있습니다.
changecolor() 함수의 로컬 환경에는 anothercolor 속성과 swapcolors 함수가 있습니다. 물론,changecolor 함수도 있습니다. 주변 환경(예: 전역 환경)의 변수뿐만 아니라
swapcolor() 함수에는 로컬 환경의 변수 tempcolor가 있습니다. 위 두 환경(changecolor 및 window)의 모든 변수는 이 함수 내에서 액세스할 수 있습니다. 왜냐하면 이 두 환경이 상위 실행 환경이기 때문입니다.
위 코드의 스코프 체인은 아래와 같습니다.
从上图发现。内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数。
标识符解析(变量名或函数名搜索)是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后(全局执行环境)回溯,直到找到标识符为止。
执行环境与作用域的区别与联系
执行环境为全局执行环境和局部执行环境,局部执行环境是函数执行过程中创建的。
作用域链是基于执行环境的变量对象的,由所有执行环境的变量对象(对于函数而言是活动对象,因为在函数执行环境中,变量对象是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO(变量对象)的角色。)共同组成。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。
小练习
<script type="text/javascript"> (function(){ a= 5; console.log(window.a);//undefined var a = 1;//这里会发生变量声明提升 console.log(a);//1 })(); </script>
window.a之所以是undefined,是因为var a = 1;发生了变量声明提升。相当于如下代码:
<script type="text/javascript"> (function(){ var a;//a是局部变量 a = 5;//这里局部环境中有a,就不会找全局中的 console.log(window.a);//undefined a = 1;//这里会发生变量声明提升 console.log(a);//1 })(); </script>