プロトタイプ継承


📚 Reference


javascript.info, https://ko.javascript.info/prototype-inheritance
注意:これは個人がサイトで復習しやすいように再編成された文章です.
詳細については、リファレンスサイトにアクセスしてください.

プロトタイプ継承


開発中に既存の機能を使用して拡張する必要がある場合があります.userという名前のオブジェクトがuserとかなり似ていると仮定しますが、adminguestを作成する必要があります.「userメソッドを複製または再実装しなければ、userにいくつかの機能を追加して、adminおよびguestオブジェクトを作成することができますよね?」そう思う
JavaScript言語の固有機能「プロトタイプ継承」(prototype継承)を利用して、上記のアイデアを実現できます.

[[Prototype]]


JavaScriptのオブジェクトには、リストに[[Prototype]]という名前の非表示のpropertyがあります.この非表示のプロパティ値はnullまたは他のオブジェクトへの参照です.他のオブジェクトを参照する場合、参照先は「プロトタイプ」と呼ばれます.

原型の振る舞いには「神秘的」な面がある.objectでPropertyを読みたいです.PropertyがなければJavaScriptは自動的にPrototypeでPropertyを検索しますから.プログラミングでは、この動作を「プロトコルタイプ継承」と呼ぶ.多くの便利な機能や開発テクニックは、プロトコルタイプ継承に基づいて作成されています.[[Prototype]]は内部Propertyであり、非表示Propertyでもあるが、開発者は様々な方法で値を設定することができる.
以下の例に示すように、特殊名称__proto__を使用して値を設定できます.
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal;
🔥 __proto__は、[[Prototype]]用のgetter・setterである.__proto__[[Prototype]]は違います.__proto__は、[[Prototype]]のgetter(取得者)およびsetter(設定者)である.互換性が低いため、__proto__を使用してもよいが、最近作成されたスクリプトでは、関数__proto__またはObject.getPrototypeOfを使用してプロトタイプ(Object.setPrototypeOf)または設定(get)が得られる.
JavaScript宣言では、setはブラウザ環境でのみサポートされることが規定されており、実際にはすべてのホスト環境(サーバ側を含む)が__proto__をサポートしています.
オブジェクト__proto__からPropertyを取得したいが、Propertyがない場合、JavaScriptはrabbitという名前のオブジェクトから自動的にPropertyを取得します.
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // (*)

// 프로퍼티 eats과 jumps를 rabbit에서도 사용할 수 있게 되었습니다.
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true
animalと表記された行では、(*)animalのプロトタイプに設定されている.rabbitとマークされた行では、(**)関数はalertを読もうとしたが、rabbit.eatsにはrabbitというpropertyはなかった.JavaScriptは、eats参照オブジェクト[[Prototype]]からanimalを取得する.下から下の図を見てください.

eatsの原型は現在rabbitです.」または「animalrabbitを継承します.」そう言ってもいいです.
プロトタイプが設定されているので、animalにおいてもrabbitで実現される有用なプロトタイプや方法を用いることができる.このタイプで継承されるpropertyを「継承property(継承属性)」と呼びます.animalからanimalに定義されたメソッドを継承propertyを使用して呼び出す.
let animal = {
  eats: true,
  walk() {
    alert("동물이 걷습니다.");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

// 메서드 walk는 rabbit의 프로토타입인 animal에서 상속받았습니다.
rabbit.walk(); // 동물이 걷습니다.
プロトタイプ(rabbit)からanimalが自動的に継承されるので、walkrabbitを呼び出すこともできる.
let animal = {
  eats: true,
  walk() {
    alert("동물이 걷습니다.");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

let longEar = {
  earLength: 10,
  __proto__: rabbit
};

// 메서드 walk는 프로토타입 체인을 통해 상속받았습니다.
longEar.walk(); // 동물이 걷습니다.
alert(longEar.jumps); // true (rabbit에서 상속받음)
プロトタイプフィルタには3つの制限があります.
📌 循環参照は許可されていません(循環参照).walkを使用して他のオブジェクトを閉じた形式で参照すると、エラーが発生します.
📌 値__proto__は、オブジェクトまたは__proto__のみです.他のデータ型は無視されます.
📌 オブジェクトにはnullしかありません.オブジェクトは2つのオブジェクトを継承できません.

プロトタイプは読み取り専用です


プロトタイプは、Propertyの読み込み時にのみ使用されます.
基本情報を追加、変更、または消去する操作は、オブジェクトに直接行わなければなりません.
オブジェクト[[Prototype]]に直接メソッドrabbitを割り当てます.
let animal = {
  eats: true,
  walk() {
    /* rabbit은 이제 이 메서드를 사용하지 않습니다. */
  }
};

let rabbit = {
  __proto__: animal
};

rabbit.walk = function() {
  alert("토끼가 깡충깡충 뜁니다.");
};

rabbit.walk(); // 토끼가 깡충깡충 뜁니다.
呼び出しwalkは、プロトタイプのメソッドを実行するのではなく、オブジェクトrabbit.walk()に直接追加するメソッドを実行する.

ただし、アクセス者属性(accessor property)はsetter関数を使用してpropertyに値を割り当てるため、アクセス者属性(rabbit)に値を割り当てるとsetter関数が呼び出されると、オブジェクト((**))はproperty(admin)を追加しません.これは上記の例とは少し異なります.
let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith (*)

// setter 함수가 실행됩니다!
admin.fullName = "Alice Cooper"; // (**)

alert(admin.fullName); // Alice Cooper, setter에 의해 추가된 admin의 프로퍼티(name, surname)에서 값을 가져옴
alert(user.fullName); // John Smith, 본래 user에 있었던 프로퍼티 값
プロトタイプfullNameにはgetter関数userが含まれているため、get fullNameとマークされた行には(*)が呼び出される.同様に、setter関数がプロトコルタイプで定義されているため(get fullName)、set fullNameでマークされた行で割当て演算が実行されると、オブジェクト(**)に構成を追加するのではなく、プロトコルタイプのsetter関数が呼び出される.
上記の例によれば、「user本明細書のset fullName(value)には、どのような値が含まれていますか?」という疑問があるかもしれません.「property thisおよびthis.nameで値が使用される場合、この値はthis.surnameに格納されますか、それともuserに格納されますか?」という疑問も生まれます.
答えは簡単です.adminはプロトタイプの影響を受けません.thisは、オブジェクトから呼び出される方法でもプロトタイプから呼び出される方法でも、常にthisの前に位置する..でsetter関数が呼び出されると、admin.fullName=thisではなくuserである.
通常、オブジェクトを作成し、多くの方法を実装してから、これらの大きなオブジェクトに複数のオブジェクトを継承させるため、これらの特性を理解する必要があります.継承メソッドを使用しても、オブジェクトはプロトタイプではなく自身の状態を変更します.
例を通してさらに理解してみましょう.adminに「メソッドストレージ」として機能するオブジェクトanimalを継承させる.
呼び出しrabbitは、rabbit.sleep()のパーセントをオブジェクトrabbitに追加する.
// animal엔 다양한 메서드가 있습니다.
let animal = {
  walk() {
    if (!this.isSleeping) {
      alert(`동물이 걸어갑니다.`);
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};

let rabbit = {
  name: "하얀 토끼",
  __proto__: animal
};

// rabbit에 새로운 프로퍼티 isSleeping을 추가하고 그 값을 true로 변경합니다.
rabbit.sleep();

alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (프로토타입에는 isSleeping이라는 프로퍼티가 없습니다.)
上記のコードを実行すると、オブジェクトの状態は次の図のようになります.
isSleepingのほか、rabbitbirdなどがsnakeを継承している.これらのオブジェクトはまた、animalの方法、例えばrabbitを使用してもよい.継承メソッドのanimalは、実際のメソッドが呼び出されたとき(this)より前のポイント(animal)のオブジェクトとなる.したがって、.にデータを書き込むと、thisではなくオブジェクトの状態が変化します.
これにより,メソッドは共有されているが,オブジェクトの状態は共有されていないと断定できる.

for...複文

animalはまた、Propertyを継承して巡回オブジェクトに含める.
let animal = {
  eats: true
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

// Object.keys는 객체 자신의 키만 반환합니다.
alert(Object.keys(rabbit)); // jumps

// for..in은 객체 자신의 키와 상속 프로퍼티의 키 모두를 순회합니다.
for(let prop in rabbit) alert(prop); // jumps, eats
for..inでは、継承プロトコルを巡回ターゲットから除外できます.この組み込み方法は、obj.hasOwnProperty(key)に対応するpropertyがpropertyを継承するのではなく、keyで直接実施されるpropertyである場合にのみobjに戻る.
アプリケーションtrueは、以下の例のように継承propertyをフィルタリングしてもよいし、継承propertyに対してのみいくつかの動作を実行してもよい.
let animal = {
  eats: true
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

for(let prop in rabbit) {
  let isOwn = rabbit.hasOwnProperty(prop);

  if (isOwn) {
    alert(`객체 자신의 프로퍼티: ${prop}`); // 객체 자신의 프로퍼티: jumps
  } else {
    alert(`상속 프로퍼티: ${prop}`); // 상속 프로퍼티: eats
  }
}
上記の例の継承関係は、次の図のようになります.obj.hasOwnProperty(key)継承rabbitanimal継承animalObject.prototype継承Object.prototype.nullanimalを継承するのは、Object.prototypeがオブジェクト文字で宣言されているためであることに注意してください.

図に示すように、animalで使用される方法for..inは、hasOwnPropertyから来ている.
うん.しかし継承property Object.prototype.hasOwnPropertyは複数のウィンドウで出力され、eatsは出力されなかった.いったい何があったのか.
原因は簡単です.hasOwnPropertyは列挙できる(hasOwnProperty)番組ではないからです.enumerableのすべての方法のObject.prototypeフラグはenumerableであり、falseはリスト可能な項目のみを含むため、for..inは複数のウィンドウに出力されない.
🔥 キー-値を巡回するほとんどの方法では、継承プロセスを除外する動作があります.
ほとんどの方法(例えば、hasOwnPropertyおよびObject.keys)は、オブジェクトのキー値に対して何らかの操作を実行するが、Propertyを継承することは含まれない.
「≪プロトコル・タイプ|Protocol Type|oraolap≫」から継承されたプロトコルに加えて、オブジェクトで定義されたプロトコルのみが計算対象に含まれます.