>웹 프론트엔드 >JS 튜토리얼 >자바스크립트 시리즈의 심층적 이해(12) 가변 객체(Variable Object)_javascript 스킬

자바스크립트 시리즈의 심층적 이해(12) 가변 객체(Variable Object)_javascript 스킬

WBOY
WBOY원래의
2016-05-16 17:57:01912검색

JavaScript를 프로그래밍할 때 시스템을 성공적으로 구축하기 위해서는 함수와 변수 선언을 피할 수 없지만, 인터프리터는 이러한 함수와 변수를 어떻게, 어디서 찾을 수 있을까요? 이러한 객체를 참조하면 정확히 어떤 일이 발생합니까?
원본 출시: Dmitry A. Soshnikov
게시 시간: 2009-06-27
러시아어 주소: http://dmitrysoshnikov.com/ecmascript/ru-chapter-2-variable-object/
영어 번역: Dmitry A. Soshnikov
출시 시간: 2010-03-15
영어 주소: http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/
일부 번역이 어려움 문장은 justinw의 중국어 번역을 나타냅니다.
대부분의 ECMAScript 프로그래머는 변수가 실행 컨텍스트와 밀접하게 관련되어 있다는 것을 알아야 합니다.

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

var a = 10; // 전역 컨텍스트의 변수
(function () {
var b = 20; // 전역 컨텍스트의 지역 변수 function context
})();
alert(a); // 10
alert(b); // 전역 변수 "b"가 선언되지 않았습니다.

많은 프로그래머 우리 모두 알고 있듯이 현재 ECMAScript 사양에서는 "함수" 코드 유형의 실행 컨텍스트를 통해서만 독립적인 범위를 생성할 수 있다고 명시하고 있습니다. 즉, C/C와 비교하여 ECMAScript의 for 루프는 로컬 컨텍스트를 생성할 수 없습니다.
코드 복사 코드는 다음과 같습니다.

for (var k in {a: 1 , b: 2}) {
alert(k);
}
alert(k); // 루프가 종료되었지만 변수 k는 여전히 현재 범위에 있습니다.

보자 데이터를 선언하면서 어떤 내용을 발견했는지 살펴보겠습니다.
데이터 선언
변수가 실행 컨텍스트와 관련된 경우 변수 자체는 데이터가 저장되는 위치와 액세스 방법을 알아야 합니다. 이 메커니즘을 변수 개체라고 합니다.
Variable 객체(VO로 약칭)는 실행 컨텍스트와 관련된 특수 객체입니다.
Variable(var, 변수 선언)
FunctionDeclaration(FunctionDeclaration, 약칭); FD);
함수의 형식적 매개변수
예를 들어, 일반 ECMAScript 객체를 사용하여 변수 객체를 나타낼 수 있습니다:
코드 복사 코드는 다음과 같습니다.

VO = {}
앞서 말했듯이 VO는 실행 컨텍스트의 속성입니다.
activeExecutionContext = {
VO : {
//컨텍스트 데이터(var, FD, 함수 인수)
}
}

전역 컨텍스트 변수 개체만 VO 속성 이름을 전달하여 간접적으로 액세스할 수 있습니다. (왜냐하면 전역 컨텍스트에서는 전역 객체 자체가 변수 객체이기 때문에 나중에 자세히 소개하겠습니다.) VO 객체는 내부 메커니즘의 구현일 뿐이므로 다른 컨텍스트에서 직접 액세스할 수 없습니다.
변수나 함수를 선언할 때는 VO의 새 속성을 생성할 때와 별 차이가 없습니다(예: 이름과 해당 값이 있음).
예:
코드 복사 코드는 다음과 같습니다.

var a = 10;
function test(x) {
var b = 20;
}
test(30)

해당 변수 개체는 다음과 같습니다.
코드 복사 코드는 다음과 같습니다.
// 전역 컨텍스트 변수 개체
VO(globalContext ) = {
a: 10,
test: <함수 참조>
}
// 테스트 함수 컨텍스트의 변수 개체
VO(test functionContext) = {
x: 30,
b: 20
};

특정 구현 수준(및 사양)에서 변수 개체는 단지 추상적인 개념입니다. (기본적으로 특정 실행 컨텍스트에서는 VO 이름이 다르고 초기 구조도 다릅니다.
다른 실행 컨텍스트의 변수 개체
모든 유형의 실행 컨텍스트에 대해 변수 개체 일부 작업(예: 변수 초기화) 및 동작이 일반적입니다. 이러한 관점에서 변수 객체를 추상적인 기본 것으로 이해하고 함수의 맥락에서 변수 객체와 관련된 추가 내용을 정의하는 것이 더 쉽습니다. 🎜>코드 복사

코드는 다음과 같습니다.

추상 변수 객체 VO(변수 초기화 프로세스의 일반적인 동작)

╠==> 전역 컨텍스트 변수 객체 GlobalContextVO
║ (VO === this === 전역)

╚==> 함수 컨텍스트 변수 객체 FunctionContextVO
(VO === AO, <인수> 및 <형식 매개변수> 추가)

자세히 살펴보세요:
전역 컨텍스트의 변수 객체
먼저 전역 객체에 명확한 정의를 제공해야 합니다.
전역 객체(전역 객체)는 실행 컨텍스트 객체에 들어가기 전에 생성됩니다.
이 개체의 복사본은 하나만 있고 해당 속성은 프로그램 어디에서나 액세스할 수 있으며 전역 개체의 수명 주기는 프로그램이 종료되는 순간 종료됩니다.
코드 복사
전역 객체의 초기 생성 단계에서는 Math, String, Date,parseInt가 자체 속성으로 사용되며 다른 속성도 속성으로 생성될 수 있습니다. 전역 객체 자체를 가리킬 수 있습니다). 예를 들어 DOM에서 전역 객체의 window 속성은 전역 객체 자체를 참조할 수 있습니다(물론 모든 특정 구현이 이와 같은 것은 아닙니다).
복사 code 코드는 다음과 같습니다.

global = {
Math: <...>,
String: <... .>
...
...
window: global //참조 자체
}

속성에 접근할 때 접두사는 일반적으로 무시됩니다. 전역 객체는 이름으로 직접 접근할 수 없기 때문입니다. 그러나 우리는 여전히 전역 컨텍스트를 통해 전역 객체에 접근할 수 있고, 재귀적으로 그 자체를 참조할 수도 있습니다. 예를 들어 DOM의 창입니다. 요약하면 코드는 다음과 같이 축약될 수 있습니다.
코드 복사 코드는 다음과 같습니다.

String( 10); // global.String(10);
// 접두사
window.a = 10; // === global.window.a === global.a = 10 ;
this.b = 20; // global.b = 20;

전역 컨텍스트의 변수 개체로 돌아가서 이 경우 변수는 object는 전역 객체 그 자체입니다:
VO(globalContext) === global;
이 원칙을 바탕으로 전역 컨텍스트에서 대응 관계가 선언된 경우에만 위의 결론을 이해하는 것이 매우 필요합니다. 전역 개체의 속성을 통해 간접적으로 액세스합니다(예를 들어 변수 이름을 미리 알 수 없음).
코드 복사 코드는 다음과 같습니다.

var a = new String('test' );
alert(a); // VO(globalContext): "test"에서 발견됨
alert(window['a']) // 전역을 통한 간접 액세스: global === VO (globalContext) ): "test"
alert(a === this.a); // true
var aKey = 'a'
alert(window[aKey]); 동적 속성 이름 액세스: "test"

함수 컨텍스트의 변수 개체
함수 실행 컨텍스트에서는 VO에 직접 액세스할 수 없습니다. 이때 활성화 개체( 약칭 AO) VO의 역할.
VO(functionContext) === AO;
함수 컨텍스트에 진입하면 활성 객체가 생성되며, 함수의 인수 속성을 통해 초기화됩니다. Arguments 속성의 값은 Arguments 객체입니다:
코드 복사 코드는 다음과 같습니다:

AO = {
arguments:
};

Arguments 개체는 다음 특성을 포함하는 활성 개체의 특성입니다.
callee — 현재 함수에 대한 참조
length — 실제로 전달된 매개변수의 수
properties-indexes(문자열 유형의 정수) 속성 값은 함수의 매개변수 값입니다(매개변수에서 왼쪽에서 오른쪽으로 정렬됨). 목록). Properties-indexes 내부의 요소 수는 Argument.length와 동일합니다. Properties-indexes의 값은 실제로 전달된 매개변수와 공유됩니다.
예:
코드 복사 코드는 다음과 같습니다.

function foo(x, y, z) {
// 선언된 함수 매개변수 인수 수(x, y, z)
alert(foo.length) // 3
// 실제로 전달된 매개변수의 수(x, y만)
alert(arguments.length) // 2
// 매개변수의 호출 수신자는 함수 자체입니다.
alert(arguments.callee === foo); // true
// 매개변수 공유
alert(x === 인수[0]) // true
alert(x) // 10개
인수[ 0] = 20;
alert(x); // 20
x = 30;
alert(arguments[0]) // 30
// 전달된 매개변수 3번째 인덱스 값은 공유되지 않습니다.
z = 40;
alert(arguments[2]) // 정의되지 않음
arguments[2] = 50; / 40
}
foo(10, 20);

이 예제의 코드에는 현재 버전의 Google Chrome에 버그가 있습니다. 매개변수 z, z가 그렇지 않은 경우에도 마찬가지입니다. 전달되었으며 인수[2]는 여전히 공유됩니다.
컨텍스트 코드 처리의 두 단계
이제 드디어 이 글의 핵심에 도달했습니다. 실행 컨텍스트 코드는 처리를 위해 두 가지 기본 단계로 구분됩니다.
실행 컨텍스트 입력
코드 실행
변수 개체의 수정은 이 두 단계와 밀접하게 관련되어 있습니다.
참고: 이 두 단계의 처리는 일반적인 동작이며 컨텍스트 유형과 관련이 없습니다(즉, 성능은 전역 컨텍스트와 기능 컨텍스트에서 동일합니다).
실행 컨텍스트 입력
실행 컨텍스트에 들어갈 때(코드 실행 전) VO에는 이미 다음 속성이 포함되어 있습니다(앞서 언급한 바와 같이):
함수의 모든 형식 매개변수(함수 실행에 있는 경우) context Medium)
- 이름과 해당 값으로 구성된 변수 개체의 속성이 생성되며, 해당 매개 변수가 전달되지 않으면 이름과 정의되지 않은 값으로 구성된 변수 개체의 속성도 생성됩니다.
모든 함수 선언(FunctionDeclaration, FD)
- 이름과 해당 값으로 구성된 변수 개체의 속성(함수-객체)이 생성됩니다. 변수 개체에 이미 동일한 이름의 속성이 있는 경우 이 속성을 완전히 대체합니다.
모든 변수 선언(var, VariableDeclaration)
- 변수 이름이 선언된 형식 매개변수 또는 함수와 동일한 경우 이름과 해당 값(정의되지 않음)으로 구성된 변수 개체의 속성이 생성됩니다. 변수 선언은 이 유형의 기존 속성을 방해하지 않습니다.
예제를 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.
function test( a, b) {
var c = 10;
function d() {}
var e = function _e() {}
(function x() {});
}
test(10); //

호출
매개변수 10을 사용하여 테스트 함수 컨텍스트에 들어가면 AO는 다음과 같이 동작합니다.

코드 복사 코드는 다음과 같습니다.
AO(test) = {
a: 10,
b: 정의되지 않음,
c : 정의되지 않음,
d:
e: 정의되지 않음
};

AO에는 " 엑스". 이는 "x"가 함수 선언이 아닌 함수 표현식(FunctionExpression, 약칭 FE)이고, 함수 표현식이 VO에 영향을 미치지 않기 때문입니다. 어쨌든 함수 "_e"도 함수 표현식이지만 아래에서 볼 수 있듯이 변수 "e"에 할당되므로 "e"라는 이름으로 액세스할 수 있습니다. 함수 선언 FunctionDeclaration과 함수 표현식 FunctionExpression의 차이점은 15장 함수에서 자세히 설명합니다. 또한 이 시리즈의 2장을 참조하여 명명된 함수 표현식에 대해 알아볼 수도 있습니다.
이후에는 컨텍스트 코드 처리의 두 번째 단계인 코드 실행에 들어갑니다.
코드 실행
이 주기 동안 AO/VO에는 이미 속성이 있습니다(단, 모든 속성에 값이 있는 것은 아니며 대부분의 속성 값은 여전히 ​​시스템 기본 초기 값인 정의되지 않음).
이전 예에서도 코드 해석 중에 AO/VO가 다음과 같이 수정되었습니다.

코드 복사 코드는 다음과 같습니다. 다음과 같습니다:
AO['c'] = 10;
AO['e'] =

다시 한번 주의할 점은 FunctionExpression "_e"가 선언된 변수 "e"에 저장되므로 메모리에 여전히 존재한다는 점입니다. FunctionExpression "x"는 AO/VO에 존재하지 않습니다. 즉, "x" 함수를 호출하려고 하면 함수 정의 전후에 관계없이 "x가 정의되지 않았습니다"라는 오류가 나타나고 함수는 표현식은 자체 정의 내에서 또는 재귀적으로만 호출될 수 있습니다.
또 다른 전형적인 예:

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

alert(x); // 함수
var x = 10;
alert(x); // 10
x = 20;
alert(x); // 20

첫 번째 경고 "x"의 반환 값이 함수이고 "x"가 선언되기 전에 "x"에 액세스하는 이유는 무엇입니까? 왜 10이나 20이 아닌가? 사양에 따르면 함수 선언은 순환에 동의하여 컨텍스트에 들어갈 때 채워지기 때문에 컨텍스트에 들어갈 때 변수 선언 "x"도 있고 이전 단계에서 말했듯이 변수 선언이 따릅니다. 순차적으로 함수 선언 및 형식 매개변수 선언 후, 이 컨텍스트 진입 단계에서 변수 선언은 VO에 이미 존재하는 동일한 이름의 함수 선언 또는 형식 매개변수 선언을 방해하지 않습니다. VO의 구조는 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.
VO = {}; VO['x'] = < FunctionDeclaration "x"에 대한 참조>
// var x = 10>// 함수 "x"가 선언되지 않은 경우 >// 이때 "x"의 값은 정의되지 않아야 합니다
// 그러나 이 경우 변수 선언은 같은 이름을 가진 함수의 값에 영향을 미치지 않습니다
VO['x'] = < ;값은 방해받지 않고 여전히 작동합니다>


다음으로, 코드 실행 단계에서 VO는 다음과 같이 수정합니다:



코드
코드는 다음과 같습니다. VO[' x'] = 10 VO['x'] = 20;
두 번째와 세 번째 알림에서 이러한 효과를 확인할 수 있습니다.
아래 예에서는 컨텍스트 단계에서 변수가 VO에 입력되는 것을 다시 볼 수 있습니다. (코드의 else 부분은 절대 실행되지 않지만 어쨌든 변수 "b"는 VO에 여전히 존재하기 때문입니다.)



코드 복사
코드는 다음과 같습니다. if (true) { var a = 1 } else {
var b = 2; 🎜>alert(a); // 1
alert(b); // 정의되지 않음, b가 선언되지 않은 것이 아니라 b의 값이 정의되지 않음


변수에 대하여
일반적으로 JavaScript와 관련된 다양한 기사에서는 "var 키워드를 사용하든(전역 컨텍스트에서) var 키워드를 사용하지 않든(어디서든) 변수를 선언할 수 있습니다."라고 주장합니다. 이는 오해입니다.
언제든지 변수는 var 키워드를 사용해서만 선언할 수 있습니다.
위 할당문:
a = 10;
이는 전역 개체에 대한 새 속성을 생성할 뿐입니다(변수는 아님). "변수가 아니다"는 것은 변경할 수 없다는 뜻이 아니라, ECMAScript 명세의 변수 개념에 맞지 않는다는 뜻이므로 "변수가 아니다"(전역객체의 속성이 될 수 있는 이유) 이는 전적으로 VO(globalContext) = == global 때문입니다. 아직도 기억하시나요?
다음 예시를 통해 구체적인 차이점을 살펴보겠습니다.




코드 복사


코드는 다음과 같습니다.모든 루트는 여전히 VO이고 컨텍스트 단계 및 코드 실행 단계에 들어갑니다.
컨텍스트 단계에 들어갑니다.




코드 복사


코드는 다음과 같습니다.
VO = { a: 정의되지 않음 }; "b"가 다음과 같기 때문에 알 수 있습니다. 변수가 아닙니다. 이 단계에서는 "b"가 전혀 없습니다. "b"는 코드 실행 단계에서만 나타납니다(그러나 우리의 경우에는 그 전에 오류가 발생합니다).
예제 코드를 변경해 보겠습니다.




코드 복사


코드는 다음과 같습니다.
변수에 대한 또 다른 중요한 지식 포인트가 있습니다. 단순 속성과 비교하여 변수에는 {DontDelete} 속성이 있습니다. 이 속성의 의미는 변수 속성을 삭제 연산자를 사용하여 직접 삭제할 수 없다는 것입니다.




코드 복사

코드는 다음과 같습니다.

a = 10;
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined
var b = 20;
alert(window.b); // 20
alert(delete b); // false
alert(window.b); // still 20

But this rule cannot be distorted in one context, that is, the eval context, and the variable does not have the {DontDelete} attribute.
Copy code The code is as follows:

eval('var a = 10;');
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined

Use some debugging tools (For example: Firebug) console when testing this instance, please note that Firebug also uses eval to execute your code in the console. Therefore, variable attributes also do not have the {DontDelete} attribute and can be deleted.
Special implementation: __parent__ attribute
As mentioned before, according to the standard specification, active objects cannot be accessed directly. However, some specific implementations do not fully comply with this regulation, such as SpiderMonkey and Rhino; in the implementation, the function has a special attribute __parent__, through which the active object or global variable object that the function has created can be directly referenced.
For example (SpiderMonkey, Rhino):
Copy code The code is as follows:

var global = this;
var a = 10;
function foo() {}
alert(foo.__parent__); // global
var VO = foo.__parent__;
alert(VO. a); // 10
alert(VO === global); // true

In the above example we can see that the function foo is created in the global context, So the attribute __parent__ points to the variable object of the global context, that is, the global object.
However, it is not possible to access the active object in the same way in SpiderMonkey: in different versions of SpiderMonkey, the __parent__ of the internal function sometimes points to null and sometimes points to the global object.
In Rhino, it is completely possible to access active objects in the same way.
For example (Rhino):
Copy code The code is as follows:

var global = this ;
var x = 10;
(function foo() {
var y = 20;
// Active object in "foo" context
var AO = (function () { }).__parent__;
print(AO.y); // 20
// The __parent__ of the current active object is the existing global object
// A special chain of variable objects forms
// So we call it scope chain
print(AO.__parent__ === global); // true
print(AO.__parent__.x); // 10
})();

Summary
In this article, we took an in-depth look at objects related to execution context. I hope this knowledge can be helpful to you and solve some problems or confusions you have encountered. As planned, in subsequent chapters, we will explore scope chains, identifier resolution, and closures.
Any questions, I’d be happy to answer them in the comments below.
Other references
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.