>웹 프론트엔드 >JS 튜토리얼 >자바스크립트 시리즈의 심층적인 이해(14) 스코프체인(Scope Chain) 소개_javascript 스킬

자바스크립트 시리즈의 심층적인 이해(14) 스코프체인(Scope Chain) 소개_javascript 스킬

WBOY
WBOY원래의
2016-05-16 17:54:281145검색
서문
12장의 변수 객체 설명에서 우리는 이미 실행 컨텍스트의 데이터(변수, 함수 선언, 함수 매개변수)가 변수 객체에 속성으로 저장된다는 것을 알고 있었습니다.
동시에 변수 개체가 컨텍스트에 들어갈 때마다 생성되고 초기 값으로 채워지는 것도 코드 실행 단계에서 발생한다는 것을 알고 있습니다.
이 장에서는 실행 컨텍스트와 직접적으로 관련된 자세한 내용을 논의하는 데 전념하고 있으며 이번에는 범위 체인이라는 주제를 언급하겠습니다.
영어 원문: http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/
중국어 참고: http://www.denisdeng.com/?p=908
본 기사 내용의 일부는 위 주소에서 왔으며 약간만 수정했습니다.
정의
에게 감사드립니다. 핵심 사항을 간략하게 설명하고 표시하려면 스코프 체인이 대부분 내부 기능과 관련되어 있습니다. .
우리는 ECMAScript가 내부 함수 생성을 허용하고 상위 함수에서 이러한 함수를 반환할 수도 있다는 것을 알고 있습니다.
코드 복사 코드는 다음과 같습니다.

var x = 10; foo() {
var y = 20;
function bar() {
alert(x y)
}
return bar;
}
foo()() ; // 30

이렇게 하면 각 컨텍스트에 고유한 변수 개체가 있다는 것이 분명해집니다. 전역 컨텍스트의 경우 이는 전역 개체 자체이고, 함수의 경우 활성 개체입니다.
스코프 체인은 내부 컨텍스트에 있는 모든 변수 개체(상위 변수 개체 포함)의 목록입니다. 이 체인은 변수 쿼리에 사용됩니다. 즉, 위의 예에서 "bar" 컨텍스트의 범위 체인에는 AO(bar), AO(foo) 및 VO(global)가 포함됩니다.
그런데 이 문제를 좀 더 자세히 살펴보겠습니다.
정의부터 시작하여 예시를 더 자세히 논의해 보겠습니다.
스코프 체인은 실행 컨텍스트와 연결되어 있으며 변수 개체 체인은 식별자 확인에서 변수 조회에 사용됩니다.
함수 호출 시 활성 객체와 이 함수 내의 [[scope]] 속성을 포함하여 함수 컨텍스트의 범위 체인이 생성됩니다. 아래에서는 함수의 [[scope]] 속성에 대해 더 자세히 설명합니다.
은 문맥상 다음과 같이 표현됩니다.

코드 복사 코드는 다음과 같습니다.
activeExecutionContext = {
VO: {...}, // 또는 AO
this: thisValue,
Scope: [ // 스코프 체인
// 모든 변수 개체 목록
/ / 식별자 조회
]
};

범위는 다음과 같이 정의됩니다.
Scope = AO [[Scope]]
이 통합 및 식별자 확인 프로세스는 아래 토론에서 논의할 것입니다. 이는 함수의 수명 주기와 관련이 있습니다.
함수의 생명주기
함수의 생명주기는 생성단계와 활성화단계(호출시)로 나누어지는데, 자세히 알아보겠습니다.
함수 생성
우리 모두 알고 있듯이 함수 선언은 컨텍스트에 들어갈 때 변수/활동(VO/AO) 개체에 배치됩니다. 전역 컨텍스트에서 변수 및 함수 선언을 살펴보겠습니다. (여기서 변수 개체는 전역 개체 자체입니다. 기억하시나요?)

코드 복사 코드는 다음과 같습니다.
var x = 10;
function foo() {
var y = 20>alert(x y); 🎜> }
foo(); // 30


함수가 활성화되면 올바른(예상) 결과인 30을 얻습니다. 그러나 매우 중요한 기능이 하나 있습니다.
이전에는 현재 컨텍스트와 관련된 변수 객체에 대해서만 이야기했습니다. 여기서 변수 "y"는 함수 "foo"에 정의되어 있지만(즉, foo 컨텍스트의 AO에 있음을 의미) 변수 "x"는 "foo" 컨텍스트에 정의되어 있지 않습니다. "foo"의 AO에 추가되지 않습니다. 언뜻 보기에 변수 "x"는 "foo" 함수와 관련하여 전혀 존재하지 않지만 아래에서 볼 수 있듯이 "한 눈에" 볼 때 "foo" 컨텍스트의 활성 객체에는 다음과 같은 항목만 포함되어 있음을 알 수 있습니다. 하나의 속성 - "y".



코드 복사 코드는 다음과 같습니다. fooContext.AO = {
y : 정의되지 않음 / / 정의되지 않음 - 컨텍스트 입력 시 20 - 활성화 시


"foo" 함수는 어떻게 변수 "x"에 접근하나요? 이론적으로 함수는 상위 수준 컨텍스트의 변수 개체에 액세스할 수 있어야 합니다. 실제로 이 메커니즘은 함수 내부의 [[scope]] 속성을 통해 구현됩니다.
[[scope]]는 현재 함수 컨텍스트 위에 있는 모든 상위 변수 개체의 계층적 체인이며 함수가 생성될 때 여기에 저장됩니다.
이 중요한 점에 유의하세요. [[scope]]는 함수가 생성될 때 저장됩니다. 함수가 소멸될 때까지 영원히 정적(불변)입니다. 즉, 함수를 호출할 수 없지만 [[scope]] 속성이 함수 개체에 작성되어 저장되었습니다.
또 다른 고려해야 할 점은 스코프 체인과 달리 [[scope]]는 컨텍스트가 아닌 함수의 속성이라는 것입니다. 위의 예를 고려하면 "foo" 함수의 [[scope]]는 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.

foo.[[Scope]] = [
globalContext.VO // === Global
]

예를 들어, 우리는 일반적인 ECMAScript 배열을 사용하여 범위와 [[범위]]를 나타냅니다.
계속해서 함수가 호출되면 컨텍스트가 입력된다는 것을 알고 있습니다. 이때 활성 개체가 생성되고 이것과 범위(범위 체인)가 결정됩니다. 이 순간을 자세히 살펴 보겠습니다.
함수 활성화
정의에서 언급한 바와 같이 AO/VO를 생성하기 위한 컨텍스트를 입력한 후 컨텍스트(변수 검색을 위한 범위 체인)의 Scope 속성은 다음과 같이 정의됩니다.
Scope = AO| VO [ [Scope]]
위 코드의 의미는 활성 개체가 범위 배열의 첫 번째 개체, 즉 범위의 프런트 엔드에 추가된다는 것입니다.
Scope = [AO].concat([[Scope]]);
이 기능은 식별자 구문 분석에 매우 중요합니다.
식별자 확인은 변수(또는 함수 선언)가 어떤 변수 개체에 속하는지 확인하는 프로세스입니다.
이 알고리즘의 반환 값에는 항상 참조 유형이 있고 기본 구성 요소는 해당 변수 개체(또는 찾을 수 없는 경우 null)이며 속성 이름 구성 요소는 조회된 식별자의 이름입니다. 참조 유형에 대한 자세한 내용은 13장에서 설명합니다.
식별자 확인 프로세스는 변수 이름에 해당하는 속성 조회, 즉 가장 깊은 컨텍스트에서 시작하여 범위 체인을 우회하여 최상위까지 범위 내 변수 개체를 지속적으로 조회하는 것으로 구성됩니다.
이런 방식으로 상향 검색에서는 컨텍스트의 지역 변수가 상위 범위의 변수보다 우선순위가 높습니다. 두 변수가 이름은 같지만 범위가 다른 경우, 가장 먼저 발견된 변수는 가장 깊은 범위에 있습니다.
위에서 언급한 내용을 설명하기 위해 약간 더 복잡한 예를 사용합니다.
코드 복사 코드는 다음과 같습니다.

var x = 10; foo() {
var y = 20;
function bar() {
var z = 30;
alert(x y z);
bar(); }
foo(); // 60


이를 위해 다음과 같은 변수/활동, 함수의 [[scope]] 속성 및 컨텍스트의 범위 체인이 있습니다.
글로벌 컨텍스트 변수 객체는



코드 복사 코드는 다음과 같습니다. globalContext.VO === Global = {
x: 10
foo: <함수 참조>


" "foo"가 생성될 때 "foo"는 다음과 같습니다.



코드 복사
코드는 다음과 같습니다. foo.[[Scope]] = [ globalContext.VO
];


"foo"가 활성화되면(컨텍스트에 들어감) "foo"의 활성 객체 컨텍스트는



코드 복사
코드는 다음과 같습니다. fooContext.AO = { y: 20,
bar: <함수 참조>
}


"foo" 컨텍스트의 범위 체인은



코드 복사
코드는 다음과 같습니다. fooContext.Scope = fooContext.AO foo.[[Scope]] // 즉. : fooContext.Scope = [
fooContext.AO,
globalContext.VO
]


내부 함수 "bar"가 생성되면 [[scope] ]] is:



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

bar.[[Scope]] = [
fooContext.AO,
globalContext.VO
]

"bar"가 활성화되면 " bar" 컨텍스트의 활성 개체는 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.

barContext.AO = {
z: 30
};

"bar" 컨텍스트의 범위 체인은 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.

barContext.Scope = barContext.AO bar.[[Scope]] // 예:
barContext .Scope = [
barContext.AO,
fooContext.AO,
globalContext.VO
]

"x", "y" 및 " z"는 다음과 같이 구문 분석됩니다:
- "x"
-- barContext.AO // 찾을 수 없음
-- fooContext.AO // 찾을 수 없음
-- globalContext.VO // 발견 - 10
- "y"
-- barContext.AO // 찾을 수 없음
-- fooContext.AO // 찾을 수 있음 - 20
- "z"
-- barContext.AO // found - 30
스코프 특성
스코프 체인 및 함수 [[scope]] 속성과 관련된 몇 가지 중요한 기능을 살펴보겠습니다.
클로저
ECMAScript에서 클로저는 함수의 [[scope]]와 직접적으로 관련되어 있습니다. 앞서 언급했듯이 [[scope]]는 함수가 생성될 때 저장되며 함수와 함께 살고 죽습니다. 실제로 클로저는 함수 코드와 해당 [[범위]]의 조합입니다. 따라서 [[Scope]]는 함수 내에서 생성된 어휘 범위(부모 변수 객체)를 객체 중 하나로 포함합니다. 함수가 추가로 활성화되면 변수 개체의 어휘 체인(생성 시 정적으로 저장됨) 내에서 더 높은 범위의 변수가 검색됩니다.
예:
코드 복사 코드는 다음과 같습니다.

var x = 10;
function foo() {
alert(x);
}
(function () {
var x = 20;
foo(); // 10이지만 not 20
})();

식별자 확인 중에 함수가 생성될 때 정의된 어휘 범위가 사용된다는 점을 다시 확인합니다. 변수는 30이 아닌 10으로 확인됩니다. 또한 이 예에서는 함수(이 경우 "foo" 함수에서 반환된 익명 함수)의 [[scope]]가 함수에 의해 생성된 범위가 완료된 후에도 지속된다는 점을 명확하게 보여줍니다.
ECMAScript의 클로저 이론과 실행 메커니즘에 대한 자세한 내용은 16장 클로저를 참조하세요.
생성자를 통해 생성된 함수의 [[scope]]
위의 예에서 함수가 생성될 때 함수의 [[scope]] 속성을 가져오고 모든 상위 컨텍스트가 이 속성 변수를 통해 액세스됩니다. 그러나 이 규칙에는 중요한 예외가 있으며, 이는 함수 생성자를 통해 생성된 함수와 관련됩니다.
코드 복사 코드는 다음과 같습니다.

var x = 10; foo() {
var y = 20;
function barFD() { // 함수 선언
alert(x)
alert(y);
var barFE = function () { // 함수 표현식
alert(x);
alert(y)
}
var barFn = Function('alert(x); Alert(y);') ;
barFD(); // 10, 20
barFE(); // 10, 20
barFn() // 10, "y"는 정의되지 않았습니다.
}
foo () ;


함수 생성자를 통해 생성된 함수 "bar"는 변수 "y"에 액세스할 수 없음을 알 수 있습니다. 하지만 이것이 "barFn" 함수에 [[scope]] 속성이 없다는 의미는 아닙니다(그렇지 않으면 변수 "x"에 액세스할 수 없습니다). 문제는 함수 생성자를 통해 생성된 함수의 [[scope]] 속성이 항상 유일한 전역 개체라는 것입니다. 이를 염두에 두고, 그러한 함수를 통해 전역 컨텍스트 클로저 이외의 최상위 컨텍스트 클로저를 생성하는 것은 불가능합니다.
2차원 스코프 체인 검색
스코프 체인 검색에서 가장 중요한 점은 ECMAScript의 프로토타입 기능에서 파생된 변수 객체(있는 경우)의 속성을 고려해야 한다는 것입니다. 개체에서 속성을 직접 찾을 수 없는 경우 쿼리는 프로토타입 체인에서 계속됩니다. 이를 종종 2차원 연쇄 검색이라고 합니다. (1) 범위 체인 링크, (2) 각 범위 체인 - 프로토타입 체인 링크로 깊숙이 들어갑니다. Object.prototype에 속성이 정의되어 있으면 이 효과를 볼 수 있습니다.


function foo() {
alert (x) ;
}
Object.prototype.x = 10;
foo(); // 10


에서 볼 수 있듯이 활성 객체에는 프로토타입이 없습니다. 아래 예:


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

function foo() {
var x = 20;
function bar() {
alert(x)
}
bar();
Object.prototype.x = 10;
foo(); // 20

함수 "bar" 컨텍스트의 활성화 객체에 프로토타입이 있으면 "x"는 AO에서 직접 구문 분석되지 않기 때문에 Object .prototype에 있어야 합니다. 그러나 위의 첫 번째 예에서는 식별자 확인 중에 Object.prototype에서 상속되는 전역 개체(일부 구현에서는 항상 그런 것은 아님)에 도달하므로 "x"는 10으로 확인됩니다.
특정 객체가 Object.prototype에서 상속된 함수 표현식의 선택적 이름을 저장하는 SpiderMokey의 일부 버전에서 명명된 함수 표현식(약칭 NFE)에서 동일한 상황이 발생합니다. Blackberry의 일부 버전에서는 실행 시간 활성화가 객체는 Object.prototype에서 상속됩니다. 그러나 이 기능에 대한 자세한 내용은 15장 기능에서 설명합니다.
전역 및 평가 컨텍스트의 범위 체인
여기서 반드시 흥미롭지는 않지만 알림이 됩니다. 전역 컨텍스트의 범위 체인에는 전역 개체만 포함됩니다. 코드 eval의 컨텍스트는 현재 호출 컨텍스트와 동일한 범위 체인을 갖습니다.

코드 복사 코드는 다음과 같습니다.
globalContext.Scope = [
Global
];
evalContext.Scope === CallingContext.Scope;

코드 실행 중 범위 체인에 미치는 영향
ECMAScript에는 수정할 수 있는 두 가지 문이 있습니다. 코드 실행 단계 범위 체인 중. 이것은 with 문과 catch 문입니다. 이는 범위 체인 앞에 추가되며 이러한 선언에 나타나는 식별자 중에서 개체를 조회해야 합니다. 이 중 하나가 발생하면 범위 체인은 다음과 같이 간략하게 수정됩니다.
Scope = withObject|catchObject AO|VO [[Scope]]
이 예에서는 개체가 매개 변수로 추가됩니다(따라서 접두사가 없음). , 이 개체의 속성에 액세스할 수 있게 됩니다.)

코드 복사 코드는 다음과 같습니다.
var foo = {x: 10, y : 20} ;
with (foo) {
alert(x); // 10
alert(y) // 20
}


Scope = foo AO|VO [[Scope]]
with 문을 통해 객체의 식별자 확인이 범위 체인 앞에 추가되는 것을 다시 볼 수 있습니다.

코드 복사 코드는 다음과 같습니다.
var x = 10, y = 10; 🎜>with ({x: 20}) {
var x = 30, y = 30;
alert(x); // 30
alert(y) // 30
} 🎜>alert(x); // 10
alert(y); // 30


컨텍스트에 들어가면 어떻게 되나요? 식별자 "x"와 "y"가 변수 개체에 추가되었습니다. 또한 코드 실행 단계에서 다음과 같은 수정이 이루어집니다.
x = 10, y = 10
객체 {x:20}이
내부에 추가됩니다. , var 문이 발생합니다. 물론 아무것도 생성되지 않습니다. 왜냐하면 컨텍스트에 들어갈 때 모든 변수가 구문 분석되고 추가되었기 때문입니다.
두 번째 단계에서는 변수 "x"만 수정됩니다. 실제로는 "x"입니다. 이제 개체가 구문 분석되어 역할에 추가됩니다. 도메인 체인의 프런트 엔드에서 "x"는 20이고 30으로 변경됩니다.
변수 개체 "y"에도 수정이 있으며 해당 값은 다음과 같습니다.
또한 with 선언이 완료된 후 해당 특정 개체가 범위 체인에서 제거됩니다(변경된 변수 "x" - 30도 해당 개체에서 제거됨). 스코프 체인의 구조가 강화되기 전의 상태로 복원되었습니다.
마지막 두 개의 경고에서 현재 변수 개체의 "x"는 동일하게 유지되고 "y"의 값은 이제 30과 동일하며 with 문 실행 중에 변경되었습니다.
마찬가지로 catch 문의 예외 매개변수에 액세스할 수 있게 되어 예외 매개변수 이름이라는 하나의 속성만 가진 새 객체가 생성됩니다. 그림은 다음과 같습니다.



...
} catch (ex) {
alert(ex)
}


스코프 체인은 다음과 같이 수정됩니다:


코드 복사 코드는 다음과 같습니다. var catchObject = {
ex: <예외 개체> >};
범위 = catchObject AO|VO [[범위]]


catch 문 실행이 완료된 후 범위 체인이 이전 상태로 복원됩니다.
결론
이 단계에서는 실행 컨텍스트와 관련된 거의 모든 공통 개념과 이와 관련된 세부 사항을 고려했습니다. 계획대로 - 함수 개체에 대한 자세한 분석: 함수 유형(함수 선언, 함수 표현식) 및 클로저. 그런데 이 글에서 클로저는 [[scope]] 속성과 직접적으로 관련되어 있지만 이에 대해서는 해당 장에서 논의할 것입니다. 댓글을 통해 귀하의 질문에 기꺼이 답변해 드리겠습니다.
기타 참고자료
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.