프로토 타입 생성자를 설정해야하는 이유는 무엇입니까? 문서의 상속에 대한 섹션 자바 스크립트 객체

에서 MDN이 문서의 상속에 대한 섹션 자바 스크립트 객체 지향 소개 , 나는 그들이 prototype.constructor를 설정주의 :

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  

이것이 중요한 목적에 도움이됩니까? 생략해도 되나요?



답변

항상 필요한 것은 아니지만 용도가 있습니다. 기본 Person클래스 에서 복사 메소드를 작성하려고한다고 가정하십시오 . 이처럼 :

// define the Person Class  
function Person(name) {
    this.name = name;
}

Person.prototype.copy = function() {
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};

// define the Student class  
function Student(name) {
    Person.call(this, name);
}

// inherit Person  
Student.prototype = Object.create(Person.prototype);

이제 새로운 것을 만들어 Student복사하면 어떻게됩니까?

var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false

사본이의 인스턴스가 아닙니다 Student. (명확한 검사없이) Student“기본”클래스에서 사본 을 반환 할 방법이 없기 때문 입니다. 우리는 a 만 반환 할 수 있습니다 Person. 그러나 생성자를 재설정 한 경우 :

// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;

… 모든 것이 예상대로 작동합니다.

var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true


답변

이것이 중요한 목적에 도움이됩니까?

예, 아니오

ES5 및 이전 버전에서는 JavaScript 자체가 constructor아무것도 사용하지 않았습니다 . 함수 prototype속성 의 기본 객체 가 가지고 있고 함수를 다시 참조하도록 정의 했습니다 . 사양의 다른 내용은 전혀 언급하지 않았습니다.

ES2015 (ES6)에서 상속 계층 구조와 관련하여 사용하기 시작했습니다. 예를 들어, 새로운 반환 약속을 만들 때 호출 한 약속 Promise#thenconstructor속성 ( SpeciesConstructor 를 통해 )을 사용합니다. 또한 ArraySpeciesCreate 를 통해 배열의 서브 타이핑에 관여 합니다.

언어 자체 이외에도 사람들은 일반적인 “복제”함수를 만들려고 할 때 또는 일반적으로 객체의 생성자 함수라고 생각되는 것을 참조하려고 할 때이를 사용합니다. 내 경험은 그것을 사용하는 것이 드물지만 때로는 사람들이 사용한다는 것입니다.

생략해도 되나요?

기본적으로 존재 하며 함수의 속성 에서 객체 를 교체 할 때만 다시 넣어야합니다 prototype.

Student.prototype = Object.create(Person.prototype);

이 작업을 수행하지 않으면 :

Student.prototype.constructor = Student;

… (아마도)이 가지고있는 것을 Student.prototype.constructor상속받습니다 . 오해의 소지가 있습니다. 물론,을 사용 하지 않는 ( 또는 같은 ) 그것을 사용하는 것을 서브 클래스 화하고 ¹ (이를 처리하는)을 사용하는 경우 올바르게 설정해야합니다. 기본적으로 : 좋은 생각입니다.Person.prototypeconstructor = PersonPromiseArrayclass

코드 (또는 사용하는 라이브러리 코드)에서 아무것도 사용하지 않으면 괜찮습니다. 항상 올바르게 연결되었는지 확인했습니다.

물론 ES2015 (일명 ES6)의 class키워드를 사용하면 대부분 키워드를 사용했을 때 더 이상 필요하지 않습니다.

class Student extends Person {
}

¹ “… 당신은 (같은 용도를 뭔가를 서브 클래스 화하는 경우 Promise또는 Array)가 아닌 사용 class…”  – 그건 가능한 그렇게하지만 진짜 고통 (그리고 약간 바보). 를 사용해야 Reflect.construct합니다.


답변

TLDR; 꼭 필요한 것은 아니지만 장기적으로 도움이 될 것이므로 그렇게하는 것이 더 정확합니다.

참고 : 이전 답변이 혼란스럽게 작성되어 신속하게 답변을 놓치면 누락 된 부분이 많으므로 편집 한 내용이 많이 있습니다. 심각한 오류를 지적한 사람들에게 감사합니다.

기본적으로 Javascript에서 하위 클래스를 올바르게 연결합니다. 서브 클래 싱 할 때 prototype객체 덮어 쓰기를 포함하여 프로토 타입 위임이 올바르게 작동하는지 확인하기 위해 펑키 한 작업을 수행해야 합니다. prototype객체를 덮어 쓰면에가 포함 constructor되므로 참조를 수정해야합니다.

ES5의 ‘클래스’작동 방식을 빠르게 살펴 보겠습니다.

생성자 함수와 프로토 타입이 있다고 가정 해 봅시다.

//Constructor Function
var Person = function(name, age) {
  this.name = name;
  this.age = age;
}

//Prototype Object - shared between all instances of Person
Person.prototype = {
  species: 'human',
}

인스턴스화하기 위해 생성자를 호출하면 다음과 같이 말합니다 Adam.

// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);

new‘Person’으로 호출 된 키워드는 기본적으로 몇 가지 추가 코드 행으로 Person 생성자를 실행합니다.

function Person (name, age) {
  // This additional line is automatically added by the keyword 'new'
  // it sets up the relationship between the instance and the prototype object
  // So that the instance will delegate to the Prototype object
  this = Object.create(Person.prototype);

  this.name = name;
  this.age = age;

  return this;
}

/* So 'adam' will be an object that looks like this:
 * {
 *   name: 'Adam',
 *   age: 19
 * }
 */

우리 경우 console.log(adam.species), 조회가 실패 할 adam경우, 그에게 프로토 타입 체인을 찾아 볼 .prototype것입니다, Person.prototype– 그리고 Person.prototype .species 조회가에서 성공할 수 있도록 속성을 Person.prototype. 그런 다음로 기록 'human'됩니다.

여기에서는 Person.prototype.constructor을 올바르게 가리 킵니다 Person.

이제 흥미로운 부분은 소위 ‘서브 클래 싱’입니다. Student클래스, 즉 Person추가 변경 사항 이있는 클래스의 하위 클래스 를 만들려면 Student.prototype.constructor정확성을 위해 학생을 가리켜 야합니다.

자체적으로는이 작업을 수행하지 않습니다. 서브 클래 싱 할 때 코드는 다음과 같습니다.

var Student = function(name, age, school) {
 // Calls the 'super' class, as every student is an instance of a Person
 Person.call(this, name, age);
 // This is what makes the Student instances different
 this.school = school
}

var eve = new Student('Eve', 20, 'UCSF');

console.log(Student.prototype); // this will be an empty object: {}

new Student()여기서 호출 하면 원하는 모든 속성을 가진 객체가 반환됩니다. 여기서 확인 eve instanceof Person하면을 반환 false합니다. 액세스하려고하면 eve.species반환 undefined됩니다.

즉, 우리는 그렇게 위임을 연결할 필요가 eve instanceof Persontrue를 반환하고 그래서의 경우 Student제대로 위임에 Student.prototype다음 Person.prototype.

그러나 new키워드로 호출하기 때문에 호출이 추가하는 것을 기억하십니까? 그것은 부를 것이다 Object.create(Student.prototype)우리는 사이 delegational 관계를 설정하는 방법이다, Student하고 Student.prototype. 지금 Student.prototype은 비어 있습니다. 찾는 지금 .species의 인스턴스 Student에 위임하고 있기 때문에 실패 Student.prototype 하고 .species속성에 존재하지 않습니다 Student.prototype.

에 할당 Student.prototype하면 Object.create(Person.prototype)Student.prototype자체를 위임하면 예상대로 Person.prototype조회 eve.species가 반환 human됩니다. 아마도 우리는 그것이 Student.prototype AND Person.prototype에서 상속 받기를 원할 것입니다. 그래서 우리는 그 모든 것을 고쳐야합니다.

/* This sets up the prototypal delegation correctly
 *so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
 *This also allows us to add more things to Student.prototype
 *that Person.prototype may not have
 *So now a failed lookup on an instance of Student
 *will first look at Student.prototype,
 *and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);

이제 대표단은 작동하지만, 우리는 덮어 쓰기하고 Student.prototype의와 함께 Person.prototype. 우리가 호출 Student.prototype.constructor하면 Person대신에 가리킬 것입니다 Student. 이것은 우리가 그것을 해결하기 위해 필요한 이유입니다.

// Now we fix what the .constructor property is pointing to    
Student.prototype.constructor = Student

// If we check instanceof here
console.log(eve instanceof Person) // true

ES5에서 우리의 constructor속성은 ‘생성자’라는 의도로 작성한 함수를 참조하는 참조입니다. new키워드가 제공 하는 것 외에도 생성자는 ‘일반’함수입니다.

ES6에서는 constructor이제 클래스를 작성하는 방식에 내장되어 있습니다. 클래스를 선언 할 때 메소드로 제공됩니다. 이것은 단순히 구문 설탕이지만 super기존 클래스를 확장 할 때 액세스 할 수있는 것과 같은 편의를 제공합니다 . 따라서 위 코드를 다음과 같이 작성합니다.

class Person {
  // constructor function here
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // static getter instead of a static property
  static get species() {
    return 'human';
  }
}

class Student extends Person {
   constructor(name, age, school) {
      // calling the superclass constructor
      super(name, age);
      this.school = school;
   }
}


답변

동의하지 않습니다. 프로토 타입을 설정할 필요는 없습니다. 정확히 동일한 코드를 사용하지만 prototype.constructor 줄을 제거하십시오. 변화가 있습니까? 아니요. 이제 다음과 같이 변경하십시오.

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

그리고 테스트 코드의 끝에서 …

alert(student1.favoriteColor);

색상은 파란색입니다.

내 경험상 prototype.constructor의 변경은 어쨌든 모범 사례가 아닌 매우 구체적이고 복잡한 작업을 수행하지 않는 한 많이하지 않습니다. 🙂

편집 : 웹을 조금 둘러보고 실험을 한 후에 사람들이 생성자를 ‘신규’로 구성되는 것과 같이 보이도록 생성자를 설정 한 것처럼 보입니다. 나는 이것에 대한 문제가 자바 스크립트가 프로토 타입 언어라는 것입니다-상속과 같은 것은 없습니다. 그러나 대부분의 프로그래머는 상속을 ‘길’로 밀어 넣는 프로그래밍의 배경을 가지고 있습니다. 그래서 우리는이 프로토 타입 언어를 ‘클래스’언어와 같은 ‘클래스’확장과 같은 시도하고 시도 할 모든 종류의 것들을 생각해 냈습니다. 실제로 그들이 제시 한 예에서, 새로운 학생은 사람입니다 – 그것은 다른 학생으로부터 ‘확장’되지 않습니다. 학생은 그 사람에 관한 것입니다. 학생, 그리고 당신이 무엇이든 연장

Crockford는 약간 미쳤고 지나치게 열성적이지만, 그가 쓴 것들 중 일부에 대해 진지한 독서를합니다.


답변

이것은 당신이 쓴다면 큰 함정이 있습니다

Student.prototype.constructor = Student;

그러나 그 프로토 타입이 Person 인 교사가 있다면

Teacher.prototype.constructor = Teacher;

학생 생성자는 이제 교사입니다!

편집 : Mozilla 예제에서와 같이 Object.create를 사용하여 작성된 Person 클래스의 새 인스턴스를 사용하여 Student 및 Teacher 프로토 타입을 설정하여이를 방지 할 수 있습니다.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);


답변

지금까지 혼란은 여전히 ​​있습니다.

기존 예제를 따르면 다음과 같은 기존 오브젝트 student1가 있습니다.

var student1 = new Student("Janet", "Applied Physics");

어떻게 student1생성 되는지 알고 싶지 않다고 가정 하고 다른 객체를 원하면 like의 constructor 속성을 사용할 수 있습니다 student1.

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

여기서 Student생성자 속성이 설정되어 있지 않으면 속성을 가져 오지 못합니다. 오히려 Person객체 를 생성 합니다.


답변

프로토 타입 생성자를 설정해야하는 이유에 대한 좋은 코드 예제가 있습니다.

function CarFactory(name){
   this.name=name;
}
CarFactory.prototype.CreateNewCar = function(){
    return new this.constructor("New Car "+ this.name);
}
CarFactory.prototype.toString=function(){
    return 'Car Factory ' + this.name;
}

AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 

function AudiFactory(name){
    this.name=name;
}

AudiFactory.prototype.toString=function(){
    return 'Audi Factory ' + this.name;
}

var myAudiFactory = new AudiFactory('');
  alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');

var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
alert(newCar);

/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ????
*/