도움말
인텔리퀀트의 사용방법과 메뉴얼입니다.

9. 객체 모델 심화

JavaScript는 프로토 타입을 기반으로 객체 기반 언어입니다. 클래스를 기반으로하는 것이 아닙니다. 이 기초 부분의 차이에 의해, JavaScript는 어떻게 개체의 계층 구조를 만들 수 있도록되었는지, 속성 및 그 값의 상속을 허용하고 있는지가 명확하지 않습니다. 이 장에서는 이러한 실태를 분명히합니다.

이 장에서는 독자가 JavaScript를 어느 정도 이해하고있는, 및 간단한 개체를 만드는 데 JavaScript 함수를 사용하여 수 있다고합니다.

클래스 기반 언어와 프로토 타입 기반 언어

Java 나 C + + 등의 클래스 기반의 객체 지향 언어는 클래스와 인스턴스는 2 개의 다른 실체가 있다는 개념을 기반으로합니다.

클래스 는 개체의 집합을 특징 짓는 모든 속성 (Java에서는 메소드와 필드를 C + +에서 멤버를 속성으로 간주합니다)를 정의합니다. 클래스는 그것이 나타내는 개체의 집합의 특정 멤버가 아닌 추상적 인 것입니다. 예를 들어, Employee 클래스는 모든 직원의 집합을 나타냅니다.
한편, 인스턴스 는 클래스를 구체화 한 것입니다. 즉, 클래스의 멤버 중 하나입니다. 예를 들어, Victoria 는 Employee 클래스의 인스턴스가 될 수 있습니다. 이 인스턴스는 특정 개인을 직원으로 나타내는 것입니다. 인스턴스는 부모 클래스의 속성을 (과부족없이) 정확하게 유지합니다. JavaScript와 같은 프로토 타입 기반 언어는이 구별이 없습니다. 단순히 개체가있을뿐입니다. 프로토 타입 기반 언어는 원형 개체 ( prototypical object ) 라는 개념이 있습니다. 이 개체는 새로운 개체의 초기 속성 반환 할 템플릿으로 사용됩니다. 모든 개체는 고유의 속성을 지정할 수 있습니다. 이것은 객체 생성시에도 실행시에도 가능합니다. 또한 모든 개체가 다른 개체에 대한 프로토 타입 으로 연관시킬 수 있습니다. 두 번째 개체가 첫 번째 개체의 속성을 공유 할 수 있습니다.

클래스의 정의

클래스 기반 언어는 독립적 인 클래스 정의 에서 클래스를 정의합니다. 정의는 생성자 라는 특별한 메소드를 사용하여 클래스의 인스턴스를 만들 수 있습니다. 생성자 메서드는 인스턴스 속성의 초기 값을 지정할 수 있습니다. 또한 만들 때 다른 적절한 작업을 수행 할 수 있습니다. new 연산자를 생성자 메서드와 함께 사용하여 클래스의 인스턴스를 만들 수 있습니다.

JavaScript 비슷한 모델에 따릅니다 만, 생성자와 별도로되어있는 클래스 정의가 없습니다. 대신, 속성과 값을 갖는 특별한 초기 설정을 가진 개체를 만드는 생성자 함수를 정의합니다. 어떤 JavaScript 함수도 생성자로 사용할 수 있습니다. new 연산자를 생성자 함수와 함께 사용하여 새 개체를 만듭니다.

하위 클래스와 상속

클래스 기반 언어는 클래스 정의를 통해 클래스 계층을 만듭니다. 클래스 정의는 새로운 클래스가 기존 클래스의 서브 클래스 가되도록 지정할 수 있습니다. 서브 클래스는 슈퍼 클래스의 모든 속성을 상속합니다. 또한 새로운 속성을 추가하거나 상속 된 속성을 변경할 수도 있습니다. 예를 들어, Employee 클래스는 name 및 dept 속성만을 포함하고 있으며, Manager 는 reports 속성을 추가하고, Employee 의 서브 클래스이다 고합니다. 이 경우 Manager 클래스의 인스턴스는 name , dept , reports 의 세 가지 속성을 가지게됩니다.

JavaScript는 원형 객체를 어떤 생성자 함수에 연결할 수있게하여 상속을 구현하고 있습니다. 따라서 동일하게 Employee 와 Manager 의 예를 만들 수 있지만, 사용하는 용어가 약간 다릅니다. 먼저 Employee 생성자 함수를 정의합니다. 이것은 name 및 dept 속성을 지정합니다. 다음 Manager 생성자 함수를 정의합니다. 이것은 reports 속성을 지정합니다. 마지막으로 새로운 Employee 오브젝트를 Manager 생성자 함수의 prototype 으로 지정합니다. 그리고 새로운 Manager 를 만들면이 개체는 Employee 개체에서 name 및 dept 속성을 상속합니다.

속성 추가 및 제거

클래스 기반 언어는 일반적으로 클래스를 컴파일 할 때 생성, 컴파일시 혹은 실행시에 클래스의 인스턴스를 만듭니다. 클래스 정의 후 그 클래스의 속성의 개수와 형식을 변경할 수 없습니다. 그러나 JavaScript는 어떤 개체도 런타임에 속성을 추가하거나 삭제할 수 있습니다. 개체의 집합에서 프로토 타입으로 사용되는 개체에 속성을 추가하면 그것을 프로토 타입 객체에 새로운 속성이 추가됩니다.

차이점 요약

다음 표에서는 이러한 차이의 일부를 짧게 정리합니다. 이 장의 나머지에서 JavaScript 생성자와 프로토 타입을 사용하여 개체 계층을 만드는 방법에 대한 자세한 설명합니다. 또한,이 방법이 Java에서는 어떻게 바뀔까라는 비교합니다.

표 8.1 : 클래스 기반 (Java)의 객체 시스템과 프로토 타입 기반 (JavaScript)의 개체 시스템과의 비교

클래스 기반 (Java) 프로토 타입 기반 (JavaScript)
클래스와 인스턴스는 다른 실체입니다. 모든 개체는 인스턴스입니다.
클래스 정의를 사용하여 클래스를 정의합니다. 또한 생성자 메서드를 사용하여 클래스를 인스턴스화합니다. 생성자 함수를 사용하여 객체의 집합을 정의하고 만듭니다.
new 연산자를 사용하여 단일 개체를 만듭니다. 같습니다.
기존 클래스의 서브 클래스를 정의하는 클래스 정의를 사용하여 개체 계층을 구축합니다. 생성자 함수에 연결된 프로토 타입으로 객체를 할당하여 개체 계층을 구축합니다.
클래스 체인 따라 속성을 상속합니다. 프로토 타입 체인에 따라 속성을 상속합니다.
클래스 정의가 클래스의 모든 인스턴스의 모든 속성을 정의합니다. 런타임에 동적 속성을 추가 할 수 없습니다. 생성자 함수 또는 프로토 타입 속성의 초기 설정 을 지정합니다. 개별 개체와 개체의 집합에 동적 속성을 추가하거나 그들에서 속성을 제거 할 수 있습니다.

직원의 예

여기에서 다음 그림과 직원의 계층을 사용하는 것입니다.

[그림 찾을것] https://developer.mozilla.org/ja/docs/JavaScript/Guide/Details_of_the_Object_Model
그림 8.1 : 간단한 개체 계층

이 예제에서는 다음 개체를 사용하고 있습니다.

Employee 는 속성 name (기본 값은 빈 문자열) 및 dept (기본값은 "general")가 있습니다.
Manager 는 Employee 를 기반으로하고 있습니다. 이것은 reports 속성 (기본값은 빈 배열 값으로 Employee 개체의 배열을 갖게합니다)를 추가합니다.
WorkerBee 도 Employee 를 기반으로하고 있습니다. 이것은 projects 속성 (기본값은 빈 배열 값으로 문자열 배열을 갖게합니다)를 추가합니다.
SalesPerson 은 WorkerBee 을 기반으로하고 있습니다. 이것은 quota 속성 (기본값은 100)을 추가합니다. 또한 dept 속성을 "sales"값으로 대체합니다. 이것은
점원이 모두 같은 부서에 소속되어있는 것을 나타냅니다.
Engineer 는 WorkerBee 을 기반으로하고 있습니다. 이것은 machine 속성 (기본값은 빈 문자열)을 추가하고, dept 속성을 "engineering"값으로 대체합니다.

계층 만들기

Employee 계층을 구현하기위한 적절한 생성자 함수를 정의하는 방법은 여러 가지가 있습니다. 이 정의에 무엇을 선택하거나 응용 프로그램에서 무엇을 할 수 있도록 한 다음에 크게 의존합니다.

이 섹션에서는 매우 단순한 (그리고 비교적 유연하지 않다) 정의의 사용 방법을 보여 상속을 작동 방법을 실제로 보여줍니다. 이러한 정의는 객체 생성시 속성 값을 지정할 수 없습니다. 새로 생성 된 개체는 단순히 기본 값을 얻을뿐입니다. 이것은 나중에 변경할 수 있습니다. 그림 8.2에서는 이러한 간단한 정의를 갖춘 계층을 보여줍니다.

실제 응용 프로그램에서 개체를 만들 때 속성 값을 사용할 수있는 생성자를 정의 할 것입니다 (자세한 내용은 더 유연한 생성자 참조). 이번에는이 간단한 정의를 사용하여 상속은 어떻게 형성되는지를 실제로 보여 나가게합니다.

[그림 찾을것] https://developer.mozilla.org/ja/docs/JavaScript/Guide/Details_of_the_Object_Model
그림 8.2 : Employee 개체의 정의

다음과 같이, Java와 JavaScript의 Employee 의 정의는 비슷합니다. 양자의 차이점은 Java에서는 각 속성에 형식을 지정해야하는 반면, JavaScript는 그 필요가 없다는 것입니다. 또한 Java 클래스에서는 명시적인 생성자 메서드를 작성해야합니다.

JavaScript Java
Function  Employee () {
  this . name = "" ;
  this . dept = "general" ;
}
public  class  Employee {
   public  String name;
   public  String DEPT;
   public  Employee () {
      this . name = "" ;
      this . dept = "general" ;
   }
}

Manager 및 WorkerBee 의 정의는 상속 체인에서 인접한 상위 객체의 지정 방법에 차이가 있습니다. JavaScript에서는 원형 인스턴스를 생성자 함수의 prototype 속성 값으로 추가합니다. 생성자를 정의한 후라면 언제든지 그렇게 할 수 있습니다. Java에서는 클래스 정의에서 슈퍼 클래스를 지정합니다. 클래스 정의 외부에서 슈퍼 클래스를 변경할 수 없습니다.

JavaScript Java
Function  Manager () {
  this . reports = [];
}
Manager.prototype = New  Employee;
 
Function  WorkerBee () {
  this . projects = [];
}
WorkerBee.prototype = New  Employee;
public  class  Manager extends  Employee {
   public  Employee [] reports;
   public  Manager () {
      this . reports = New  Employee [ 0 ];
   }
}
 
public  class  WorkerBee extends  Employee {
   public  String [] projects;
   public  WorkerBee () {
      this . projects = New  String [ 0 ];
   }
}

Engineer 및 SalesPerson 의 정의는 WorkerBee 의 자손, 따라서 Employee 의 후손이기도 한 개체를 만듭니다. 이러한 유형의 객체는 연쇄의 상위에있는 모든 개체의 속성이 있습니다. 또한 이러한 정의는 dept 속성 상속 된 값을 자동 개체 고유의 새로운 값보다 우선합니다.

JavaScript Java
Function  SalesPerson () {
   this . dept = "sales" ;
   this . quota = 100;
}
SalesPerson.prototype = New  WorkerBee;
 
Function  Engineer () {
   this . dept = "engineering" ;
   this . machine = "" ;
}
Engineer.prototype = New  WorkerBee;
public  class  SalesPerson extends  WorkerBee {
   public  double  quota;
   public  SalesPerson () {
      this . dept = "sales" ;
      this . quota = 100.0 ;
   }
}
 
public  class  Engineer extends  WorkerBee {
   public  String machine;
   public  Engineer () {
      this . dept = "engineering" ;
      this . machine = "" ;
   }
}

이러한 정의를 사용하여 속성이 기본 값을받는 객체의 인스턴스를 만들 수 있습니다. 그림 8.3에서는 이러한 JavaScript의 정의를 사용하여 새로운 개체를 만드는 방법을 보여줍니다. 또한 새로운 개체의 속성 값을 보여줍니다.

주의 : 인스턴스 라는 용어는 클래스 기반 언어에서는 특정 기술적 인 의미를 가지고 있습니다. 이러한 언어에서는 인스턴스는 클래스의 개별 멤버이며 클래스와는 근본적으로 다른 것입니다. JavaScript는 "인스턴스"이 같은 기술적인 의미는 없습니다. 왜냐하면 JavaScript는 클래스와 인스턴스 사이에 그런 차이가 없기 때문입니다. 그러나 JavaScript에 대해 이야기 할 때 "인스턴스"를 개별 생성자 함수를 사용하여 만든 개체를 뜻하는 말로, 비공식적 인 형태로 사용할 수 있습니다. 예 jane 는 Engineer 의 인스턴스이다, 부서진 말투를 할 수 있습니다. 마찬가지로, 부모 , 자식 , 조상 , 그리고 후손 이라는 용어는 JavaScript에서 공식적인 의미를 가지지 않지만, 프로토 타입 체인에서 위나 아래에있는 객체에 대해 언급 할 때, 그들을 비공식적으로 사용하여도됩니다 없습니다.

[그림 찾을것] https://developer.mozilla.org/ja/docs/JavaScript/Guide/Details_of_the_Object_Model 그림 8.3 : 간단한 정의를 이용한 객체 생성

개체의 속성

이 섹션에서는 프로토 타입 체인에서 개체 형식의 개체에서 어떻게 속성을 상속할지 또한 런타임에 속성을 추가하면 무슨 일이 일어날 지에 대해 논의한다.

속성 상속

다음 문장을 사용하여 ( 그림 8.3 에서 볼 수 있듯이), mark 개체를 WorkerBee 로 만들합니다 :

var  mark = New  WorkerBee;

JavaScript는 new 연산자에 만나면 새로 일반 객체를 만들고이 새로운 개체를 this 키워드 값으로 WorkerBee 생성자 함수에 전달합니다. 생성자 함수는 명시 적으로 projects 속성 값을 설정합니다. 또한 내부 __proto__ 속성 WorkerBee.prototype 를 설정합니다 (이 속성 이름은 처음과 마지막 두 문자 밑줄이 붙어 있습니다). __proto__ 속성은 속성 값을 반환에 사용되는 프로토 타입 체인을 결정합니다. 이러한 속성이 설정되면 JavaScript는 새로운 개체를 반환 입문 변수 mark 해당 개체를 설정합니다.

이 과정에서 mark 가 프로토 타입 체인에서 상속되는 속성에 명시 적으로 mark 개체 값 ( 로컬 값)를 저장하지 않습니다. 속성의 값을 사용하는 경우, JavaScript는 우선 그 값이 객체에 존재하고 있는지를 확인합니다. 존재하는 경우는 그 값이 반환됩니다. 값이 로컬에 존재하지 않는 경우, JavaScript는 프로토 타입 체인을 확인합니다 ( __proto__ 속성을 사용). 프로토 타입 체인에있는 객체가 속성 값을 가지고있는 경우는 그 값이 반환됩니다. 그런 속성을 찾을 수없는 경우, JavaScript는 개체 속성이 없다고보고합니다. 이렇게하여 mark 개체는 다음과 같은 속성과 값을 갖게됩니다 :

mark.name = "" ;
mark.dept = "general" ;
mark.projects = [];

mark 개체 mark.__proto__ 의 원형 개체에서 name 및 dept 속성 값을 상속합니다. projects 속성은 WorkerBee 생성자에서 로컬 값이 할당됩니다. 이제 JavaScript에서 속성 값 상속 수 있습니다. 이 과정의 자세한 내용은 속성 상속 다시 에서 설명합니다.

이러한 생성자 인스턴스 별 값을 전달 않기 때문에이 정보는 일반입니다. 속성 값은 WorkerBee 의해 생성되는 모든 새로운 개체에 공유되는 기본값입니다. 물론 이러한 모든 속성에서 값을 바꿀 수 있습니다. 따라서 다음과 같이하고 mark 와 관련된 정보를 제공합니다 :

mark.name = "Doe, Mark" ;
mark.dept = "admin" ;
mark.projects = "navigator" ];

속성 추가

JavaScript는 런타임에 어떤 객체에 속성을 추가 할 수 있습니다. 생성자 함수에 주어진 속성 밖에 사용할 수없는 것은 아닙니다. 하나의 개체에 고유 한 속성을 추가하려면 다음과 같이 개체에 값을 할당합니다 :

mark.bonus = 3000;

그러자 mark 개체 bonus 속성이 있습니다. 그러나 다른 어떤 WorkerBee 에도이 속성은 존재하지 않습니다.

있는 생성자 함수에 대한 프로토 타입으로 사용되는 객체에 새로운 속성을 추가하면 프로토 타입에서 속성을 상속하는 모든 개체에 속성을 추가합니다. 예를 들어 다음의 문장을 사용하면 specialty 속성을 모든 직원에 대해 추가 할 수 있습니다 :

Employee.prototype.specialty = "none" ;

JavaScript이 문을 실행하면 즉시 mark 개체도 "none" 값을 가진 specialty 속성을 갖게됩니다. 다음 그림에서는이 속성을 Employee 프로토 타입에 추가하고, Engineer 프로토 타입의 속성을 재정의 할 때의 효과를 나타냅니다.

그림 8.4 : 속성 추가

더 유연한 생성자

지금까지 본 적이 생성자 함수는 인스턴스를 만들 때 속성 값을 지정할 수 없습니다. Java처럼 생성자에 인수를주고, 인스턴스의 속성 값을 초기화 할 수 있습니다. 다음 그림에서 그것을 실현하는 하나의 방법입니다.

그림 8.5 : 생성자의 속성 지정, 그 1

다음 표에서는 Java 및 JavaScript에서 이러한 개체의 정의를 보여줍니다.
JavaScript Java
Function  Employee (name, dept) {
  this . name = name | | "" ;
  this . dept = dept | | "general" ;
}
public  class  Employee {
   public  String name;
   public  String DEPT;
   public  Employee () {
      this ( "" , "general" );
   }
   public  Employee (String name) {
      this (name, "general" );
   }
   public  Employee (String name, String dept) {
      this . name = name;
      this . dept = dept;
   }
Function  WorkerBee (projs) {
  this . projects = projs | | [];
}
WorkerBee.prototype = New  Employee;
public  class  WorkerBee extends  Employee {
   public  String [] projects;
   public  WorkerBee () {
      this ( New  String [ 0 ]);
   }
   public  WorkerBee (String [] projs) {
      projects = projs;
   }
}
Function  Engineer (mach) {
   this . dept = "engineering" ;
   this . machine = mach | | "" ;
}
Engineer.prototype = New  WorkerBee;
public  class  Engineer extends  WorkerBee {
   public  String machine;
   public  Engineer () {
      dept = "engineering" ;
      machine = "" ;
   }
   public  Engineer (String mach) {
      dept = "engineering" ;
      machine = mach;
   }
}

이러한 JavaScript의 정의는 디폴트 값 세트에 특별한 관용구를 사용하고 있습니다 :

this . name = name | | "" ;

JavaScript 논리 OR 연산자 ( | | )는 첫 번째 인수를 평가합니다. 인수가 true로 평가되는 경우 연산자는 인수를 반환합니다. 그렇지 않으면 제 2 인수의 값을 돌려줍니다. 따라서이 코드는 name 이 name 속성 값에 사용할 수있는 값인지 확인합니다. 그렇다면 this.name 에 그 값을 설정합니다. 그렇지 않으면 this.name 에 널 문자로 설정합니다. 간결함은 장에서는이 관용구를 사용합니다. 그러나 첫눈은 괴괴 망측하게 보일지 모릅니다.

주의 : 예상대로이 관용구는 생성자 함수가 false 로 변환되는 인수 ( 0 (영) 또는 빈 문자열 ( "" ) 등)로 호출되는 경우에 작동하지 않을 것이다. 이 경우, 기본값이 선택됩니다. 이러한 정의를 사용하면 개체의 인스턴스를 만들 때, 국소 적으로 정의 된 속성에 대한 값을 지정할 수 있습니다. 그림 8.5 에서 볼 수 있듯이, 다음 문장을 사용하면 새로운 Engineer 를 만들 수 있습니다 :

var  jane = New  Engineer ( "belau" );

그러자 Jane 의 속성은 다음과 같습니다 :

jane.name == "" ;
jane.dept == "engineering" ;
jane.projects == [];
jane.machine == "belau"

이러한 정의는 name 과 같은 상속 된 속성에 대해 초기 값을 지정할 수 없으므로주의하십시오. JavaScript에서 상속 된 속성에 대해 초기 값을 지정하고 싶은 경우, 생성자 함수에 추가 코드를 추가해야합니다.

지금까지 생성자 함수는 일반 객체를 생성 한 다음에 새 개체의 로컬 속성 값을 정의했습니다. 프로토 타입 체인의 상위 객체의 생성자 함수를 직접 호출하여 생성자에 속성을 더 추가 할 수 있습니다. 다음 그림에서는 이러한 새로운 정의를 보여줍니다.

그림 8.6 : 생성자의 속성 지정, 그 2

이러한 정의 중 하나를 자세히 살펴 보겠습니다. 이것은 Engineer 생성자의 새로운 정의입니다 :

Function  Engineer (name, projs, mach) {
  this . base = WorkerBee;
  this . base (name, "engineering" , projs);
  this . machine = mach | | "" ;
}

다음으로 새 Engineer 개체를 만드는합니다 :

var  jane = New  Engineer ( "Doe, Jane" , "navigator" , "javascript" ], "belau" );

JavaScript는 다음 단계를 수행합니다 :


new 연산자를 일반 객체를 생성하고 그 __proto__ 속성 Engineer.prototype 을 설정합니다.
new 연산자 Engineer 생성자에 새로운 개체를 this 키워드의 값으로 전달합니다.
생성자가 개체에 base 라는 새로운 속성을 생성하고 WorkerBee 생성자 값을 base 속성에 할당합니다. 그러면 WorkerBee 생성자는 Engineer 개체의 메소드입니다. base 라는 속성 이름은 특별한 것이 없습니다. 어떤 정당한 속성 이름을 사용할 수 있습니다. base 단순히 의도를 연상시키는 데 충분하기 때문입니다.
생성자가 base 메서드를 호출합니다. 그 인자로서 생성자에 전달 된 인수 중 2 개 ( "Doe, Jane" 및 [ "navigator", "javascript"] )와 더 "engineering" 을 전달합니다. 생성자에서 "engineering" 을 명시 적으로 사용함으로써 모든 Engineer 개체가 상속 된 dept 속성에 대해 동일한 값을 갖게하고 Employee 에서 상속 된 값을이 값을 대체합니다.
base 는 Engineer 방법이기 때문에 base 의 호출 내부에서 JavaScript가 this 키워드를 1 단계에서 만든 개체에 연결합니다. 따라서 WorkerBee 함수는 순서 "Doe, Jane" 및 "engineering" 라는 인수를 Employee 생성자 함수에 전달합니다. Employee 생성자 함수에서 돌아 오면 WorkerBee 함수는 나머지 인수를 사용하여 projects 속성을 설정합니다.
base 메서드가 반환하면 Engineer 생성자가 객체 machine 속성을 "belau" 로 초기화합니다.
생성자에서 돌아 오면, JavaScript는 새로운 개체를 jane 라는 변수에 할당합니다.

Engineer 생성자 내부에서 WorkerBee 생성자를 호출하면 Engineer 개체에 적절하게 상속을 설치 한 것이다 싶을지도 모릅니다. 실제로는 그렇지 않습니다. WorkerBee 생성자를 호출하면 호출 된 모든 생성자 함수에서 지정되는 속성을 가진, Engineer 개체가있는 것이 확실합니다. 그러나 나중에 속성을 Employee 또는 WorkerBee 프로토 타입에 추가하는 경우, 그 속성은 Engineer 개체로 전파되지 않습니다. 예를 들어 다음의 문장이 있다고합니다 :

Function  Engineer (name, projs, mach) {
  this . base = WorkerBee;
  this . base (name, "engineering" , projs);
  this . machine = mach | | "" ;
}
var  jane = New  Engineer ( "Doe, Jane" , "navigator" , "javascript" ], "belau" );
Employee.prototype.specialty = "none" ;

jane 개체 specialty 속성을 상속하지 않습니다. 동적 상속을 보장하려면 역시 프로토 타입을 명시 적으로 표시해야합니다. 대신 다음 문을 사용합니다 :

Function  Engineer (name, projs, mach) {
  this . base = WorkerBee;
  this . base (name, "engineering" , projs);
  this . machine = mach | | "" ;
}
Engineer.prototype = New  WorkerBee;
var  jane = New  Engineer ( "Doe, Jane" , "navigator" , "javascript" ], "belau" );
Employee.prototype.specialty = "none" ;

그러자 jane 개체 specialty 속성 값은 "none"입니다.

또 다른 상속 방법은 call () / apply () 메소드를 사용하는 것입니다. 다음 코드는 동일합니다 :

Function  Engineer (name, projs, mach) {
  this . base = WorkerBee;
  this . base (name, "engineering" , projs);
  this . machine = mach | | "" ;
}

Function  Engineer (name, projs, mach) {
  WorkerBee.call ( this , name, "engineering" , projs);
  this . machine = mach | | "" ;
}

JavaScript의 call () 메소드를 사용하여 구현하기가 더 깨끗해진다. base 가 전혀 필요 없기 때문입니다.

속성 상속 다시

지금까지의 섹션에서는 JavaScript 생성자와 프로토 타입 계층을 어떻게 실현하고 있는지를 설명했습니다. 이 섹션에서는 지금까지의 논의는 반드시 분명 아니었다, 세세한 부분에 대해 논의 할 것입니다.

로컬 값 상속 값

개체의 속성에 액세스하면이 장에서는 앞서 설명한 바와 같이, JavaScript는 다음 단계를 수행합니다 :

속성 값이 로컬에 있는지 확인합니다. 존재하는 경우는, 그 값을 돌려줍니다.
값이 로컬에 존재하지 않는 경우는 프로토 타입 체인을 확인합니다 ( __proto__ 속성을 사용).
프로토 타입 체인에있는 객체가 지정된 속성 값을 가지고있는 경우, 그 값을 돌려줍니다.
그런 속성을 찾을 수없는 경우 개체에 속성이 존재하지 않습니다.
이 단계의 결과는 그때까지 어떻게 객체를 정의 했느냐에 달려 있습니다. 원래 예제에서는 다음 정의를 사용했습니다.

Function  Employee () {
  this . name = "" ;
  this . dept = "general" ;
}
 
Function  WorkerBee () {
  this . projects = [];
}
WorkerBee.prototype = New  Employee;

이 정의를 전제로 다음의 문장을 사용하여 WorkerBee 인스턴스로 amy 을 만들합니다 :

var  Amy = New  WorkerBee;

amy 개체는 로컬 속성이 하나 있습니다. 그것은 projects 입니다. name 및 dept 속성 값은 amy 에게 로컬이 아니며, amy 개체 __proto__ 속성에서 가져옵니다. 그 결과, amy 에는 다음 속성이 존재하는 것입니다 :

amy.name == "" ;
amy.dept == "general" ;
amy.projects == [];

여기서 Employee 에 연결된 프로토 타입의 name 속성 값을 변경합니다 :

Employee.prototype.name = "Unknown"

보기, Employee 의 모든 인스턴스에 새로운 값이 반영되도록 생각됩니다. 하지만 그렇게는되지 않습니다.

Employee 개체의 어떤 인스턴스를 만들어도, 그 인스턴스는 name 속성의 로컬 값 (빈 문자열)을 가지게됩니다. 새로운 Employee 객체를 만들고 WorkerBee 프로토 타입을 넣으면 WorkerBee.prototype 는 name 프로토 타입의 로컬 값을 갖는다는 것입니다. 따라서 JavaScript가 amy 개체 ( WorkerBee 인스턴스)의 name 속성을 검색하면 JavaScript는 해당 속성의 로컬 값을 WorkerBee.prototype 에서 발견합니다. 그리고 Employee.prototype 에 체인 검색을 중지합니다.

런타임에 개체의 속성 값을 변경하고 새 값이 객체의 모든 자손에게 상속하게하려면 개체의 생성자 함수에서 그 속성을 정의하지 마십시오. 대신 생성자 함수에 연결된 프로토 타입에 속성을 추가합니다. 예를 들면, 먼저 코드를 다음과 같이 변경합니다 :

Function  Employee () {
  this . dept = "general" ;
}
Employee.prototype.name = "" ;
 
Function  WorkerBee () {
  this . projects = [];
}
WorkerBee.prototype = New  Employee;
 
var  Amy = New  WorkerBee;
 
Employee.prototype.name = "Unknown" ;

이 경우, amy 의 name 속성은 "Unknown"입니다.

이 예에서 알 수 있듯이, 객체의 속성에 기본 값을 갖게하고 또한 실행시에 기본 값을 변경할 수 있도록하고 싶은 경우 생성자 함수 자체 내에서가 아닌 생성자의 프로토 타입에서 속성을 설정하도록 합니다.

인스턴스 관계 결정

JavaScript에서 속성 검색은 우선 객체 자신의 속성에서 검색하고 그 속성 이름이 존재하지 않는 경우는 특수한 개체 속성 인 __proto__ 에서 탐색합니다. 이것은 재귀 적으로 계속합니다. 이 프로세스는 "프로토 타입 체인의 탐색"이라고합니다.

이 특수 속성 __proto__ 은 개체가 생성 될 때 설정되고 생성자의 prototype 속성의 값이됩니다. 따라서 식 new Foo () 는 __proto__ == Foo.prototype 인 객체를 만듭니다. 그 결과, Foo.prototype 속성 변경은 new Foo () 에서 생성 된 모든 객체의 속성 탐색을 변경합니다.

모든 개체는 __proto__ 개체 속성이 있습니다 ( Object 제외). 또한 모든 함수는 prototype 객체 속성이 있습니다. 따라서 객체는 다른 객체에 '프로토 타입 상속'에 연관시킬 수 있습니다. 개체 __proto__ 와 함수의 prototype 개체를 비교하는 것으로, 상속을 확인 할 수 있습니다. JavaScript는 빠른 방법이 있습니다. instanceof 연산자는 객체와 함수를 검사하여 오브젝트가 함수의 프로토 타입에서 상속하는 경우에 true를 돌려줍니다. 예를 들어,

var  F = New  Foo ();
var  IsTrue = (F instanceof  Foo);

자세한 예로 속성 상속 에 나와있는 정의와 동일한 세트를 가정합니다. 다음과 같이하여 Engineer 개체를 만듭니다 :

var  Chris = New  Engineer ( "Pigman, Chris" , "jsd" ], "fiji" );

이 개체에 대해 다음의 문장은 모두 true입니다 :

chris.__proto__ == Engineer.prototype;
chris.__proto__.__proto__ == WorkerBee.prototype;
chris.__proto__.__proto__.__proto__ == Employee.prototype;
chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null ;

여기에서 다음과 같은 instanceOf 함수를 써 봅시다 :

Function  instanceOf (object, constructor) {
   while  (object! = null ) {
      if  (object == constructor.prototype)
         return  true ;
      if  ( typeof  object == 'xml' ) {
        return  constructor.prototype == XML.prototype;
      }
      object = object.__proto__;
   }
   return  false ;
}

주의 : 위의 구현은 최신 버전의 JavaScript에서 XML 객체의 표현 방법의 단점을 해결하기 위해 객체의 형태와 "xml"과를 대조하고 있습니다. 본질적인 정보를 원하시면 bug 634150 을 참조하십시오.
이 정의를 이용하면, 다음 식은 모든 true입니다.

instanceOf (chris, Engineer)
instanceOf (chris, WorkerBee)
instanceOf (chris, Employee)
instanceOf (chris, Object)

그러나 다음 식은 false입니다 :

instanceOf (chris, SalesPerson)

생성자의 글로벌 정보

생성자를 만들 때 생성자에서 글로벌 정보를 설정하는 경우는주의가 필요합니다. 예를 들어, 고유 한 ID를 각각의 새 직원 정보에 자동으로 지정하려합니다. 그래서 다음과 같이 Employee 를 정의합니다 :

var  idCounter = 1;
 
Function  Employee (name, dept) {
   this . name = name | | "" ;
   this . dept = dept | | "general" ;
   this . id = idCounter + +;
}

이 정의를 사용하면 새로운 Employee 를 만들 때 생성자가 다음 ID를 순차적으로 할당하고 글로벌 ID 카운터를 증가시킵니다. 그 결과, 계속 다음 문장을 넣어 victoria.id 은 1이며 harry.id 은 2입니다 :

var  victoria = New  Employee ( "Pigbert, Victoria" , "pubs" )
var  Harry = New  Employee ( "Tschopik, Harry" , "sales" )

보기 이것은 확실히 없을 것입니다. 그러나 idCounter 어떤 용도이든, Employee 개체를 만들 때마다 증가됩니다. 이 장에서 설명한 Employee 계층 구조를 만들면 Employee 생성자 프로토 타입을 설치 할 때마다 호출됩니다. 다음 코드를 가정합니다 :

var  idCounter = 1;
 
Function  Employee (name, dept) {
   this . name = name | | "" ;
   this . dept = dept | | "general" ;
   this . id = idCounter + +;
}
 
Function  Manager (name, dept, reports) {...}
Manager.prototype = New  Employee;
 
Function  WorkerBee (name, dept, projs) {...}
WorkerBee.prototype = New  Employee;
 
Function  Engineer (name, projs, mach) {...}
Engineer.prototype = New  WorkerBee;
 
Function  SalesPerson (name, projs, quota) {...}
SalesPerson.prototype = New  WorkerBee;
 
var  Mac = New  Engineer ( "Wood, Mac" );

또한 여기에서는 생략 된 정의에 base 속성이 그 정의가 프로토 타입 체인에서 상위의 생성자를 호출합니다. 이 경우, mac 개체가 생성 될 때까지 mac.id 가 5 수 있습니다.

카운터가 불필요하게 증가하는 것이 문제가 될까되지 않을까 응용 프로그램 바입니다. 이 카운터의 정확한 값을 걱정하는 경우, 대신에 하나의 해결책으로 다음 생성자가 필요하다고 생각합니다 :

Function  Employee (name, dept) {
   this . name = name | | "" ;
   this . dept = dept | | "general" ;
   if  (name)
      this . id = idCounter + +;
}

프로토 타입으로 사용하기 위해 Employee 의 인스턴스를 만들 때 생성자에 인수를 포기해서는 안됩니다. 이 생성자의 정의를 사용하면 인수를 전달하지 때 생성자가 ID 값을 할당하지 않고, 카운터를 업데이트하지 않습니다. 따라서 Employee 가 id에 값을 할당하게하려면 직원의 이름을 지정해야합니다. 이 예에서는 mac.id 는 1이됩니다.

다중 상속 같은

객체 지향 언어 중에는 다중 상속을 허용하는 것이 있습니다. 즉, 개체가 무관 한 부모 개체에서 속성 값을 상속 할 수 있다는 것입니다. JavaScript는 다중 상속을 지원하지 않습니다.

런타임 속성 값 상속은 JavaScript가 값을 찾으려고 객체의 프로토 타입 체인을 검색하여 수행됩니다. 개체에 연결된 프로토 타입은 하나이기 때문에 JavaScript는 여러 프로토 타입 체인에서 동적으로 상속 할 수 없습니다.

JavaScript에서는 생성자 함수가 그 안에서 여러 다른 생성자 함수를 호출 할 수 있습니다. 이에 따라 다중 상속 같은 것이 가능합니다. 예를 들어 다음 문장이 있다고합니다 :

Function  Hobbyist (hobby) {
   this . hobby = hobby | | "scuba" ;
}
 
Function  Engineer (name, projs, mach, hobby) {
   this . base1 = WorkerBee;
   this . base1 (name, "engineering" , projs);
   this . base2 = Hobbyist;
   this . base2 (hobby);
   this . machine = mach | | "" ;
}
Engineer.prototype = New  WorkerBee;
 
var  dennis = New  Engineer ( "Doe, Dennis" , "collabra" ], "hugo" )

또한 WorkerBee 의 정의는이 장에서 먼저 사용한 것이라고합니다. 이 경우 dennis 개체에는 이러한 속성이 존재합니다 :

dennis.name == "Doe, Dennis"
dennis.dept == "engineering"
dennis.projects == [ "collabra" ]
dennis.machine == "hugo"
dennis.hobby == "scuba"

dennis 는 Hobbyist 생성자에서 hobby 속성을 검색하는 것입니다. 여기서 Hobbyist 생성자의 프로토 타입에 속성을 추가 할 수 있습니다 :

Hobbyist.prototype.equipment = "mask" , "fins" , "regulator" , "bcd" ]

이렇게도 dennis 개체는 새 속성을 상속하지 않습니다.

저작권 공지

이 문서의 모든 저작권은 Mozilla.org에 있습니다. 이 문서는 "모질라 기여자"들에 의해 작성 되었습니다. 원문 보기
저희가 한글로 번역한 2차적저작물에 대한 저작권 역시 Mozilla.org에 있습니다.