javascript 정의부터 실행까지 JS 엔진은 구현 계층에서 많은 초기화 작업을 수행합니다. 따라서 JS 엔진의 작동 메커니즘을 배우기 전에 실행 환경 스택, 전역 개체 등 몇 가지 관련 개념을 소개해야 합니다. , 실행 환경, 변수 객체, 활성 객체, 범위 및 범위 체인 등 이러한 개념은 JS 엔진의 핵심 구성 요소입니다. 이 기사의 목적은 각 개념을 개별적으로 설명하는 것이 아니라 간단한 데모를 사용하여 분석을 수행하고 정의부터 실행까지 JS 엔진의 모든 세부 사항과 이러한 개념이 수행하는 역할을 포괄적으로 설명하는 것입니다.
관련 학습 권장 사항: javascript 비디오 튜토리얼
var x = 1; //定义一个全局变量 x function A(y){ var x = 2; //定义一个局部变量 x function B(z){ //定义一个内部函数 B console.log(x+y+z); } return B; //返回函数B的引用 } var C = A(1); //执行A,返回B C(1); //执行函数B
이 데모는 클로저이며 실행 결과는 4입니다. 아래에서는 이를 세 부분으로 나눕니다: 전역 초기화, 실행 함수 A, 실행 함수 B JS 엔진의 작동 메커니즘을 단계별로 분석해 보겠습니다.
1. 전역 초기화
JS 엔진이 실행 가능한 코드 조각을 입력하면 다음 세 가지 초기화 작업을 완료해야 합니다.
먼저 , 전역 개체 생성(Global Object) , 이 개체의 복사본은 전역적으로 하나만 있고 해당 속성은 어디에서나 액세스할 수 있으며 해당 존재는 응용 프로그램의 전체 수명 주기와 함께 제공됩니다. 전역 객체가 생성되면 Math, String, Date, document 등 일반적으로 사용되는 JS 객체가 해당 속성으로 사용됩니다.
이 전역 객체는 이름으로 직접 접근할 수 없기 때문에 또 다른 속성 창이 존재하고, window가 자신을 가리키므로 전역 객체는 window를 통해 접근할 수 있습니다. 의사 코드로 시뮬레이션된 전역 객체의 일반적인 구조는 다음과 같습니다.
//创建一个全局对象 var globalObject = { Math:{}, String:{}, Date:{}, document:{}, //DOM操作 ... window:this //让window属性指向了自身 }
그런 다음 JS 엔진은 실행 환경 스택(Execution Context Stack)을 구축하는 동시에 전역 실행 환경도 생성해야 합니다. (실행 컨텍스트) EC를 추가하고 이를 추가합니다. 전역 실행 환경 EC가 실행 환경 스택에 푸시됩니다. 실행 환경 스택의 기능은 프로그램이 올바른 순서로 실행될 수 있도록 보장하는 것입니다.
JavaScript에서는 각 함수마다 고유한 실행 환경이 있습니다. 함수가 실행되면 해당 함수의 실행 환경이 실행 환경 스택의 맨 위로 푸시되고 실행 권한을 얻습니다. 함수의 실행이 완료되면 해당 실행 환경은 스택 상단에서 제거되고 실행 권한은 이전 실행 환경으로 반환됩니다. 우리는 의사코드를 사용하여 실행 환경 스택과 EC 사이의 관계를 시뮬레이션합니다.
var ECStack = []; //定义一个执行环境栈,类似于数组 var EC = {}; //创建一个执行空间, //ECMA-262规范并没有对EC的数据结构做明确的定义,你可以理解为在内存中分配的一块空间 ECStack.push(EC); //进入函数,压入执行环境 ECStack.pop(EC); //函数返回后,删除执行环境
마지막으로 JS 엔진은 EC와 연관된 전역 변수 객체(Varibale Object) VO를 생성하고 VO가 포함할 뿐만 아니라 전역 객체를 가리킵니다. 전역 객체의 원래 속성에는 전역적으로 정의된 변수 x와 함수 A도 포함됩니다. 동시에 함수 A를 정의할 때 내부 속성 범위가 A에 추가되고 범위는 VO를 가리킵니다. 각 함수가 정의되면 해당 함수와 연관된 범위 속성이 생성됩니다. 범위는 항상 함수가 정의된 환경을 가리킵니다. 이때 ECStack 구조는 다음과 같습니다.
ECStack = [ //执行环境栈 EC(G) = { //全局执行环境 VO(G):{ //定义全局变量对象 ... //包含全局对象原有的属性 x = 1; //定义变量x A = function(){...}; //定义函数A A[[scope]] = this; //定义A的scope,并赋值为VO本身 } } ];
2. 함수 A 실행
실행이 A(1)에 들어가면 JS 엔진은 다음 작업을 완료해야 합니다.
먼저 JS 엔진은 함수 A의 실행 환경 EC. 그런 다음 EC는 실행 환경 스택의 맨 위로 푸시하고 실행 권한을 얻습니다. 이때 실행 환경 스택에는 전역 실행 환경과 함수 A의 실행 환경이라는 두 가지 실행 환경이 있습니다. A의 실행 환경은 스택의 맨 위에 있고 전역 실행 환경은 맨 아래에 있습니다. 스택의.
그런 다음 함수 A의 범위 체인(Scope Chain)을 만듭니다. JavaScript에서는 각 실행 환경에 식별자 확인을 위한 자체 범위 체인이 있습니다. 실행 환경이 생성되면 해당 범위 체인이 포함된 개체로 초기화됩니다. 현재 실행 중인 함수의 범위입니다.
그러면 JS 엔진은 현재 함수의 활성화 개체(Activation Object) AO를 생성하게 됩니다. 여기서 활동 개체는 변수 개체의 역할을 하지만 함수에서는 이름이 다릅니다(변수 개체라고 생각하시면 됩니다). 일반적인 개념으로 활성 객체는 그것의 가지입니다.) AO에는 함수의 형식 매개 변수, 인수 객체, 이 객체, 지역 변수 및 내부 함수의 정의가 포함되어 있으며 AO는 맨 위로 푸시됩니다. 범위 체인의.
함수 B를 정의할 때 JS 엔진은 B에 범위 속성을 추가하고 함수 B가 정의된 환경을 범위로 가리킨다는 점에 유의해야 합니다. AO, AO는 연결리스트의 맨 앞에 위치한다. 연결리스트는 끝과 끝이 연결되어 있으므로 함수 B의 범위는 A의 전체 범위 체인을 가리킨다. 이번에는 ECStack 구조를 살펴보겠습니다:
ECStack = [ //执行环境栈 EC(A) = { //A的执行环境 [scope]:VO(G), //VO是全局变量对象 AO(A) : { //创建函数A的活动对象 y:1, x:2, //定义局部变量x B:function(){...}, //定义函数B B[[scope]] = this; //this指代AO本身,而AO位于scopeChain的顶端,因此B[[scope]]指向整个作用域链 arguments:[],//平时我们在函数中访问的arguments就是AO中的arguments this:window //函数中的this指向调用者window对象 }, scopeChain:<AO(A),A[[scope]]> //链表初始化为A[[scope]],然后再把AO加入该作用域链的顶端,此时A的作用域链:AO(A)->VO(G) }, EC(G) = { //全局执行环境 VO(G):{ //创建全局变量对象 ... //包含全局对象原有的属性 x = 1; //定义变量x A = function(){...}; //定义函数A A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G) } } ];
3. 함수 B 실행
함수 A가 실행된 후 B에 대한 참조가 반환되어 변수 C에 할당됩니다. C(1)을 실행하는 것은 다음과 같습니다. B(1)을 실행하려면 JS 엔진은 다음 작업을 완료해야 합니다.
먼저 위와 같이 함수 B의 실행 환경 EC를 생성한 다음 EC를 실행 환경 스택의 최상위에 푸시하고 실행 권한을 얻습니다. . 이때 실행 환경 스택에는 전역 실행 환경과 함수 B의 실행 환경이라는 두 가지 실행 환경이 있다. 스택의 맨 위에는 B의 실행 환경이 있고, 맨 아래에는 전역 실행 환경이 있다. 스택의. (참고: 함수 A가 반환되면 A의 실행 환경은 스택에서 삭제되고 전역 실행 환경만 남습니다.)
然后,创建函数B的作用域链,并初始化为函数B的scope所包含的对象,即包含了A的作用域链。最后,创 建函数B的活动对象AO,并将B的形参z, arguments对象 和 this对象作为AO的属性。此时ECStack将会变成这样:
ECStack = [ //执行环境栈 EC(B) = { //创建B的执行环境,并处于作用域链的顶端 [scope]:AO(A), //指向函数A的作用域链,AO(A)->VO(G) var AO(B) = { //创建函数B的活动对象 z:1, arguments:[], this:window } scopeChain:<AO(B),B[[scope]]> //链表初始化为B[[scope]],再将AO(B)加入链表表头,此时B的作用域链:AO(B)->AO(A)-VO(G) }, EC(A), //A的执行环境已经从栈顶被删除, EC(G) = { //全局执行环境 VO:{ //定义全局变量对象 ... //包含全局对象原有的属性 x = 1; //定义变量x A = function(){...}; //定义函数A A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G) } } ];
当函数B执行“x+y+z”时,需要对x、y、z 三个标识符进行一一解析,解析过程遵守变量查找规则:先查找自己的活动对象中是否存在该属性,如果存在,则停止查找并返回;如果不存在,继续沿着其作用域 链从顶端依次查找,直到找到为止,如果整个作用域链上都未找到该变量,则返回“undefined”。从上面的分析可以看出函数B的作用域链是这样的:
AO(B)->AO(A)->VO(G)
因此,变量x会在AO(A)中被找到,而不会查找VO(G)中的x,变量y也会在AO(A)中被找到,变量z 在自身的AO(B)中就找到了。所以执行结果:2+1+1=4.
简单的总结语
了解了JS引擎的工作机制之后,我们不能只停留在理解概念的层面,而要将其作为基础工具,用以优化和改善我们在实际工作中的代码,提高执行效率,产 生实际价值才是我们的真正目的。就拿变量查找机制来说,如果你的代码嵌套很深,每引用一次全局变量,JS引擎就要查找整个作用域链,比如处于作用域链的最 底端window和document对象就存在这个问题,因此我们围绕这个问题可以做很多性能优化的工作,当然还有其他方面的优化,此处不再赘述,本文仅 当作抛砖引玉吧!
위 내용은 자바스크립트 정의부터 실행까지 알아야 할 사항의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!