プロトタイプとプロトタイプの継承


1.プロトタイプ継承


開発の過程で、既存の機能を導入して拡張する必要がある場合があります.
たとえば、userという名前のオブジェクトがこのuserとよく似ていますが、少し異なるオブジェクトを作成する必要がある場合、どの方法が最も効果的ですか?
1つの方法は、プロトタイプ継承(プロトタイプ継承)です.

[[prototype]]


JavaScriptのオブジェクトには、[[prototype]]という非表示プログラムがあります.
この値はnullまたは他のオブジェクトへの参照です.他のオブジェクトをロックすると、参照オブジェクトは「プロトタイプ」と呼ばれます.

プロトタイプの興味深い点は、上図objectでpropertyを読みたいということであり、そのpropertyがなければ自動的にpropertyでpropertyを検索する
プログラミングではこの動作をprototypal inheritanceと呼ぶ.[[prototype]]は非表示のプログラムですが、開発者は様々な方法で値を設定できます.そのために__proto__を使用します.
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal;
__proto__[[prototype]]のgetter-setter
すなわち、__proto__ === [[prototype]]ではなく、値の取得または変更のみが可能である.
最近の値は、__proto__ではなくObject.getPrototypeOfです.Object.setPrototypeOfを使用して値を設定
⚠(ただし互換性の問題で⚜4|を使用する場合が多い)
上記の例では、__proto__にはrabbitはありませんが、これをプロトタイプeats: trueに設定することで、そのオブジェクトからプロトタイプをインポートできます.
let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // (*)

// 프로퍼티 eats과 jumps를 rabbit에서도 사용할 수 있게 되었습니다.
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true

この方法も当てはまる
let animal = {
  eats: true,
  walk() {
    alert("동물이 걷습니다.");
  }
};

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

// 메서드 walk는 rabbit의 프로토타입인 animal에서 상속받았습니다.
rabbit.walk(); // 동물이 걷습니다.

プロトタイプの深さは無限に深くすることができますが、推奨されません.
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에서 상속받음)

プロトタイプフィルタにはいくつかの制限があります.
  • ループ参照は許可されていません(ループ参照)
    つまり、他のオブジェクトを閉じた形式で参照すると、エラー
  • が発生します.
  • animalの値はオブジェクト__proto__のみであり、他のデータ型
  • は無視されます.
  • オブジェクトは1つのみnullすなわち、2つのオブジェクトを同時に継承することはできない
  • const animal = {
        walk: true
    }
    const human = {
        talk: true
    }
    const ayaan = {
        eat: true
    }
    
    ayaan.__proto__ = animal;
    console.log(ayaan.walk); // true
    
    // 하나의 객체만 상속받을 수 있음
    ayaan.__proto__ = human;
    console.log(ayaan.walk); // undefined
    console.log(ayaan.talk); // true
    
    // 순환 참조
    human.__proto__ = animal;
    animal.__proto__ = ayaan;
    // VM570:1 Uncaught TypeError: Cyclic __proto__ value
    「原型は読み取り専用」という言葉を必ず覚えておきましょう.
    プロトタイプは、プロトタイプを読むときにのみ使用されます.
    つまり、Propertyを追加、変更または削除する演算は、オブジェクトに直接行わなければなりません.
    また、prototypeは、継承されたオブジェクトに対応するpropertyがない場合にのみ継承されます.
    let animal = {
      eats: true,
      walk() {
        /* rabbit은 이제 이 메서드를 사용하지 않습니다. */
      }
    };
    
    let rabbit = {
      __proto__: animal
    };
    
    rabbit.walk = function() {
      alert("토끼가 깡충깡충 뜁니다.");
    };
    
    rabbit.walk(); // 토끼가 깡충깡충 뜁니다.

    しかし、アクセス者プログラム(accessor property)では、動作が少し異なります.
    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에 있었던 프로퍼티 값
    上記のコードを理解するには、[[prototype]]をもっと理解する必要があるようです.

    2. this


    上記の例では、thisにはどのような値が含まれていますか?
    結論から言えば、thisの原型は影響を受けない.thisは、オブジェクトからメソッドを呼び出すか、プロトタイプからメソッドを呼び出すかにかかわらず、thisは常に.の前のオブジェクトを表します.
    例えば、上でsetter関数をadmin.fullName = ...で呼び出した場合、thisadminである
    これらのプロパティは、巨大なオブジェクトを作成し、ここで多くの方法を継承すると簡単に使用できます.
    例えば、メソッド・リポジトリとして機能するオブジェクトanimalrabbitが継承される.
    // 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이라는 프로퍼티가 없습니다.)

    「メソッドを共有したいがオブジェクトのステータスを共有しない場合は、このオプションを使用します」for...in繰り返し文を使用すると、継承されたプログラムも巡回オブジェクトに含まれます.
    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
    obj.hasOwnProperty(key)booleanvalueを使用して、継承された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
      }
    }
    ここで興味深いのは、animalがオブジェクト文字で宣言されているため、Object.prototypeを継承することです.

    だからこそ、rabbitを持つことができますrabbit.hasOwnProperty(key)MDNドキュメントでは、Object.prototype.someMethod()が継承されているため、Object.prototypeと同じフォーマットがリストされています.
    でもおかしくないですか?hasOwnPropertyメソッドはfor...inでリストできません.理由は何ですか.hasOwnProperty)この方法は、Property flags and descriptorsenumerablefalseであるためである.

    3.コンストラクション関数のプロトタイプ


    エンティティ・オブジェクトが上に作成され、Object.prototypeが継承される場合、new F()のようなコンストラクション関数によって作成されるオブジェクトのプロトタイプは何ですか?
    重要なことは、コンストラクション関数(F)のプロトタイプを表すF.prototypeにおいて、prototypeFで定義された汎用プロトタイプである.F.prototypeprototypeは前に学んだ[[Prototype]]と似ているように聞こえますが、名前が同じだけで、実際には違います.
    let animal = {
      eats: true
    };
    
    function Rabbit(name) {
      this.name = name;
    }
    
    Rabbit.prototype = animal;
    
    let rabbit = new Rabbit("흰 토끼"); //  rabbit.__proto__ == animal
    
    alert(rabbit.eats); // true
    console.dir(rabbit);
    console.dir(Rabbit);
    console.dir()から、図に示すように、コンストラクション関数とコンストラクション関数によって生成されたインスタンスの構造の違いがわかります.

    関数のprototypeは、特に割り当てられていなくても、実際にはすべての関数が持つpropertyである.
    defaultはconstructor,constructor関数そのものを指す


    特別な操作が行われていない場合は、new Rabbitを実行して作成したすべてのウサギオブジェクトがconstructorpropertyを使用できます.この場合、[[Prototype]]が通過します.
    function Rabbit() {}
    // 디폴트 prototype:
    // Rabbit.prototype = { constructor: Rabbit }
    
    let rabbit = new Rabbit(); // {constructor: Rabbit}을 상속받음
    
    alert(rabbit.constructor == Rabbit); // true ([[Prototype]]을 거쳐 접근함)
    constructor最初のように新しいオブジェクトを割り当てるよりも、prototypeに必要なプログラムを追加および削除します.
    function Rabbit() {}
    
    // Rabbit.prototype 전체를 덮어쓰지 말고
    // 원하는 프로퍼티가 있으면 그냥 추가합니다.
    Rabbit.prototype.jumps = true
    // 이렇게 하면 디폴트 프로퍼티 Rabbit.prototype.constructor가 유지됩니다.
    誤ってconstructorを削除すると、手動で再生成できます.これにより、最初のconstructorの特徴が保証されます.
    Rabbit.prototype = {
      jumps: true,
      constructor: Rabbit
    };
    (作成中)

    *References

  • プロトタイプとプロトタイプの継承