단순 숫자 유형: 정의되지 않음, Null, Boolean, 숫자 및 문자열이 있습니다. 여기서 설명하는 영어 단어는 데이터 유형의 이름만을 참조할 뿐 JS의 전역 개체 Nan, Boolean, Number, String 등을 구체적으로 참조하지 않는다는 점에 유의하세요. 이들 개념의 차이는 상대적으로 큽니다.
객체: 값이 단순한 숫자 유형, 객체 또는 함수인 정렬되지 않은 속성 모음입니다. 위와 마찬가지로 여기서 개체는 전역 개체 개체를 구체적으로 참조하지 않습니다.
함수: 함수는 객체의 유형입니다. 구현 시 내부 속성 [[Class]]의 값은 "Function"이며, 이는 객체의 내부 속성 메서드임을 나타냅니다. [[Construct]], [ [Call]] 및 [[Scope]]와 같은 내부 속성도 있습니다. 함수 호출로서의 함수 처리 메커니즘은 생성자의 처리 메커니즘과 다릅니다(인스턴스 개체를 생성하기 위해 new 키워드 사용)(Function 개체 제외). 내부 메서드 [[Construct]]는 생성자의 논리를 구현하는 데 사용됩니다. [[Call]] 메소드는 Logic을 함수 호출로 구현합니다. 위와 마찬가지로 여기서 함수는 전역 객체 Function을 구체적으로 참조하지 않습니다.
함수는 JS의 프로토타입 언어에서 객체지향 언어의 클래스로 볼 수 있으며, 객체 인스턴스를 구성하는 데 사용할 수 있습니다. 함수는 클래스로 볼 수 있으므로 각 함수는 확장 데이터 유형으로 볼 수 있습니다.
내장 데이터형(내장 객체)
함수: 함수형의 사용자 인터페이스.
객체: 객체 유형의 사용자 인터페이스입니다.
부울, 숫자, 문자열: 이러한 세 가지 간단한 숫자 유형에 대한 개체 래퍼입니다. 개체 래핑은 개념적으로 C#의 Box/Unbox와 유사합니다.
Date, Array, RegExp: 여러 내장 확장 데이터 유형으로 간주될 수 있습니다.
우선 Function, Object, Boolean, Number, String, Date, Array, RegExp 등은 모두 JavaScript 언어의 내장 객체라고 할 수 있습니다. 예를 들어 NumberinstanceofFunction은 true입니다. NumberinstanceofObject는 true입니다. 이런 의미에서 사용자 정의 함수와 동일하게 처리될 수 있습니다.
둘째, 각각은 데이터 유형을 나타낼 수 있으며 이는 기본 코드 또는 내장 JS 코드를 사용하여 JS 엔진에 의해 구현되며 이러한 내장 데이터 유형을 조작하기 위해 개발자에게 노출되는 인터페이스입니다. 이런 의미에서 이는 그 뒤에 숨겨진 특정 구현 메커니즘이 있는 추상적인 개념입니다.
숫자, 함수 등의 단어를 언급할 때마다 위의 두 가지 상황 중 하나로 빠르게 머릿속에서 예시해야 합니다.
데이터 유형 구현 모델 설명
빌드인 *** 데이터 구조: JS 내부에서 *** 유형을 구현하는 데 사용되는 데이터 구조를 말합니다. . 이러한 구조는 기본적으로 우리가 직접 접근할 수 없습니다.
빌드인 *** 객체: JS에 내장된 숫자, 문자열, 부울 및 기타 객체를 의미하며 JS가 내부적으로 구현된 데이터 유형을 개발자에게 공개하는 인터페이스입니다.
빌드인 *** 생성자: JS에 내장된 일부 생성자를 말하며 해당 유형의 객체 인스턴스를 생성하는 데 사용됩니다. 예를 들어, 다음 방법을 사용하여 이러한 함수 객체에 액세스할 수 있습니다.
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
//access the build-in number constructor
var number = new Number(123);
var numConstructor1 = number.constructor; //or
var numConstructor2 = new Object(123).constructor;
//both numConstructor1 and numConstructor2 are the build-in Number constructor
numConstructor1 == numConstructor2 //result: true
//access the build-in object constructor
var objConstructor1 = {}.constructor; //or
var objConstructor2 = new Object().constructor;
//both objConstructor1 and objConstructor2 are the build-in Object constructor
objConstructor1==objConstructor2 //result: true
具体实现上,上图中横向之间可能也存在关联,例如对于build-in data structure和constructor,Function、 Date、 Array、 RegExp等都可以继承Object的结构而实现,但这是具体实现相关的事情了。
关于简单数值类型的对象化这是一个细微的地方,下面描述对于Boolean, String和Number这三种简单数值类型都适用,以Number为例说明。
JS规范要求: 使用var num1=123;这样的代码,直接返回基本数据类型,就是说返回的对象不是派生自Number和Object类型,用num1 instanceof Object测试为false;使用new关键字创建则返回Number类型,例如var num2=new Number(123); num2 instanceof Number为true。
将Number当作函数调用,返回结果会转换成简单数值类型。下面是测试代码:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var num1 = new Number(123); //num1 derived from Number & Object
num1 instanceof Number //result: true
num1 instanceof Object //result: true
//convert the num1 from Number type to primitive type, so it's no longer an instance of Number or Object
num1 = Number(num1);
num1 instanceof Number //result: false
num1 instanceof Object //result: false
var num2 = 123; //num2 is a primitive type
num2 instanceof Number //result: false
num2 instanceof Object //result: false
간단한 숫자 유형을 얻었지만 여전히 Object와 해당 유형의 모든 속성 및 메서드를 사용하는 JS Object 객체처럼 보입니다. 기본적으로 유일한 차이점은 instanceof의 테스트 결과입니다.
프로토타입 상속
프로토타입모든 객체는 [[프로토타입]]이라는 내부 속성을 가지며, 값은 다음과 같습니다. null 또는 다른 개체입니다. 함수 객체에는 내부 [[Prototype]] 속성이 아닌 명시적인 프로토타입 속성이 있습니다. 다양한 JS 엔진 구현자는 내부 [[Prototype]] 속성에 임의의 이름을 지정하고 JS 엔진 내에서만 사용하도록 가시성을 설정할 수 있습니다. 내부 [[Prototype]]은 JS 코드에서 액세스할 수 없지만(FireFox에서 액세스할 수 있으며 Mozilla가 공개하기 때문에 이름은 __proto__입니다) 테스트를 위해 객체의 isPrototypeOf() 메서드를 사용할 수 있습니다. 프로토타입 체인에서 판단이 수행되는 동안 전체에 걸쳐 사용됩니다.
obj.propName을 사용하여 객체의 속성에 액세스하는 경우 아래 단계를 따르세요(obj의 내부 [[Prototype]] 속성 이름이 __proto__라고 가정).
obj에 propName 속성이 있는 경우 속성 값, 그렇지 않은 경우
2. obj.__proto__가 null이면 정의되지 않은 값을 반환합니다.
3. obj.__proto__.propName을 반환합니다
객체 호출 방법은 속성 검색 프로세스에 액세스하는 것과 동일합니다. 객체 메소드의 기능은 객체의 속성 값이기 때문입니다.
팁: 위 단계는 3단계에서 obj.__proto__가 또 다른 개체임을 암시합니다. 1, 2, 3단계도 propName 속성을 검색하는 데 사용됩니다.
예를 들어 아래 그림에 표시된 것처럼 object1에는 prop1, prop2, prop3 속성과 fn1, fn2, fn3 메서드가 있습니다. 그림의 점선 화살표는 프로토타입 체인을 나타냅니다.
Prototype을 기반으로 한 상속이자 공유입니다. object1의 fn2 메서드는 object2에서 가져옵니다. 개념적으로 object2는 object3의 fn2 메서드를 재정의합니다.
JavaScript 객체는 모두 프로토타입 체인을 통해 연결되어야 합니다. 최상위 수준은 Object입니다. 즉, 객체는 모두 Object 유형에서 파생됩니다.
C와 같은 객체 지향 언어는 메서드를 전달하기 위해 클래스(추상 유형)를 사용하고 속성을 전달하기 위해 객체(인스턴스화된 객체)를 사용합니다. 본질적인 차이점은 전자는 메모리 구조에 대한 설명을 기반으로 상속을 구현하는 반면, 후자는 특정 메모리 블록을 기반으로 상속을 구현한다는 것입니다.
객체 생성 과정JS에서는 함수 객체만 클래스 개념을 가지므로 객체를 생성하려면 함수 객체를 사용해야 합니다. 함수 객체 내부에는 [[Construct]] 메소드와 [[Call]] 메소드가 있습니다. [[Construct]]는 객체를 생성하는 데 사용되며, [[Call]]은 함수 호출에만 사용됩니다. new 연산자를 사용할 때.
var obj=new Object(); 내장된 Object 함수 객체를 사용하여 인스턴스화된 객체 obj를 생성합니다. var obj={}; 및 var obj=[]; 이러한 종류의 코드는 JS 엔진에 의한 객체 및 배열 생성 프로세스를 트리거합니다. function fn(){}; var myObj=new fn(); 사용자 정의 유형을 사용하여 인스턴스화된 객체를 생성합니다.
new Fn(args) 생성 과정은 다음과 같습니다. (즉, 함수 객체의 [[Construct]] 메소드가 로직과 객체 생성 과정을 담당합니다. 또한, 함수객체 자체의 생성과정(함수를 정의하거나 Function을 사용하여 함수객체를 생성하는 것을 말한다 등) 역시 다음과 같은 처리 로직을 사용하지만 특별한 곳이 있는데, 이에 대해서는 후술한다.
1. 내장 객체 obj를 생성하고 초기화합니다.
2. Fn.prototype이 Object 유형인 경우 obj의 내부 [[Prototype]]을 Fn.prototype으로 설정합니다. 그렇지 않으면 obj의 [[Prototype ]]이 해당 값을 초기화합니다(예: Object.prototype)
3. obj를 사용하고 args 매개 변수를 사용하여 Fn의 내부 [[Call]] 메서드를 호출합니다.
3.1 내부 [[Call]] 메서드는 현재 개체를 생성합니다. 실행 컨텍스트
3.2 F의 함수 본문 호출
3.3 현재 실행 컨텍스트 삭제
3.4 F의 함수 본문의 반환 값을 반환합니다. F의 함수 본문에 반환 값이 없으면 정의되지 않은4. [[ Call의 반환 값]]이 Object 유형이면 이 값이 반환되고, 그렇지 않으면 obj
2단계에서 프로토타입은 객체가 표시하는 프로토타입 속성을 참조하고, [[Prototype은 ]]는 객체의 내부 Prototype 속성(암시적)을 나타냅니다.
객체의 프로토타입 체인을 구성하는 것은 객체의 명시적인 프로토타입 속성이 아닌 내부의 암시적 [[Prototype]]입니다. 표시된 프로토타입은 함수 객체에서만 의미가 있습니다. 위의 생성 과정에서 볼 수 있듯이 함수의 프로토타입은 이러한 방식으로 파생 객체의 암시적 [[Prototype]] 속성에 할당됩니다. 규칙, 파생 객체의 프로토타입 객체, 함수 사이에는 속성과 메소드의 상속/공유 관계만 존재합니다.
코드를 사용하여 몇 가지 확인을 수행하세요.
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(){}
//the value of implicit [[Prototype]] property of those objects derived from fn will be assigned to fn.prototype
fn.prototype={ attr1:"aaa", attr2:"bbb"};
var obj=new fn();
document.write(obj.attr1 + "
"); //result: aaa
document.write(obj.attr2 + "
"); //result: bbb
document.write(obj instanceof fn); //result: true
document.write("
");
//I change the prototype of fn here, so by the algorithm of Prototype the obj is no longer the instance of fn,
//but this won't affect the obj and its [[Prototype]] property, and the obj still has attr1 and attr2 properties
fn.prototype={};
document.write(obj.attr1 + "
"); //result: aaa
document.write(obj.attr2 + "
"); //result: bbb
document.write(obj instanceof fn); //result: false
关于创建过程返回值的验证:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(){
//according to step 4 described above,
//the new fn() operation will return the object { attr1: 111, attr2: 222 }, it's not an instance of fn!
return { attr1: 111, attr2: 222 };
}
fn.prototype={ attr1:"aaa", attr2:"bbb"};
var obj=new fn();
document.write(obj.attr1 + "
"); //result: 111
document.write(obj.attr2 + "
"); //result: 222
document.write(obj instanceof fn); //result: false
做个练习经过上面的理解应,请写出下面这幅图的实现代码。图中CF是一个函数,Cfp是CF的prototype对象,cf1, cf2, cf3, cf4, cf5都是CF的实例对象。虚线箭头表示隐式Prototype关系,实线箭头表示显示prototype关系。
供参考的实现方案:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function CF(q1, q2){
this.q1=q1;
this.q2=q2;
}
CF.P1="P1 in CF";
CF.P2="P2 in CF";
function Cfp(){
this.CFP1="CFP1 in Cfp";
}
CF.prototype=new Cfp();
var cf1=new CF("aaa", "bbb");
document.write(cf1.CFP1 + "
"); //result: CFP1 in Cfp
document.write(cf1.q1 + "
"); //result: aaa
document.write(cf1.q2 + "
"); //result: bbb
로컬 속성 및 상속 속성객체는 암시적 프로토타입 체인을 통해 속성과 메서드를 상속받을 수 있지만 프로토타입도 일반 객체, 즉 일반 인스턴스화된 객체입니다. 순전히 추상적인 데이터 구조 설명보다 따라서 지역 속성과 상속 속성에 문제가 있습니다.
먼저 객체 속성을 설정하는 과정을 살펴보겠습니다. JS는 속성이 JavaScript 코드에서 설정될 수 있는지, for in 등에 의해 열거될 수 있는지 여부를 나타내기 위해 객체의 속성을 설명하는 데 사용되는 속성 세트를 정의합니다.
obj.propName=value의 할당문을 처리하는 단계는 다음과 같습니다.
1. propName의 속성이 설정할 수 없는 경우
을 반환합니다. obj.propName이 존재하지 않는 경우. , obj에 대한 속성을 생성합니다. 이름은 propName
입니다. obj.propName의 값을 value
로 설정합니다. 값 설정 프로세스에서 Prototype 체인을 고려하지 않는 것을 볼 수 있습니다. obj의 내부 [[Prototype]]은 인스턴스입니다. obj와 속성을 공유할 뿐만 아니라, 이를 수정하면 다른 객체에도 영향을 미칠 수 있습니다.
위의 CF 및 Cfp 예를 사용하여 인스턴스 객체 cf1에는 로컬 속성 q1, q2 및 상속된 속성 CFP1이 있습니다. cf1.CFP1=""가 실행되면 cf1에는 로컬 속성 CFP1이 있습니다. 다음과 같습니다:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var cf1=new CF("aaa", "bbb");
var cf2=new CF(111, 222);
document.write(cf1.CFP1 + "
"); //result: CFP1 in Cfp
document.write(cf2.CFP1 + "
"); //result: CFP1 in Cfp
//it will result in a local property in cf1
cf1.CFP1="new value for cf1";
//changes on CF.prototype.CFP1 will affect cf2 but not cf1, because there's already a local property with
//the name CFP1 in cf1, but no such one in cf2
CF.prototype.CFP1="new value for Cfp";
document.write(cf1.CFP1 + "
"); //result: new value for cf1
document.write(cf2.CFP1 + "
"); //result: new value for Cfp
의미 혼란이 있으신가요? 아직도 위의 CF, Cfp 예시 시나리오를 사용하고 계시나요?
Prototype의 메커니즘에 따르면 객체 cf1, cf2 등은 모두 객체 Cfp의 속성과 메서드를 상속한다고 말할 수 있으므로 이들 사이에는 상속 관계가 있다고 해야 합니다. 속성의 상속/공유는 암시적 프로토타입 체인을 따라 작동하므로 상속 관계도 이 체인을 따라 이해되어야 합니다.
다시 instanceOf 연산을 살펴보겠습니다. cf1이 CF의 인스턴스 객체이고, CF가 Cfp의 인스턴스 객체라고 말하는 대신, cf1이 CF에서 상속된다고 해야 할까요? 하지만 CF는 타사 팩토리 역할만 하며 CF와 cf1 사이에는 속성 상속 관계가 없습니다.
CF와 Cfp를 전체적으로 이해하는 것도 무리입니다.
프로토타입은 자바스크립트와 객체지향 개념을 억지로 결합할 필요가 없습니다. 자바스크립트는 제한된 객체지향 기능만을 가지고 있으며, 다른 관점에서 보면 함수형 언어 또는 동적 언어로 간주할 수 있습니다. 따라서 여러 언어 기능을 통합한 간소화된 버전입니다.
객체 모델우리는 어디에 있는가?1. JavaScript의 데이터 유형을 이해합니다. , Number와 같은 시스템 내장 개체에는 여러 ID가 있다는 것이 분명합니다. a) 그 자체는 함수 개체이고 엔진에 의해 내부적으로만 구현됩니다. b) 데이터 유형을 나타내며 이를 사용하여 정의하고 작동할 수 있습니다. 해당 유형의 데이터, c) 내부 데이터 구조, JavaScript 객체로 패키징된 다양한 생성자 등과 같은 엔진의 내부 구현 메커니즘이 그 뒤에 숨겨져 있습니다.
2. 프로토타입 메커니즘, 객체가 이를 통해 속성과 메소드를 상속하는 방법, 객체 생성 프로세스 중 JS 엔진 내부에서 프로토타입 관계가 설정되는 방법을 이해합니다.
사용자 정의 함수 개체 자체의 생성 과정을 이해한 후에는 JavaScript 개체 모델에 대한 포괄적인 개요를 얻을 수 있습니다.
함수 객체 생성 과정JavaScript 코드에서 함수를 정의하거나 Function을 호출하여 함수를 생성할 때 Function 함수는 결국 다음과 유사한 형식으로 호출됩니다. var newFun=함수(funArgs, funBody); 함수 객체를 생성하는 주요 단계는 다음과 같습니다.
1. 내장 객체 fn을 생성합니다
2. fn의 내부 [[Prototype]]을 Function.prototype으로 설정합니다
. 논리적 참조 객체 생성 과정의 3단계를 처리하는 내부 구현 메소드인 내부 [[ Call]] 속성
4. 논리적 참조를 처리하는 내부 구현 메소드인 내부 [[Construct]] 속성을 설정합니다. 객체 생성 프로세스의 1,2,3,4
5단계. fn.length를 funArgs.length로 설정하고, 함수에 매개변수가 없으면 fn.length를 0
6으로 설정합니다. new Object() 객체 객체 fnProto
를 만듭니다. 7. fnProto.constructor를 fn
8로 설정합니다. fn.prototype을 fnProto
로 설정합니다. 1단계와 6단계의 차이점은 다음과 같습니다. 그 1단계는 단지 Object 객체를 구현하는데 사용되는 내부 데이터 구조(빌드인 객체 구조)를 생성하고 필요한 내부 초기화 작업을 완료하지만, 그 [[Prototype]], [[Call]], [[Construct]] 그리고 다른 속성은 null이거나 내부 초기화 값이어야 합니다. 즉, 어떤 객체도 가리키지 않거나([[Prototype]]과 같은 속성의 경우) 어떤 처리도 포함하지 않는 것으로 이해할 수 있습니다([[Call과 같은 속성의 경우). ]], [[Construct]] 방법). 6단계에서는 앞서 설명한 객체 생성 과정에 따라 새로운 객체를 생성하고 해당 객체의 [[프로토타입]] 등을 설정합니다.
위의 처리 단계에서 알 수 있듯이 함수를 정의할 때마다 해당 함수의 프로토타입은 Object 인스턴스이므로 기본적으로 사용자 정의 함수의 인스턴스 개체를 생성할 때 해당 프로토타입 체인은 Object.prototype을 가리킵니다.
또한 함수의 특별한 특징은 [[Call]]과 [[Construct]] 처리 논리가 동일하다는 것입니다.
JavaScript 객체 모델
빨간색 점선은 암시적 프로토타입 체인을 나타냅니다.
이 개체 모델 다이어그램에는 내용이 너무 많습니다. 확인을 위해 몇 가지 테스트 코드를 작성해야 합니다. 이 그림을 완전히 이해하고 나면 JavaScript 언어에 대한 이해도가 거의 동일해집니다. 다음은 몇 가지 추가 지침입니다.
1. 그림에서 내장 함수 생성자가 언급된 곳이 여러 군데 있습니다. 이는 동일한 개체이며 테스트하고 확인할 수 있습니다.
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
Function==Function.constructor //result: true
Function==Function.prototype.constructor //result: true
Function==Object.constructor //result: true
//Function also equals to Number.constructor, String.constructor, Array.constructor, RegExp.constructor, etc.
function fn(){}
Function==fn.constructor //result: true
이는 다음과 같은 몇 가지 문제를 보여줍니다. 시스템의 내장 함수 생성자(내장 함수 생성자)를 가리키는 함수는 시스템의 모든 함수가 Function에서 구성됩니다.
2. 왼쪽 하단에 있는 obj1, obj2...objn 범위는 다음과 같은 코드로 생성된 객체를 나타냅니다. function fn1(){};
객체는 로컬 생성자 메서드가 아니지만 프로토타입 체인, 즉 fn.prototype.constructor에서 상속된 생성자 메서드를 가져옵니다. 함수 객체의 생성 프로세스를 보면 그것이 fn 자체라는 것을 알 수 있습니다.
오른쪽 하단의 obj1, obj2...objn 범위는 다음과 같은 코드로 생성된 객체를 나타냅니다. var obj1=new Object() 또는 var obj1=new Number(123) ; 또는 obj1=/w /;등. 따라서 이러한 객체의 프로토타입 체인을 가리키는 방식, 프로토타입 체인에서 상속된 생성자의 값(해당 생성자가 내장 Number 생성자인지 내장 Object 생성자인지 등을 참조)은 특정 객체에 따라 달라집니다. 객체 유형. 또한 var obj=new Object(123); 이 방법으로 생성된 객체의 유형은 여전히 Number이며 매개변수 값의 유형에 따라 결정되어야 합니다.
마찬가지로 로컬 생성자가 없지만 프로토타입 체인에서 상속된 생성자 메서드, 즉 내장 *** 생성자를 얻습니다. 구체적인 생성자는 데이터 유형에 따라 결정됩니다.
3. 그림의 Prototype 체인에 대한 추가 설명:
Object.prototype은 전체 체인의 끝점이며 내부 [[Prototype]]은 null입니다.
모든 함수의 프로토타입 체인은 Function.prototype을 가리킵니다.
Function의 Prototype 체인은 Function.prototype을 가리키며, 이는 디자이너가 부트스트랩되도록 Function을 설계했기 때문에 사양에 필요한 것입니다. Function 프로토타입 체인이 이런 방식으로 디자인되면 Function.constructor==Function 및 Function instanceOf Function이 모두 true가 됩니다. 게다가 Function은 이미 최상위 생성자이지만 Function 자체도 무언가에 의해 생성되어야 하므로 부트스트래핑은 의미상 합리적입니다.
Function.prototype의 프로토타입 체인은 사양에서도 요구하는 Object.prototype을 가리킵니다. 먼저 Function.prototype은 Function의 인스턴스 객체(typeof Function.prototype은 Function이라는 것을 알 수 있고, 내부적으로 Prototype 체인이 추가로 설정되어 있기 때문에 instanceOf는 테스트를 통과할 수 없다)이므로 Prototype의 규칙에 따라 Function.prototype의 내부 함수 [ [Prototype]] 값은 Function.prototype 객체여야 합니다. 즉, 프로토타입 체인이 자신을 가리킵니다. 한편으로는 프로토타입 체인에 무한 루프가 생성되고 다른 한편으로는 그 자체가 끝점이 됩니다. 결과적으로 모든 함수 객체는 더 이상 Object에서 파생되지 않습니다. 이 필수 요구 사항을 추가한 후 프로토타입 체인에는 엔드포인트가 하나만 있습니다.
4. Function.prototype은 함수 객체이므로 표시되는 프로토타입 속성, 즉 Function.prototype.prototype이 있어야 하지만 FireFox에서만 액세스할 수 있고 IE, Opera 또는에서는 액세스할 수 없습니다. 원정 여행. 따라서 그림에는 존재하지 않음을 나타내는 기호가 사용됩니다.
5. 기본적으로 사용자 정의 함수의 [[Prototype]] 값은 Object.prototype입니다. 즉, 암시적 Prototype 체인이 Object.prototype을 가리키므로 그림에 표시됩니다. 그러나 이것이 항상 그런 경우라는 의미는 아닙니다. 사용자 정의 함수의 프로토타입 속성을 설정하면 상황이 달라집니다.
실행 모델실행 컨텍스트 소개JavaScript 코드가 실행되는 곳 실행이 있습니다 context는 JavaScript 런타임 범위, 수명 등의 처리를 완료하는 데 사용되는 개념이자 메커니즘입니다. 실행 컨텍스트에는 변수 개체, 변수 인스턴스화, 범위/범위 체인 등과 같은 개념이 포함됩니다. 다양한 시나리오/실행 환경에서 처리에 약간의 차이가 있습니다. 이러한 시나리오는 아래에 설명되어 있습니다.
함수 객체는 사용자 정의 함수 객체와 시스템 내장 함수 객체로 구분됩니다. 사용자 정의 함수 객체는 아래 설명된 메커니즘에 따라 처리되지만 내장 함수 객체는 특정 구현과 관련됩니다. , ECMA 사양은 이를 시행합니다. 컨텍스트 처리에 대한 요구 사항은 없습니다. 즉, 일반적으로 이 섹션에 설명된 내용에 적합하지 않습니다.
실행되는 JavaScript 코드는 세 가지 유형으로 나누어지며, 이 세 가지 유형의 처리 차이점은 나중에 설명하겠습니다.
1. 글로벌 코드, 즉 어떤 기능에도 속하지 않는 글로벌 코드입니다. js 파일, HTML 페이지에 포함된 js 코드 등
2. Eval Code는 eval() 함수를 사용해 동적으로 실행되는 JS 코드입니다.
3. 사용자 정의 함수의 함수 본문 JS 코드인 함수 코드입니다.
기본 원리사용자 정의 함수에서는 매개변수를 전달하고 함수에 지역 변수를 정의할 수 있습니다. 함수 본문 코드는 이러한 입력 매개변수와 지역 변수를 사용할 수 있습니다. 그 뒤에 있는 메커니즘은 무엇입니까?
JS 실행 흐름이 함수에 들어가면 JavaScript 엔진은 내부적으로 변수 개체라는 개체를 생성합니다. 함수의 각 매개변수에 해당하는 속성을 Variable Object에 추가합니다. 속성의 이름과 값은 매개변수의 이름과 값과 동일합니다.函数中每声明一个变量,也会在Variable Object上添加一个属性,名字就是变量名,因此为变量赋值就是给Variable Object对应的属性赋值。在函数中访问参数或者局部变量时,就是在variable Object上搜索相应的属性,返回其值。
一般情况下Variable Object是一个内部对象,JS代码中无法直接访问。规范中对其实现方式也不做要求,因此它可能只是引擎内部的一种数据结构。
大致处理方式就这样,但作用域的概念不只这么简单,例如函数体中可以使用全局变量、函数嵌套定义时情况更复杂点。这些情况下怎样处理? JavaScript引擎将不同执行位置上的Variable Object按照规则构建一个链表,在访问一个变量时,先在链表的第一个Variable Object上查找,如果没有找到则继续在第二个Variable Object上查找,直到搜索结束。这就是Scope/Scope Chain的大致概念。
下面是各个方面详细的处理。
Global ObjectJavaScript的运行环境都必须存在一个唯一的全局对象-Global Object,例如HTML中的window对象。Global Object是一个宿主对象,除了作为JavaScript运行时的全局容器应具备的职责外,ECMA规范对它没有额外要求。它包Math、 String、Date、parseInt等JavaScript中内置的全局对象、函数(都作为Global Object的属性),还可以包含其它宿主环境需要的一些属性。
Variable Object上面简述了Variable Object的基本概念。创建Variable Object,将参数、局部变量设置为Variable Object属性的处理过程叫做Variable Instatiation-变量实例化,后面结合Scope Chain再进行详细说明。
Global CodeVariable Object就是Global Object,这是Variable Object唯一特殊的地方(指它是内部的无法访问的对象而言)。
var globalVariable = "WWW";
document.write(window.globalVariable); //result: WWW
위의 코드는 전역 코드 모드에서 실행됩니다. Variable Object의 처리에 따라 globalVariable 변수가 정의되면 이 속성이 전역 객체(예: 창) 객체에 추가되므로 출력은 WWW입니다.
함수 코드Variable Object는 Activation Object라고도 합니다(몇 가지 차이점이 있으므로 차이점을 보여주기 위해 사양에서 새로운 이름을 부여합니다. Variable Object라고 합니다. Global Code/Eval Code에서는 Function Code에서는 Activation Object라고 합니다.
함수 실행에 들어갈 때마다 새로운 Activation Object 객체가 생성되고, 이후 Argument 객체가 생성되어 Activation Object의 속성으로 설정된 후 Variable Instantiation이 처리됩니다.
함수를 종료하면 활성화 개체가 삭제됩니다(메모리 해제는 아니지만 가비지 수집될 수 있음).
인수 개체의 속성: length: 전달된 매개변수의 실제 개수입니다. 참고로 함수 객체 생성 과정을 보면 함수 객체의 길이는 함수 정의 시 필요한 매개변수의 개수입니다.
호출자: 실행된 함수 객체 자체입니다. 예를 들어 재귀 호출이 필요한 경우 함수 개체가 자신을 참조할 수 있도록 하는 것이 목적입니다.
function fnName(...) { ... }는 이렇게 함수를 정의하고, 함수 본문에서 fnName을 사용하여 재귀 호출을 완료할 수 있습니다. var fn=function(...) { ... } 이 방법으로 익명 함수를 정의합니다. 이름을 사용하여 함수 본문에서 자신을 참조할 수 없습니다.
매개변수 목록: 호출자가 실제로 전달한 매개변수 목록입니다. 이 매개변수 목록은 색인을 사용하여 실제 매개변수에 액세스하는 방법을 제공합니다. 변수 인스턴스화는 함수가 선언될 때 지정된 매개변수 목록이 있는 경우 처리 중에 활성화 개체 개체에 속성을 추가합니다. 함수 선언에 매개변수 목록이 제공되지 않거나, 실제 매개변수 개수가 함수 선언에 있는 매개변수 개수와 다른 경우, 각 매개변수는 인수를 통해 접근할 수 있습니다.
인수의 매개변수 목록과 활성화 개체의 매개변수 속성은 동일한 매개변수 개체를 참조합니다(수정된 경우 두 위치에 모두 반영됩니다). 사양에서는 인수가 배열 객체일 것을 요구하지 않습니다. 테스트는 다음과 같습니다.
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
var argumentsLike = { 0: "aaa", 1: 222, 2: "WWW", length: 3, callee: function() { } };
document.write(argumentsLike[2] + "
"); //result: WWW
document.write(argumentsLike[1] + "
"); //result: 222
//convert the argumentsLike to an Array object, just as we can do this for the arguments property
var array = [].slice.apply(argumentsLike);
document.write(array instanceof Array); //result: true
document.write("
");
document.write(array.reverse().join("|")); //result: WWW|222|aaa
Eval CodeVariable Object就是调用eval时当前执行上下文中的Variable Object。在Global Code中调用eval函数,它的Variable Object就是Global Object;在函数中调用eval,它的Variable Object就是函数的Activation Object。
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4
function fn(arg){
var innerVar = "variable in function";
eval(' \
var evalVar = "variable in eval"; \
document.write(arg + "
"); \
document.write(innerVar + "
"); \
');
document.write(evalVar);
}
fn("arguments for function");
输出结果是:
arguments for function
variable in function
variable in eval
说明: eval调用中可以访问函数fn的参数、局部变量;在eval中定义的局部变量在函数fn中也可以访问,因为它们的Varible Object是同一个对象。
Scope/Scope Chain首先Scope Chain是一个类似链表/堆栈的结构,里面每个元素基本都是Variable Object/Activation Object。
其次存在执行上下文的地方都有当前Scope Chain,可以理解为Scope Chain就是执行上下文的具体表现形式。
Global CodeScope Chain只包含一个对象,即Global Object。在开始JavaScript代码的执行之前,引擎会创建好这个Scope Chain结构。
Function Code函数对象在内部都有一个[[Scope]]属性,用来记录该函数所处位置的Scope Chain。
创建函数对象时,引擎会将当前执行环境的Scope Chain传给Function的[[Construct]]方法。[[Construct]]会创建一个新的Scope Chain,内容与传入的Scope Chain完全一样,并赋给被创建函数的内部[[Scope]]属性。在前面函数对象创建过程一节中,这个处理位于步骤4和5之间。
进入函数调用时,也会创建一个新的Scope Chain,包括同一个函数的递归调用,退出函数时这个Scope Chain被丢弃。新建的Scope Chain第一个对象是Activation Object,接下来的内容与内部[[Scope]]上存储的Scope Chain内容完全一样。
Eval Code进入Eval Code执行时会创建一个新的Scope Chain,内容与当前执行上下文的Scope Chain完全一样。
实例说明Scope Chain的原理就上面这些,必须结合JS代码的执行、Variable Instantiation的细节处理,才能理解上面这些如何产生作用,下面用一个简单的场景来综合说明。假设下面是一段JavaScript的Global Code:
var outerVar1="variable in global code";
function fn1(arg1, arg2){
var innerVar1="variable in function code";
function fn2() { return outerVar1+" - "+innerVar1+" - "+" - "+(arg1 + arg2); }
return fn2();
}
var outerVar2=fn1(10, 20);
执行处理过程大致如下:
1. 初始化Global Object即windo0,0)">20
);
실행과정은 대략 다음과 같습니다.
1. 윈도우 객체인 Global 객체와 윈도우 객체 자체인 Variable 객체를 초기화합니다. 창 개체만 포함하는 범위_1이라고 가정하고 범위 체인 개체를 만듭니다.
2. JS 소스 코드를 스캔하고(소스 코드를 읽으면 어휘 및 구문 분석 과정이 있을 수 있음) 결과에서 정의된 변수 이름과 함수 객체를 얻을 수 있습니다. 스캔 순서에 따라:
2.1 변수 externalVar1을 발견하고, window 객체에 externalVar1 속성을 추가합니다. 값은 정의되지 않습니다.
2.2 함수 fn1의 정의를 발견하고, 이 정의를 사용하여 함수 객체를 생성합니다. 생성 프로세스에 전달된 범위 체인은 range_1 입니다. 이름이 fn1이고 값이 반환된 함수 객체인 window 속성에 결과를 추가합니다. fn1의 내부 [[Scope]]는scope_1입니다. 또한 생성 과정에서는 함수 본문에 있는 JS 코드에 대해 특별한 처리를 수행하지 않는다는 점에 유의하세요. 이는 함수 본문에 있는 JS 코드의 스캔 결과만 함수 개체의 내부 속성에 저장하는 것으로 이해될 수 있습니다. 함수가 실행될 때 처리합니다. 이는 함수 코드, 특히 중첩된 함수 정의의 변수 인스턴스화를 이해하는 데 중요합니다.
2.3 변수 externalVar2를 발견하고 window 객체에 externalVar2 속성을 추가하면 값이 정의되지 않습니다.
3. 값을 "전역 코드의 변수"로 할당합니다.
4. fn1 함수를 실행하고 반환 값을 가져옵니다.
4.1 활성화 개체를 생성합니다(활성화_1이라고 가정). 범위 체인을 생성하고 범위_2의 첫 번째 개체는 활성화_1입니다. 개체는 창 개체입니다(fn1의 [[Scope]], 즉 범위_1의 내용에서 가져옴).
4.2 프로세스 매개변수 목록입니다. 활성화_1에 arg1 및 arg2 속성을 각각 값 10과 20으로 설정합니다. 인수 객체를 생성하고 설정하고 인수를 활성화_1의 속성으로 설정합니다.
4.3 fn1의 함수 본문에서 2단계와 유사한 프로세스를 수행합니다.
4.3.1 변수 innerVar1을 검색하고 활성화_1에 innerVar1 속성을 추가합니다. object, 값은 정의되지 않았습니다.
4.3.2 함수 fn2의 정의를 찾고 이 정의를 사용하여 함수 객체를 생성하며 생성 프로세스에 전달된 스코프 체인은scope_2입니다(함수 fn1의 스코프 체인은 내용입니다) 현재 실행 컨텍스트의). fn2라는 이름과 반환된 함수 객체의 값을 사용하여 활성화_1의 속성에 결과를 추가합니다. fn2의 내부 [[Scope]]는scope_2입니다.
4.4 innerVar1 할당문을 실행하고 "함수 코드의 변수"에 값을 할당합니다.
4.5 fn2 실행:
4.5.1 활성화 개체를 생성합니다. 활성화_2라고 가정하고, 새로운 범위 체인을 생성합니다. 범위_3, 범위_3의 첫 번째 개체는 활성화_2, 다음 개체는 활성화_1이라고 가정합니다. 객체(fn2의 [[Scope]], 즉scope_2에서 가져옴)
4.5.2 프로세스 매개변수 목록. fn2에는 매개변수가 없으므로 인수 객체를 생성하고 이를 활성화_2의 속성으로 설정하면 됩니다.
4.5.3 fn2의 함수 본문에 대해 2단계와 유사한 프로세스를 수행하면 변수 정의 및 함수 선언이 발견되지 않습니다.
4.5.4 함수 본체를 실행합니다. 변수 참조의 경우 범위_3에서 검색합니다. 이 예에서는 innerVar1, arg1 및 arg2가 활성화_1에서 발견됩니다.
4.5.5 범위_3 및 활성화_2를 폐기합니다(가비지 수집 가능함을 의미).
4.5.6 fn2의 반환값을 반환합니다.
4.6 활성화_1과 범위_2를 삭제합니다.
4.7 결과 반환.
5. 결과를 externalVar2에 할당합니다.
그 외의 경우에는 Scope Chain과 Variable Instantiation을 위 과정과 유사하게 분석할 수 있습니다.
위 사실에 따르면