자바스크립트 시리즈 심층이해(10) 자바스크립트 핵심(고급 전문가 필독)_자바스크립트 스킬

WBOY
풀어 주다: 2016-05-16 17:56:59
원래의
988명이 탐색했습니다.

적합한 독자: 숙련된 개발자, 전문 프론트엔드 직원.

원저자: 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와 같은 일부 스크립트 엔진의 프로토타입 개념을 구체적으로 구현한 것임) ).


var foo = {
x: 10,
y: 20
};


위 코드 foo 객체에는 두 개의 명시적 속성(명시적 자체 속성)과 암시적 __proto__ 속성[암시적 __proto__ 속성]이 있습니다. foo의 프로토타입.


그림 1. 프로토타입이 있는 기본 객체

프로토타입이 왜 필요한가요? 이 질문에 답하기 위해 프로토타입 체인의 개념을 생각해 보겠습니다.

프로토타입 체인
프로토타입 객체도 일반 객체이며, 자체 프로토타입을 가질 수도 있습니다. 프로토타입 객체의 프로토타입이 null이 아닌 경우 이를 프로토타입 체인(프로토타입 체인)이라고 합니다. ).
프로토타입 체인은 상속 및 공유 속성을 구현하는 데 사용되는 유한한 개체 체인입니다.

이런 상황을 상상해 보세요. 2개의 객체가 있고 대부분의 내용은 동일하지만 작은 부분만 다릅니다. 분명히 좋은 디자인 패턴에서는 동일한 부분을 재사용해야 합니다. 모든 객체에 동일한 메서드나 속성을 반복적으로 정의하지 마세요. 클래스 기반 시스템에서는 이러한 재사용된 부분을 클래스 상속이라고 합니다. 동일한 부분이 클래스 A에 추가된 다음 클래스 B와 클래스 C가 A에서 상속되어 각각 고유한 것을 주장할 수 있습니다.

ECMAScript에는 클래스 개념이 없습니다. 그러나 재사용의 개념은 다르지 않으며(어떤 측면에서는 클래스보다 훨씬 유연함) 프로토타입 체인으로 구현될 수 있습니다. 이러한 종류의 상속을 위임 기반 상속, 더 대중적으로는 프로토타입 상속이라고 합니다.

ECMAScript에서는 클래스 "A", "B", "C"와 유사하게 객체 클래스 "a", "b", "c"를 생성하므로 객체 "a"는 객체 "를 소유합니다. b"와 "c"의 일부. 동시에 개체 "b"와 "c"에는 고유한 추가 속성이나 메서드만 포함됩니다.



var a = { x: 10, 계산: function (z) { return this.x this.y z }}; var b = { y: 20, __proto__: a} var c = { y: 30, __proto__: a}; 메소드 b .calculate(30); // 60c.calculate(40) // 80


아주 간단해 보이지 않나요? b와 c는 프로토타입 체인[prototype chain]에 의해 구현되는 a에 정의된 계산 메소드를 사용할 수 있습니다.

원리는 매우 간단합니다. 객체 b에서 계산 메소드를 찾을 수 없는 경우(즉, 객체 b에 계산 속성이 없는 경우) 프로토타입 체인을 따라 검색이 시작됩니다. 이 계산 메서드가 b의 프로토타입에서 발견되지 않으면 a의 프로토타입이 프로토타입 체인을 따라 발견되고 전체 프로토타입 체인이 순회됩니다. 일단 발견되면 발견된 첫 번째 속성이나 메서드가 반환된다는 점을 기억하십시오. 따라서 발견된 첫 번째 속성이 상속 속성이 됩니다. 전체 프로토타입 체인을 순회했지만 여전히 찾을 수 없으면 정의되지 않은 값이 반환됩니다.

상속 메커니즘에서 this 값은 프로토타입 체인에서 발견되었을 때 속한 객체가 아니라 원래 속한 객체를 가리킨다는 점에 유의하세요. 예를 들어, 위의 예에서 this.y는 a가 아닌 b와 c에서 얻어집니다. 물론 this.x가 프로토타입 체인 메커니즘을 통해 발견되기 때문에 a에서 가져온 것임을 발견했습니다.

객체의 프로토타입이 명시적으로 선언되거나 정의되지 않은 경우 __prototype__의 기본값은 object.prototype이고 object.prototype도 프로토타입 체인의 끝인 __prototype__을 갖습니다. , null로 설정됩니다.

아래 그림은 위의 a,b,c의 상속관계를 보여줍니다

그림 2. 프로토타입 체인

프로토타입 체인은 일반적으로 객체가 동일하거나 유사한 상태 구조(즉, 동일한 속성 집합)와 다른 상태 값을 갖는 상황에서 사용됩니다. 이 경우 생성자를 사용하여 지정된 패턴으로 개체를 만들 수 있습니다.
생성자
생성자는 객체를 생성하는 것 외에도 또 다른 유용한 작업을 수행합니다. 즉, 생성된 새 객체에 대한 프로토타입 객체를 자동으로 설정합니다. 프로토타입 객체는 ConstructorFunction.prototype 속성에 저장됩니다.

예를 들어 이전 예제를 다시 작성하고 생성자를 사용하여 "b" 및 "c" 객체를 생성하면 객체 "a"가 "Foo.prototype" 역할을 합니다.

코드 복사 코드는 다음과 같습니다.

//Constructor
function Foo(y) {
//구성 이 함수는 특정 모드에서 객체를 생성합니다. 생성된 객체는 "y" 속성을 갖습니다.
this.y = y
}

// "Foo.prototype" 새 객체의 프로토타입 참조를 저장합니다
// 이를 사용하여 상속과 공유 속성 또는 메서드를 정의할 수 있습니다
// 위의 예와 같이 다음 코드가 있습니다.

// 상속된 속성" x"
Foo.prototype.x = 10;

// 상속된 메서드 "calculate"
Foo.prototype.calculate = function(z) {
return this.x this.y z
};

// foo 패턴을 사용하여 "b" 및 "c" 생성
var b = new Foo(20)
var c = new Foo(30) );

// 상속된 메서드 호출
b.calculate(30); // 60
c.calculate(40); // 80

// 사용된 예상 속성

console.log(

b.__proto__ === Foo.prototype, // true
c.__proto__ === Foo.prototype, // true

// "Foo.prototype"은 "constructor"라는 특수 속성을 자동으로 생성합니다.
// a 자체의 생성자를 가리킵니다.
// 인스턴스 "b" 및 "c"는 Find it을 통해 인증될 수 있습니다. 그리고 이를 사용하여 자신의 생성자를 테스트하세요

b.constructor === Foo, // true
c.constructor === Foo, // true
Foo.prototype.constructor == = Foo // true

b.calculate === b.__proto__.calculate, // true
b.__proto__.calculate === Foo.prototype.calculate // true

);

위 코드는 다음과 같은 관계로 표현할 수 있습니다.

그림 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에는 전역, 함수, 평가의 세 가지 유형의 코드가 있습니다.

각 코드 유형의 실행은 해당 컨텍스트에 따라 다릅니다. 물론 전역 컨텍스트에는 많은 함수 및 평가 인스턴스가 포함될 수 있습니다. 함수가 호출될 때마다 함수 실행 컨텍스트에 들어가 함수의 변수 값을 계산합니다. 평가 함수를 실행할 때마다 평가 실행 컨텍스트로 들어가 변수 값을 어디서 얻어야 하는지 결정합니다.

함수 호출(재귀 포함)이 새로운 컨텍스트를 생성하므로 함수는 무제한 컨텍스트를 생성할 수 있습니다.

코드 복사 코드는 다음과 같습니다.

function foo(bar) {}
// 동일한 함수를 호출하면 매번 3개의 다른 컨텍스트가 생성됩니다.
//(매개변수 bar 값과 같은 다른 상태 포함)

foo(10)
foo(20); ;
foo(30)

실행 컨텍스트는 함수가 다른 함수를 호출하는 것처럼(또는 전역 컨텍스트가 전역 함수를 호출하는 것처럼) 다른 컨텍스트를 활성화한 다음 이를 계층별로 호출할 수 있습니다. 논리적으로 말하자면, 이 구현은 스택이며, 이를 컨텍스트 스택이라고 부를 수 있습니다.

다른 컨텍스트를 활성화하는 컨텍스트를 호출자라고 합니다. 활성화된 컨텍스트를 호출 수신자라고 합니다. 피호출자는 호출자가 될 수도 있습니다(예를 들어 전역 컨텍스트에서 호출되는 함수는 자체 내부 메서드 중 일부를 호출합니다).

호출자가 호출 수신자를 활성화하면 호출자는 자신의 실행을 일시 중단한 다음 호출 수신자에게 제어권을 넘겨줍니다. 따라서 호출 수신자는 실행 중인 컨텍스트[실행 중인 컨텍스트]/활성 실행이라고 하는 스택에 놓입니다. context]. 이 호출 수신자의 컨텍스트가 종료되면 제어가 다시 호출자에게 넘겨지고 호출자는 일시 중지된 위치에서 실행을 계속합니다. 이 호출자가 종료된 후에도 다른 컨텍스트가 계속해서 트리거됩니다. 호출 수신자는 예외를 반환하거나 발생시켜 자체 컨텍스트를 종료할 수 있습니다.

아래와 같이 모든 ECMAScript 프로그램 실행은 실행 컨텍스트 스택[Execution Context(EC) 스택]으로 간주할 수 있습니다. 스택의 맨 위는 활성 컨텍스트입니다.

그림 4. 실행 컨텍스트 스택

프로그램이 시작되면 먼저 스택의 맨 아래 요소이기도 한 전역 실행 컨텍스트[전역 실행 컨텍스트]로 들어갑니다. 이 전역 프로그램은 필요한 개체와 기능을 초기화하고 생성하기 시작합니다. 이 전역 컨텍스트를 실행하는 동안 일부 메서드(물론 이미 초기화됨)를 활성화한 다음 해당 컨텍스트 환경에 들어간 다음 새 요소를 스택에 푸시할 수 있습니다. 이러한 초기화가 완료된 후 시스템은 일부 이벤트(예: 사용자 마우스 클릭 등)를 기다리고 일부 메서드를 트리거한 다음 새 컨텍스트를 입력합니다.

그림 5를 참조하세요. 함수 컨텍스트 "EC1"과 글로벌 컨텍스트 "Global EC"가 있습니다. 다음 그림은 "Global EC"에서 "EC1"에 들어가고 나갈 때 스택의 변경 사항을 보여줍니다.

그림 5. 실행 컨텍스트 스택의 변화

ECMAScript 런타임 시스템이 코드 실행을 관리하는 방법은 다음과 같습니다.

ECMAScript 실행 컨텍스트 스택에 대한 자세한 내용은 이 튜토리얼 시리즈의 11장 실행 컨텍스트(실행 컨텍스트)를 참조하세요.

위에서 언급했듯이 스택의 각 실행 컨텍스트는 객체로 표현될 수 있습니다. 컨텍스트 개체의 구조와 해당 코드를 실행하는 데 필요한 상태를 살펴보겠습니다.
실행 컨텍스트
실행 컨텍스트는 추상적으로 객체로 이해될 수 있습니다. 각 실행 컨텍스트에는 연결된 코드의 실행 진행 상황을 추적하는 데 사용되는 속성 집합(컨텍스트 상태라고 함)이 있습니다. 이 다이어그램은 컨텍스트의 구조입니다.

그림 6. 컨텍스트 구조

이 세 가지 필수 속성(변수 객체(변수 객체), 이 포인터(이 값), 범위 체인(scope chain)) 외에도 실행 컨텍스트는 특정 구현에는 추가 속성이 있을 수도 있습니다. 다음으로 이 세 가지 속성에 대해 자세히 살펴보겠습니다.

변수 객체
변수 객체는 실행 컨텍스트와 관련된 데이터의 범위입니다.
컨텍스트와 연관된 특수 객체로 컨텍스트 내에서 정의되는 변수와 함수 선언을 저장합니다.

변수 객체는 실행 컨텍스트와 관련된 데이터의 범위입니다.
컨텍스트와 연관된 특수 객체로 컨텍스트에 정의된 변수 및 함수 선언을 저장하는 데 사용됩니다.
코드 복사
참고: 함수 표현식[함수 표현식](함수 선언[함수 선언, 차이점은 이 시리즈의 2장 참조] 대신)은 VO[변수 객체]에 포함되지 않습니다.

가변 객체는 다양한 맥락에서 다양한 객체의 사용을 나타내는 추상적인 개념입니다. 예를 들어, 전역 컨텍스트에서 변수 object는 전역 객체 자체이기도 합니다. (이것이 전역 개체의 속성을 통해 전역 변수를 가리킬 수 있는 이유입니다.)

다음 예에서 전역 실행 컨텍스트를 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다. 다음과 같습니다:

var foo = 10;

function bar() {} // // 함수 선언
(function baz() {}) // 함수 표현식

console.log(
this.foo == foo, // true
window.bar == bar // true
); (baz); // 참조 오류, baz가 정의되지 않았습니다


전역 컨텍스트의 변수 개체(VO)에는 다음 속성이 있습니다.

그림 7. 전역 변수 객체

위에서 보듯이 "baz" 함수는 함수 표현식으로 사용될 경우 변수 객체에 포함되지 않습니다. 이것이 함수 외부에 액세스하려고 하면 ReferenceError가 발생하는 이유입니다. ECMAScript에서는 다른 언어(예: C/C++)와 비교하여 함수만 새 범위를 생성할 수 있다는 점에 유의하세요. 함수 내부에 정의된 변수 및 내부 함수는 외부에 직접 표시되지 않으며 전역 개체를 오염시키지 않습니다. eval을 사용할 때 새로운(eval에서 생성된) 실행 컨텍스트도 사용합니다. eval은 전역 변수 개체 또는 호출자의 변수 개체(eval 호출 소스)를 사용합니다.

함수와 그 자체의 변수 객체는 어떻습니까? 함수의 맥락에서 변수 객체는 활성화 객체로 표현됩니다.
활성화 개체
호출자가 함수를 활성화하면 이 특수 활성화 개체가 생성됩니다. 여기에는 일반 매개변수(형식 매개변수)와 특수 매개변수(인수) 객체(인덱싱된 속성이 있는 매개변수 매핑 테이블)가 포함됩니다. 활성 개체는 함수 컨텍스트에서 변수 개체로 사용됩니다.

즉, 함수의 변수 개체는 변경되지 않지만 저장 변수 및 함수 선언 외에도 특수 개체 인수도 포함됩니다.

다음 상황을 고려해보세요.

코드 복사 코드는 다음과 같습니다.

function foo(x, y) {
var z = 30;
function bar() {} // 함수 선언
(function baz() {}) // 함수 표현식
}
foo(10, 20);

"foo" 함수 컨텍스트의 다음 활성화 개체(AO)는 다음과 같습니다.

그림 8. 개체 활성화

같은 이유로 AO에는 함수 표현식이 포함되지 않습니다.

이 AO에 대한 자세한 내용은 이 튜토리얼 시리즈의 9장에서 확인할 수 있습니다.

다음으로 이야기할 내용은 세 번째 주요 대상입니다. 우리 모두 알고 있듯이 ECMAScript에서는 내부 함수[내부 함수]를 사용합니다. 이러한 내부 함수에서는 상위 함수 변수 또는 전역 변수를 참조할 수 있습니다. 이러한 변수 객체를 컨텍스트의 범위 객체라고 부릅니다. 위에서 설명한 프로토타입 체인과 유사하게 여기서는 이를 범위 체인이라고 부릅니다.
스코프 체인
스코프 체인은 컨텍스트의 코드에 나타나는 식별자를 검색하는 개체 목록입니다.
스코프 체인은 개체 목록입니다. ) 컨텍스트 코드에 나타나는 식별자를 검색합니다.
코드 복사
스코프 체인의 원리는 프로토타입 체인과 매우 유사합니다. 변수가 자체 스코프에 없으면 상위 수준까지 상위를 찾습니다.

식별자[식별자]는 변수 이름, 함수 선언 및 일반 매개변수로 이해될 수 있습니다. 예를 들어, 함수가 자체 함수 본문 내에서 변수를 참조해야 하지만 변수가 함수 내에서 선언되지 않은 경우(또는 매개변수 이름이 아닌 경우) 해당 변수를 자유 변수[자유 변수]라고 부를 수 있습니다. 그런 다음 범위 체인을 사용하여 이러한 자유 변수를 검색해야 합니다.

일반적으로 범위 체인에는 상위 변수 개체(범위 체인의 최상위), 함수 자체 변수 VO 및 활성화 개체가 포함됩니다. 그러나 어떤 경우에는 with 또는 catch 문과 같이 실행 중에 범위 체인에 동적으로 추가되는 등 다른 개체도 포함될 수 있습니다. [주석: with-objects는 with 문에 의해 생성된 임시 범위 개체를 참조하고, catch-clauses는 예외 개체를 생성하고 범위 변경을 발생시키는 catch(e)와 같은 catch 절을 참조합니다.]

식별자를 찾을 때는 범위 체인의 활성 개체 부분에서 시작한 다음(활성 개체에서 식별자를 찾을 수 없는 경우) 범위 체인의 맨 위에서 검색하는 식으로 진행됩니다. 액션처럼 도메인 체인처럼요.

코드 복사 코드는 다음과 같습니다.

var x = 10; 🎜>( function foo() {
var y = 20;
(function bar() {
var z = 30;
// "x"와 "y"는 자유 변수입니다
// 범위 체인의 다음 객체에서 발견됩니다(함수 "bar"의 대화형 객체 뒤)
console.log(x y z)
})()
})() ;


범위 체인의 객체 연결은 범위 체인의 다음 객체를 가리키는 __parent__ 속성을 통해 이루어진다고 가정합니다. Rhino 코드에서 이 프로세스를 테스트할 수 있습니다. 이 기술은 실제로 ES5 환경에서 구현됩니다(외부 링크라고 함). 물론 간단한 데이터를 사용하여 이 모델을 시뮬레이션할 수도 있습니다. __parent__ 개념을 사용하여 위 코드를 다음 상황으로 시연할 수 있습니다. (따라서 상위 변수는 함수의 [[Scope]] 속성에 저장됩니다.)

그림 9. 스코프 체인

코드 실행 중에 with 또는 catch 문을 사용하면 범위 체인이 변경됩니다. 이러한 객체는 단순한 객체이며 프로토타입 체인도 갖습니다. 이 경우 범위 체인은 2차원으로 검색됩니다.

  1. 먼저 원래 범위 체인에서
  2. 각 링크 포인트의 범위 체인(이 링크 포인트에 프로토타입이 있는 경우)

다음 예를 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.

Object.prototype.x = 10;

var w = 20;
var y = 30;

// SpiderMonkey 전역 객체에서
// 전역 컨텍스트의 변수 객체 "Object.prototype"에서 상속됩니다
// "선언되지 않은 전역 변수"를 얻을 수 있습니다
// 프로토타입 체인에서 얻을 수 있기 때문입니다

console.log(x) ; // 10

(function foo() {

// "foo"는 지역 변수입니다
var w = 40;
var x = 100;

// "x"는 "Object.prototype"에서 얻을 수 있습니다. 값은 10입니다.
// {z: 50}이 여기에서 상속되기 때문입니다

with ({z: 50 }) {
console.log(w, x, y, z) // 40, 10, 30, 50
}

// "with " 객체는 범위 체인에서 삭제됩니다. 그 후
// foo의 컨텍스트에서 x를 다시 얻을 수 있습니다. 이번에는 값이 100으로 반환되었습니다.
// "w"도 지역 변수입니다.
console.log(x, w); / / 100, 40

// 브라우저에서
// 다음 명령문을 통해 전역 w 값을 얻을 수 있습니다
console.log (window.w); // 20

})();
다음과 같은 구조 다이어그램을 갖게 됩니다. 즉, __parent__를 검색하기 전에 먼저 __proto__에 대한 링크로 이동합니다.

그림 10. 확장된 범위 체인

모든 전역 개체가 Object.prototype에서 상속되는 것은 아닙니다. 위에서 설명한 상황은 SpiderMonkey에서 테스트할 수 있습니다.

모든 외부 함수의 변수 개체가 존재하는 한 내부 함수에서 외부 데이터를 참조하는 데 특별한 것은 없습니다. 범위 목록을 탐색하여 필요한 변수를 찾습니다. 그러나 위에서 언급한 것처럼 컨텍스트가 종료되면 해당 상태와 자체가 파괴되고 내부 함수가 외부 함수에서 반환됩니다. 게다가, 반환된 함수는 나중에 다른 컨텍스트에서 활성화될 수 있는데, 일부 자유 변수가 포함된 이전에 종료된 컨텍스트가 다시 활성화되면 어떻게 될까요? 일반적으로 이 문제를 해결하는 개념은 ECMAScript의 그것과 직접적으로 관련되어 있습니다. (어휘적) 클로저라고 합니다.
클로저
ECMAScript에서 함수는 "일류" 객체입니다. 이 용어는 함수가 다른 함수에 인수로 전달될 수 있음을 의미합니다. (이 경우 함수는 "funargs"라고 합니다 - "기능적 인수"의 약어입니다. [주석: 적절하게 함수적 인수로 번역되었는지는 모르겠습니다]) . "funargs"를 허용하는 함수를 고차 함수 또는 보다 수학적으로 연산자라고 합니다. 다른 함수의 런타임도 함수를 반환하며, 이렇게 반환된 함수를 함수 값 함수(함수 값이 있는 함수)라고 합니다.

"funargs"와 "함수값" 사이에는 두 가지 개념적 문제가 있습니다. 이 두 가지 하위 문제를 "Funarg 문제"("함수 매개변수 문제")라고 합니다. 기능적 매개변수의 문제를 정확하게 해결하기 위해서는 클로저(closure)의 개념이 도입되어야 합니다. 이 두 가지 문제를 자세히 설명하겠습니다(보시다시피 ECMAScript에서는 이 문제를 해결하기 위해 함수의 [[Scope]] 속성이 사용됩니다).

"funarg 문제"의 하위 문제는 "upward funarg 문제"입니다. [주석: 이는 다음과 같이 번역될 수 있습니다: 상향 검색 기능 매개변수 문제]. 이 문제는 함수가 다른 함수에서 외부로 복귀할 때 발생합니다. 외부 컨텍스트가 종료될 때 외부 컨텍스트의 변수에 액세스할 수 있으려면 내부 함수가 생성 시(생성 순간) [[Scope]] 특성의 상위 요소 범위에 이를 저장해야 합니다. 그러면 함수가 활성화되면 컨텍스트의 범위 체인이 [[Scope]] 속성과 결합된 활성화 개체로 나타납니다(실제로 위 그림에서 볼 수 있습니다).

범위 체인 = 활성화 개체 [[ Scope]]
스코프 체인 = 활성 객체 [[Scope]]

가장 중요한 점은 이 저장된 역할로 인해 함수가 생성될 때 외부 스코프를 저장한다는 것입니다. 범위 체인은 향후 함수 호출에서 변수 조회에 사용됩니다.

코드 복사 코드는 다음과 같습니다.

function foo() {
var x = 10 ;
return function bar() {
console.log(x);
}

// "foo"도 함수를 반환합니다. // 그리고 이 반환된 함수는 내부 변수 x를 자유롭게 사용할 수 있습니다.

var returnFunction = foo()

// 전역 변수 "x"
var x = 20;
// 반환 함수 지원
returnedFunction(); // 결과는 20이 아닌 10입니다.


이러한 형태의 범위를 정적 범위[정적/어휘 범위]라고 합니다. 위의 x 변수는 함수 표시줄의 [[Scope]]에서 찾을 수 있습니다. 이론적으로는 동적 범위[동적 범위]도 있을텐데, 즉 위의 x는 10이 아닌 20으로 해석됩니다. 하지만 EMCAScript는 동적 범위를 사용하지 않습니다.
"funarg 문제"의 또 다른 유형은 top-down["downward funarg 문제"]입니다. 이 경우 부모의 위쪽과 아래쪽이 존재하지만 변수의 값을 판단할 때 모호성이 있습니다. . 즉, 이 변수는 어떤 범위를 사용해야 합니까? 함수가 생성될 때의 범위인가요, 아니면 실행될 때의 범위인가요? 이러한 모호함을 피하기 위해 클로저, 즉 정적 범위를 사용할 수 있습니다.

아래 예시를 참고하세요.





코드 복사 코드는 다음과 같습니다. : // 전역 변수 "x"
var x = 10

// 전역 함수
function foo() {
console.log( x);
}

(function (funArg) {

// 지역 변수 "x"
var x = 20;

// 모호하지 마세요
// "foo" 함수의 [[Scope]]에 저장된 전역 변수 "x"를 사용하기 때문에
// 호출자 범위의 "x"가 아닙니다

funArg(); // 20이 아니라 10

})(foo) // foo를 "funarg"로 전달


위의 상황에서 우리는 정적 범위를 사용하는 것이 언어 클로저의 필수 요구 사항이라는 결론을 내릴 수 있는 것 같습니다. 그러나 일부 언어에서는 동적 및 정적 범위 지정의 조합이 제공되므로 개발자가 사용할 범위를 선택할 수 있습니다. 하지만 ECMAScript에서는 정적 범위만 사용됩니다. 따라서 ECMAScript는 [[Scope]] 속성 사용을 완벽하게 지원합니다. 클로저에 대해 다음과 같은 정의를 얻을 수 있습니다.

클로저는 코드 블록(ECMAScript에서는 함수임)과 정적으로/어휘적으로 저장된 모든 상위 범위의 조합입니다.
따라서 이러한 저장된 범위를 통해. 함수는 자유 변수를 쉽게 참조할 수 있습니다.
클로저는 일련의 코드 블록(ECMAScript의 함수)이며 모든 상위 항목의 범위를 정적으로 보유합니다. 함수 내의 자유 변수는 저장된 범위를 통해 검색됩니다.
코드 복사
모든 일반 함수는 생성 시 [[Scope]]를 저장하므로 이론적으로 ECMAScript의 모든 함수는 클로저입니다.

또 다른 매우 중요한 점은 여러 함수가 동일한 상위 범위를 가질 수 있다는 것입니다. 이는 매우 일반적인 상황입니다. 예를 들어 여러 내부 또는 전역 함수가 있습니다. 이 경우 [[Scope]]에 존재하는 변수는 공유됩니다. 한 클로저의 변수를 변경하면 다른 클로저에도 영향을 미칩니다.
코드 복사 코드는 다음과 같습니다.

function baz() {
var x = 1 ;
return {
foo: function foo() { return x },
bar: function bar() { return --x }
}

var closures = baz();

console.log(
closures.foo(), // 2
closures.bar() // 1
);

위 코드는 다음 그림으로 표현할 수 있습니다.

그림 11. 공유 [[범위]]

위 그림은 루프에서 여러 함수를 생성할 때 혼동을 일으킬 수 있습니다. 생성된 함수에 루프 변수(예: "k")를 사용하면 모든 함수가 동일한 루프 변수를 사용하므로 일부 프로그래머는 종종 예상 값을 얻지 못하는 경우가 있습니다. 이제 그러한 문제가 발생하는 이유가 분명해졌습니다. 모든 함수가 동일한 [[Scope]]를 공유하기 때문입니다. 여기서 루프 변수는 마지막 복합 할당입니다.


코드 복사 코드는 다음과 같습니다.
var data = [];
for (var k = 0; k data[k] = function () {
alert(k)
}

data[0](); // 3이지만 0은 아님
data[1](); // 3이지만 1은 아님
data[2]() // 3이지만 0은 아님 2


이러한 문제를 해결하는 몇 가지 기술이 있습니다. 한 가지 기술은 함수 추가와 같이 범위 체인에 추가 개체를 제공하는 것입니다.



코드 복사 코드 var data = [];
for (var k = 0; k < 3; k ) {
data[k] = (함수 ( x) {
return function () {
alert(x);
})(k) // k를 매개변수로 전달

// 결과는 정확합니다
data[0](); // 0
data[1]() // 1
data[2]() // 2



클로저 이론에 대한 심층적인 연구와 구체적인 실습은 이 튜토리얼 시리즈의 16장인 클로저에서 확인할 수 있습니다. 범위 체인에 대한 자세한 내용을 보려면 이 자습서 시리즈의 14장인 범위 체인(범위 체인)을 참조하세요.

다음 장에서는 실행 컨텍스트의 마지막 속성인 이 포인터의 개념에 대해 논의합니다.

이 포인터
이 값은 실행 컨텍스트와 관련된 특수 개체입니다.
따라서 컨텍스트 개체(즉, 실행 컨텍스트가 있는 개체)라고 명명할 수 있습니다. 활성화됨)
실행 컨텍스트와 밀접한 관련이 있는 특수 개체입니다. 따라서 컨텍스트 객체(실행 컨텍스트를 활성화하는 컨텍스트)라고도 할 수 있습니다.
코드 복사
모든 객체를 컨텍스트의 this 값으로 사용할 수 있습니다. 나는 ECMAScript의 실행 컨텍스트에 관한 몇 가지 오해, 특히 이것에 대해 다시 한 번 명확히 하고 싶습니다. 종종 이는 변수 개체의 속성으로 잘못 설명됩니다. 예를 들어 나는 최근에 이 책에서 그것을 발견했습니다(비록 책에서 이것을 언급하는 장은 나쁘지 않지만). 기억하세요:

이 값은 실행 컨텍스트의 속성이지만 변수 개체의 속성은 아닙니다.
이것은 실행 컨텍스트의 속성이지만 변수 개체의 속성은 아닙니다. 🎜>코드 복사
변수와 달리 변수를 검색하는 것과 유사한 프로세스가 없기 때문에 이 기능은 매우 중요합니다. 코드에서 이것을 사용하면 범위 체인에서 검색하지 않고 실행 컨텍스트에서 직접 this 값을 얻습니다. 이 값은 컨텍스트에 들어갈 때의 상황에 따라서만 달라집니다.

그런데, Python에는 ECMAScript와 달리 self 매개변수가 있는데 이와 유사하지만 실행 중에 변경될 수 있습니다. ECMAScript에서는 변수가 아니기 때문에 여기에 값을 할당할 수 없습니다.

글로벌 컨텍스트(global context)에서 this의 값은 전역 객체를 참조하는데, 이는 this의 값이 변수 자체라는 뜻입니다.

코드 복사 코드는 다음과 같습니다.
var x = 10; 🎜>console .log(
x, // 10
this.x, // 10
window.x // 10
)


함수 컨텍스트에서 [함수 컨텍스트 ] 이는 각 함수 호출에 따라 다른 값이 될 수 있으며, 호출자는 각 호출자 표현식 [호출 표현식]을 호출하여 생성됩니다(즉, 이 함수가 어떻게 활성화되고 호출되는지). . 예를 들어, 다음 예에서 foo는 전역 컨텍스트에서 활성화된 호출 수신자입니다. 다음 예에서는 호출자에 따라 발생하는 차이를 보여줍니다.


코드 복사 코드는 다음과 같습니다. // "foo"에 있는 경고 함수는 변경되지 않았습니다
// 하지만 활성화될 때마다 다릅니다

function foo() {
alert(this)
}

// caller "foo"를 활성화합니다.
// 이 호출자에게 "this"를 제공합니다.

foo() // 전역 객체
foo.prototype.constructor() // foo.prototype

var bar = {
baz: foo
}

bar.baz() // 바

(bar.baz)(); // 또한 bar
(bar.baz = bar.baz)(); // 이것은 전역 객체입니다
(bar.baz, bar.baz)() // 또한 전역 객체입니다
(false || bar.baz)(); // 전역 객체이기도 합니다

var otherFoo = bar.baz;
otherFoo(); // 여전히 전역 객체입니다


더 깊이 생각하고 싶다면 각 함수 호출에서 this의 값이 어떻게 변경되는지(더 중요하게는 어떻게 변경되는지)에 대한 이 튜토리얼 시리즈의 10장을 읽어보세요. 위에서 언급한 상황에 대해서는 이 장에서 자세히 설명합니다.
결론
여기까지 간략한 개요를 마쳤습니다. 그렇게 간단해 보이지 않을 수도 있지만 이러한 주제를 완전히 다루려면 책 전체가 필요할 것입니다. 우리가 다루지 않은 두 가지 중요한 주제가 있습니다: 함수(함수 선언 및 함수 표현식과 같은 다양한 유형의 함수 간의 차이점)와 ECMAScript의 평가 전략입니다. 이 두 주제는 각각 이 튜토리얼 시리즈의 15장 함수와 19장 평가 전략(평가 전략)에서 찾을 수 있습니다.

의견, 질문, 추가 사항이 있는 경우 기사 댓글을 통해 논의해 주시기 바랍니다.

모두에게 ECMAScript를 배우는데 행운이 있기를 바랍니다.
관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿