적합한 독자: 숙련된 개발자, 전문 프론트엔드 직원.
원저자: Dmitry A. Soshnikov
출시 시간: 2010-09-02
원문: http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
참고자료 1 : http://ued.ctrip.com/blog/?p=2795
참고 2 : http://www.cnblogs.com/ifishing/archive/2010/12/08/1900594.html
Main 위의 두 전문가의 중국어 번역을 결합하고 두 기사의 장점을 결합합니다.
먼저 ECMASript의 가장 기본 개념이기도 한 객체 [Object]의 개념을 살펴보겠습니다.
Object Object
ECMAScript는 Objects 객체를 처리하는 데 사용되는 매우 추상적인 객체 지향 언어입니다. 물론 기본 유형도 있지만 필요한 경우 객체 객체로 변환해야 합니다. . 사용.
객체는 속성의 모음이며 단일 프로토타입 객체를 갖습니다. 프로토타입은 객체이거나 null 값일 수 있습니다. 프로토타입 객체[프로토타입 객체]는 다음과 같습니다. 개체 또는 null 값입니다.
코드 복사
기본 객체의 예를 들어 보겠습니다. 먼저 객체의 프로토타입이 내부 [[prototype]] 속성에 대한 참조라는 점을 이해해야 합니다.
그러나 일반적으로 말하면 __proto__와 같은 이중 괄호 대신 ____ 밑줄을 사용합니다(표준은 아니지만 SpiderMonkey와 같은 일부 스크립트 엔진의 프로토타입 개념을 구체적으로 구현한 것임) ).
그림 1. 프로토타입이 있는 기본 객체
프로토타입이 왜 필요한가요? 이 질문에 답하기 위해 프로토타입 체인의 개념을 생각해 보겠습니다.
프로토타입 체인
프로토타입 객체도 일반 객체이며, 자체 프로토타입을 가질 수도 있습니다. 프로토타입 객체의 프로토타입이 null이 아닌 경우 이를 프로토타입 체인(프로토타입 체인)이라고 합니다. ).
프로토타입 체인은 상속 및 공유 속성을 구현하는 데 사용되는 유한한 개체 체인입니다.
이런 상황을 상상해 보세요. 2개의 객체가 있고 대부분의 내용은 동일하지만 작은 부분만 다릅니다. 분명히 좋은 디자인 패턴에서는 동일한 부분을 재사용해야 합니다. 모든 객체에 동일한 메서드나 속성을 반복적으로 정의하지 마세요. 클래스 기반 시스템에서는 이러한 재사용된 부분을 클래스 상속이라고 합니다. 동일한 부분이 클래스 A에 추가된 다음 클래스 B와 클래스 C가 A에서 상속되어 각각 고유한 것을 주장할 수 있습니다.
ECMAScript에는 클래스 개념이 없습니다. 그러나 재사용의 개념은 다르지 않으며(어떤 측면에서는 클래스보다 훨씬 유연함) 프로토타입 체인으로 구현될 수 있습니다. 이러한 종류의 상속을 위임 기반 상속, 더 대중적으로는 프로토타입 상속이라고 합니다.
ECMAScript에서는 클래스 "A", "B", "C"와 유사하게 객체 클래스 "a", "b", "c"를 생성하므로 객체 "a"는 객체 "를 소유합니다. b"와 "c"의 일부. 동시에 개체 "b"와 "c"에는 고유한 추가 속성이나 메서드만 포함됩니다.
그림 2. 프로토타입 체인
프로토타입 체인은 일반적으로 객체가 동일하거나 유사한 상태 구조(즉, 동일한 속성 집합)와 다른 상태 값을 갖는 상황에서 사용됩니다. 이 경우 생성자를 사용하여 지정된 패턴으로 개체를 만들 수 있습니다.
생성자
생성자는 객체를 생성하는 것 외에도 또 다른 유용한 작업을 수행합니다. 즉, 생성된 새 객체에 대한 프로토타입 객체를 자동으로 설정합니다. 프로토타입 객체는 ConstructorFunction.prototype 속성에 저장됩니다.
예를 들어 이전 예제를 다시 작성하고 생성자를 사용하여 "b" 및 "c" 객체를 생성하면 객체 "a"가 "Foo.prototype" 역할을 합니다.
그림 3. 생성자와 객체의 관계
위 다이어그램에서 볼 수 있듯이 각 개체에는 프로토타입이 있습니다. 생성자 Foo에는 자체 __proto__, 즉 Function.prototype이 있으며 Function.prototype의 __proto__는 Object.prototype을 가리킵니다. , Foo.prototype은 단지 명시적인 속성, 즉 b와 c의 __proto__ 속성입니다.
이 문제에 대한 완전하고 자세한 설명은 삼촌이 곧 번역할 18장과 19장에서 확인할 수 있습니다. 두 부분으로 구성됩니다. 객체 지향 프로그래밍. 다양한 객체 지향 패러다임과 스타일(OOP 패러다임 및 스타일)을 설명하는 일반 이론(OOP. 일반 이론)과 ECMAScript 구현(. OOP) . ECMAScript 구현), 특히 ECMAScript의 객체 지향 프로그래밍에 대해 설명합니다.
이제 기본적인 객체 원리를 이해했으니, ECMAScript의 프로그램 실행 환경[런타임 프로그램 실행]을 살펴보겠습니다. 이를 일반적으로 "실행 컨텍스트 스택"[실행 컨텍스트] 스택]이라고 합니다. 각 요소는 추상적으로 객체로 이해될 수 있습니다. 예, ECMAScript에서는 거의 모든 곳에서 객체를 볼 수 있다는 것을 발견했을 것입니다.
실행 컨텍스트 스택
ECMASscript에는 전역, 함수, 평가의 세 가지 유형의 코드가 있습니다.
각 코드 유형의 실행은 해당 컨텍스트에 따라 다릅니다. 물론 전역 컨텍스트에는 많은 함수 및 평가 인스턴스가 포함될 수 있습니다. 함수가 호출될 때마다 함수 실행 컨텍스트에 들어가 함수의 변수 값을 계산합니다. 평가 함수를 실행할 때마다 평가 실행 컨텍스트로 들어가 변수 값을 어디서 얻어야 하는지 결정합니다.
함수 호출(재귀 포함)이 새로운 컨텍스트를 생성하므로 함수는 무제한 컨텍스트를 생성할 수 있습니다.
그림 4. 실행 컨텍스트 스택
프로그램이 시작되면 먼저 스택의 맨 아래 요소이기도 한 전역 실행 컨텍스트[전역 실행 컨텍스트]로 들어갑니다. 이 전역 프로그램은 필요한 개체와 기능을 초기화하고 생성하기 시작합니다. 이 전역 컨텍스트를 실행하는 동안 일부 메서드(물론 이미 초기화됨)를 활성화한 다음 해당 컨텍스트 환경에 들어간 다음 새 요소를 스택에 푸시할 수 있습니다. 이러한 초기화가 완료된 후 시스템은 일부 이벤트(예: 사용자 마우스 클릭 등)를 기다리고 일부 메서드를 트리거한 다음 새 컨텍스트를 입력합니다.
그림 5를 참조하세요. 함수 컨텍스트 "EC1"과 글로벌 컨텍스트 "Global EC"가 있습니다. 다음 그림은 "Global EC"에서 "EC1"에 들어가고 나갈 때 스택의 변경 사항을 보여줍니다.
그림 5. 실행 컨텍스트 스택의 변화
ECMAScript 런타임 시스템이 코드 실행을 관리하는 방법은 다음과 같습니다.
ECMAScript 실행 컨텍스트 스택에 대한 자세한 내용은 이 튜토리얼 시리즈의 11장 실행 컨텍스트(실행 컨텍스트)를 참조하세요.
위에서 언급했듯이 스택의 각 실행 컨텍스트는 객체로 표현될 수 있습니다. 컨텍스트 개체의 구조와 해당 코드를 실행하는 데 필요한 상태를 살펴보겠습니다.
실행 컨텍스트
실행 컨텍스트는 추상적으로 객체로 이해될 수 있습니다. 각 실행 컨텍스트에는 연결된 코드의 실행 진행 상황을 추적하는 데 사용되는 속성 집합(컨텍스트 상태라고 함)이 있습니다. 이 다이어그램은 컨텍스트의 구조입니다.
그림 6. 컨텍스트 구조
이 세 가지 필수 속성(변수 객체(변수 객체), 이 포인터(이 값), 범위 체인(scope chain)) 외에도 실행 컨텍스트는 특정 구현에는 추가 속성이 있을 수도 있습니다. 다음으로 이 세 가지 속성에 대해 자세히 살펴보겠습니다.
변수 객체
변수 객체는 실행 컨텍스트와 관련된 데이터의 범위입니다.
컨텍스트와 연관된 특수 객체로 컨텍스트 내에서 정의되는 변수와 함수 선언을 저장합니다.
변수 객체는 실행 컨텍스트와 관련된 데이터의 범위입니다.
컨텍스트와 연관된 특수 객체로 컨텍스트에 정의된 변수 및 함수 선언을 저장하는 데 사용됩니다.
코드 복사
참고: 함수 표현식[함수 표현식](함수 선언[함수 선언, 차이점은 이 시리즈의 2장 참조] 대신)은 VO[변수 객체]에 포함되지 않습니다.
가변 객체는 다양한 맥락에서 다양한 객체의 사용을 나타내는 추상적인 개념입니다. 예를 들어, 전역 컨텍스트에서 변수 object는 전역 객체 자체이기도 합니다. (이것이 전역 개체의 속성을 통해 전역 변수를 가리킬 수 있는 이유입니다.)
다음 예에서 전역 실행 컨텍스트를 살펴보겠습니다.
그림 7. 전역 변수 객체
위에서 보듯이 "baz" 함수는 함수 표현식으로 사용될 경우 변수 객체에 포함되지 않습니다. 이것이 함수 외부에 액세스하려고 하면 ReferenceError가 발생하는 이유입니다. ECMAScript에서는 다른 언어(예: C/C++)와 비교하여 함수만 새 범위를 생성할 수 있다는 점에 유의하세요. 함수 내부에 정의된 변수 및 내부 함수는 외부에 직접 표시되지 않으며 전역 개체를 오염시키지 않습니다. eval을 사용할 때 새로운(eval에서 생성된) 실행 컨텍스트도 사용합니다. eval은 전역 변수 개체 또는 호출자의 변수 개체(eval 호출 소스)를 사용합니다.
함수와 그 자체의 변수 객체는 어떻습니까? 함수의 맥락에서 변수 객체는 활성화 객체로 표현됩니다.
활성화 개체
호출자가 함수를 활성화하면 이 특수 활성화 개체가 생성됩니다. 여기에는 일반 매개변수(형식 매개변수)와 특수 매개변수(인수) 객체(인덱싱된 속성이 있는 매개변수 매핑 테이블)가 포함됩니다. 활성 개체는 함수 컨텍스트에서 변수 개체로 사용됩니다.
즉, 함수의 변수 개체는 변경되지 않지만 저장 변수 및 함수 선언 외에도 특수 개체 인수도 포함됩니다.
다음 상황을 고려해보세요.
그림 8. 개체 활성화
같은 이유로 AO에는 함수 표현식이 포함되지 않습니다.
이 AO에 대한 자세한 내용은 이 튜토리얼 시리즈의 9장에서 확인할 수 있습니다.
다음으로 이야기할 내용은 세 번째 주요 대상입니다. 우리 모두 알고 있듯이 ECMAScript에서는 내부 함수[내부 함수]를 사용합니다. 이러한 내부 함수에서는 상위 함수 변수 또는 전역 변수를 참조할 수 있습니다. 이러한 변수 객체를 컨텍스트의 범위 객체라고 부릅니다. 위에서 설명한 프로토타입 체인과 유사하게 여기서는 이를 범위 체인이라고 부릅니다.
스코프 체인
스코프 체인은 컨텍스트의 코드에 나타나는 식별자를 검색하는 개체 목록입니다.
스코프 체인은 개체 목록입니다. ) 컨텍스트 코드에 나타나는 식별자를 검색합니다.
코드 복사
스코프 체인의 원리는 프로토타입 체인과 매우 유사합니다. 변수가 자체 스코프에 없으면 상위 수준까지 상위를 찾습니다.
식별자[식별자]는 변수 이름, 함수 선언 및 일반 매개변수로 이해될 수 있습니다. 예를 들어, 함수가 자체 함수 본문 내에서 변수를 참조해야 하지만 변수가 함수 내에서 선언되지 않은 경우(또는 매개변수 이름이 아닌 경우) 해당 변수를 자유 변수[자유 변수]라고 부를 수 있습니다. 그런 다음 범위 체인을 사용하여 이러한 자유 변수를 검색해야 합니다.
일반적으로 범위 체인에는 상위 변수 개체(범위 체인의 최상위), 함수 자체 변수 VO 및 활성화 개체가 포함됩니다. 그러나 어떤 경우에는 with 또는 catch 문과 같이 실행 중에 범위 체인에 동적으로 추가되는 등 다른 개체도 포함될 수 있습니다. [주석: with-objects는 with 문에 의해 생성된 임시 범위 개체를 참조하고, catch-clauses는 예외 개체를 생성하고 범위 변경을 발생시키는 catch(e)와 같은 catch 절을 참조합니다.]
식별자를 찾을 때는 범위 체인의 활성 개체 부분에서 시작한 다음(활성 개체에서 식별자를 찾을 수 없는 경우) 범위 체인의 맨 위에서 검색하는 식으로 진행됩니다. 액션처럼 도메인 체인처럼요.
그림 9. 스코프 체인
코드 실행 중에 with 또는 catch 문을 사용하면 범위 체인이 변경됩니다. 이러한 객체는 단순한 객체이며 프로토타입 체인도 갖습니다. 이 경우 범위 체인은 2차원으로 검색됩니다.
다음 예를 살펴보겠습니다.
그림 10. 확장된 범위 체인
모든 전역 개체가 Object.prototype에서 상속되는 것은 아닙니다. 위에서 설명한 상황은 SpiderMonkey에서 테스트할 수 있습니다.
모든 외부 함수의 변수 개체가 존재하는 한 내부 함수에서 외부 데이터를 참조하는 데 특별한 것은 없습니다. 범위 목록을 탐색하여 필요한 변수를 찾습니다. 그러나 위에서 언급한 것처럼 컨텍스트가 종료되면 해당 상태와 자체가 파괴되고 내부 함수가 외부 함수에서 반환됩니다. 게다가, 반환된 함수는 나중에 다른 컨텍스트에서 활성화될 수 있는데, 일부 자유 변수가 포함된 이전에 종료된 컨텍스트가 다시 활성화되면 어떻게 될까요? 일반적으로 이 문제를 해결하는 개념은 ECMAScript의 그것과 직접적으로 관련되어 있습니다. (어휘적) 클로저라고 합니다.
클로저
ECMAScript에서 함수는 "일류" 객체입니다. 이 용어는 함수가 다른 함수에 인수로 전달될 수 있음을 의미합니다. (이 경우 함수는 "funargs"라고 합니다 - "기능적 인수"의 약어입니다. [주석: 적절하게 함수적 인수로 번역되었는지는 모르겠습니다]) . "funargs"를 허용하는 함수를 고차 함수 또는 보다 수학적으로 연산자라고 합니다. 다른 함수의 런타임도 함수를 반환하며, 이렇게 반환된 함수를 함수 값 함수(함수 값이 있는 함수)라고 합니다.
"funargs"와 "함수값" 사이에는 두 가지 개념적 문제가 있습니다. 이 두 가지 하위 문제를 "Funarg 문제"("함수 매개변수 문제")라고 합니다. 기능적 매개변수의 문제를 정확하게 해결하기 위해서는 클로저(closure)의 개념이 도입되어야 합니다. 이 두 가지 문제를 자세히 설명하겠습니다(보시다시피 ECMAScript에서는 이 문제를 해결하기 위해 함수의 [[Scope]] 속성이 사용됩니다).
"funarg 문제"의 하위 문제는 "upward funarg 문제"입니다. [주석: 이는 다음과 같이 번역될 수 있습니다: 상향 검색 기능 매개변수 문제]. 이 문제는 함수가 다른 함수에서 외부로 복귀할 때 발생합니다. 외부 컨텍스트가 종료될 때 외부 컨텍스트의 변수에 액세스할 수 있으려면 내부 함수가 생성 시(생성 순간) [[Scope]] 특성의 상위 요소 범위에 이를 저장해야 합니다. 그러면 함수가 활성화되면 컨텍스트의 범위 체인이 [[Scope]] 속성과 결합된 활성화 개체로 나타납니다(실제로 위 그림에서 볼 수 있습니다).
범위 체인 = 활성화 개체 [[ Scope]]
스코프 체인 = 활성 객체 [[Scope]]
가장 중요한 점은 이 저장된 역할로 인해 함수가 생성될 때 외부 스코프를 저장한다는 것입니다. 범위 체인은 향후 함수 호출에서 변수 조회에 사용됩니다.
그림 11. 공유 [[범위]]
위 그림은 루프에서 여러 함수를 생성할 때 혼동을 일으킬 수 있습니다. 생성된 함수에 루프 변수(예: "k")를 사용하면 모든 함수가 동일한 루프 변수를 사용하므로 일부 프로그래머는 종종 예상 값을 얻지 못하는 경우가 있습니다. 이제 그러한 문제가 발생하는 이유가 분명해졌습니다. 모든 함수가 동일한 [[Scope]]를 공유하기 때문입니다. 여기서 루프 변수는 마지막 복합 할당입니다.