JavaScriptプロトコルタイプ

13441 ワード

オブジェクト向けプログラミング


オブジェクト向けプログラミングは,実世界の実体(物事や概念)を認識する哲学的思考からプログラミングの試みに切り込むことから始まる.エンティティには、エンティティを識別または区別できる特徴または性質を表す属性(attribute/property)があります.
// 이름과 주소 속성을 갖는 객체
const person = {
  name: 'Lee',
  address: 'Seoul'
};

console.log(person); // {name: "Lee", address: "Seoul"}
この属性によって複数の値から1単位を構成する複合材料構造をオブジェクトと呼び,オブジェクト向けプログラミングは独立したオブジェクトの集合でプログラムを表現しようとするプログラミングパターンである.
const circle = {
  radius: 5, // 반지름

  // 원의 지름: 2r
  getDiameter() {
    return 2 * this.radius;
  },

  // 원의 둘레: 2πr
  getPerimeter() {
    return 2 * Math.PI * this.radius;
  },

  // 원의 넓이: πrr
  getArea() {
    return Math.PI * this.radius ** 2;
  }
};

console.log(circle);
// {radius: 5, getDiameter: ƒ, getPerimeter: ƒ, getArea: ƒ}

console.log(circle.getDiameter());  // 10
console.log(circle.getPerimeter()); // 31.41592653589793
console.log(circle.getArea());      // 78.53981633974483
半径は円の状態を表すデータで、円の直径、周長、幅を求めるのが動作です.
オブジェクトは,状態データと動作を1つの論理ユニットに組み合わせた複合データ構造といえる.このとき,オブジェクトの状態データをproperty,動作をメソッドと呼ぶ.

protoビジターProperty


すべてのオブジェクトはprotoアクセス者propertyを介して自分のprototype、すなわち[prototype]内部スロットに間接的にアクセスできます.

protoは訪問者propertyです。


アクセスプロバイダ自体は、値を持たず、他のデータプロバイダの値を読み出したり格納したりするアクセス者関数(accessor function)からなります.すなわち、「取得」、「設定」プロバイダです.

Protoアクセス者Propertyからプロトコルタイプにアクセスすると、内部でprotoアクセス者Propertyのgetter関数[Get]が呼び出されます.
protoビジター・コンフィギュレーションで新しいプロトコル・タイプを割り当てると、protoビジター・コンフィギュレーションのsetter関数[Set]が呼び出されます.
const obj = {};
const parent = { x: 1 };

// getter 함수인 get __proto__가 호출되어 obj 객체의 프로토타입을 취득
obj.__proto__;
// setter함수인 set __proto__가 호출되어 obj 객체의 프로토타입을 교체
obj.__proto__ = parent;

console.log(obj.x); // 1

関数オブジェクトのプロトタイプ構成


関数オブジェクトのみが持つprototypeとは、コンストラクション関数によって作成されるインスタンスのタイプです.
// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty('prototype'); // -> true

// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty('prototype'); // -> false

文字記号で作成されたオブジェクトのコンストラクション関数とプロトタイプ


コンストラクション関数によって生成されたインスタンスは、プロトタイプのコンストラクション関数propertyによってコンストラクション関数に関連付けられます.この場合、コンストラクション関数propertyが指すコンストラクション関数は、インスタンスを作成するコンストラクション関数です.
// obj 객체를 생성한 생성자 함수는 Object다.
const obj = new Object();
console.log(obj.constructor === Object); // true

// add 함수 객체를 생성한 생성자 함수는 Function이다.
const add = new Function('a', 'b', 'return a + b');
console.log(add.constructor === Function); // true

// 생성자 함수
function Person(name) {
  this.name = name;
}

// me 객체를 생성한 생성자 함수는 Person이다.
const me = new Person('Lee');
console.log(me.constructor === Person); // true
一部のオブジェクトの作成方法は、文字記号のオブジェクトの作成方法と同じであり、new演算子を明示的に使用してコンストラクション関数を呼び出してインスタンスを生成しないオブジェクトの作成方法もあります.
// 객체 리터럴
const obj = {};

// 함수 리터럴
const add = function (a, b) { return a + b; };

// 배열 리터럴
const arr = [1, 2, 3];

// 정규표현식 리터럴
const regexp = /is/ig;

プロトタイプの作成時間


オブジェクトは文字記号またはコンストラクション関数によって生成されるため、最終的にはすべてのオブジェクトがコンストラクション関数に関連付けられます.
プロトタイプは,サブ関数を生成しながら生成される.

ユーザー定義のコンストラクション関数とプロトタイプを作成するタイミング


コンストラクション関数として呼び出すことができる関数、すなわち、コンストラクション関数は、関数定義が評価され、関数オブジェクトが生成されるときにプロトタイプとともに生成される.
// 함수 정의(constructor)가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.
console.log(Person.prototype); // {constructor: ƒ}

// 생성자 함수
function Person(name) {
  this.name = name;
}
コンストラクション関数として呼び出せない関数(すなわちnon-constructor)はプロトタイプを生成しません.
// 화살표 함수는 non-constructor다.
const Person = name => {
  this.name = name;
};

// non-constructor는 프로토타입이 생성되지 않는다.
console.log(Person.prototype); // undefined

プロトタイプチェーン

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

// 프로토타입 메서드
Person.prototype.sayHello = function () {
  console.log(`Hi! My name is ${this.name}`);
};

const me = new Person('Lee');

// hasOwnProperty는 Object.prototype의 메서드다.
console.log(me.hasOwnProperty('name')); // true
Personジェネレータ関数から生成されるmeオブジェクトはObjectである.prototypeのメソッドhasOwnPropertyを呼び出すことができます.これは私の対象がpersonです.プロトタイプオブジェクトだけではありませんこれは原型も受け継がれていることを意味します.
メソッドを含むオブジェクトのプロパティにアクセスしようとしたときに、そのオブジェクトにアクセスするプロパティがない場合、JavaScriptは[Prototype]の内部スロットの参照順に、親ロールとして機能するプロパティを検索します.これをプロトタイプチェーンと呼ぶ.プロトコルタイプチェーンはJavaScriptがオブジェクト向けプログラミング継承を実現するメカニズムである.
// hasOwnProperty는 Object.prototype의 메서드다.
// me 객체는 프로토타입 체인을 따라 hasOwnProperty 메서드를 검색하여 사용한다.
me.hasOwnProperty('name'); // -> true

オーバーライドとプロフェッショナルシャドウ

const Person = (function () {
  // 생성자 함수
  function Person(name) {
    this.name = name;
  }

  // 프로토타입 메서드
  Person.prototype.sayHello = function () {
    console.log(`Hi! My name is ${this.name}`);
  };

  // 생성자 함수를 반환
  return Person;
}());

const me = new Person('Lee');

// 인스턴스 메서드
me.sayHello = function () {
  console.log(`Hey! My name is ${this.name}`);
};

// 인스턴스 메서드가 호출된다. 프로토타입 메서드는 인스턴스 메서드에 의해 가려진다.
me.sayHello(); // Hey! My name is Lee
インスタンスにプロトタイプpropertyと同じ名前のpropertyを追加すると、プロトタイプチェーンに沿ってプロトタイプpropertyを検索し、プロトタイプpropertyを上書きするのではなく、インスタンスpropertyとして追加します.このとき,インスタンスメソッドsayHelloはプロトタイプメソッドsayHelloを覆い,プロトタイプメソッドsayHelloは遮られる.このような継承関係によって職業を区別する現象を職業の影と呼ぶ.
// 인스턴스 메서드를 삭제한다.
delete me.sayHello;
// 인스턴스에는 sayHello 메서드가 없으므로 프로토타입 메서드가 호출된다.
me.sayHello(); // Hi! My name is Lee
番組を削除するのもそうです.

instanceof演算子


オブジェクトinstanceofコンストラクション関数
右側のコンストラクション関数のprototypeにバインドされたオブジェクトが左側のオブジェクトのプロトタイプチェーンに存在する場合、オブジェクトはtrueとして評価され、そうでない場合falseとして評価されます.
// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// 프로토타입으로 교체할 객체
const parent = {};

// 프로토타입의 교체
Object.setPrototypeOf(me, parent);

// Person 생성자 함수와 parent 객체는 연결되어 있지 않다.
console.log(Person.prototype === parent); // false
console.log(parent.constructor === Person); // false

// Person.prototype이 me 객체의 프로토타입 체인 상에 존재하지 않기 때문에 false로 평가된다.
console.log(me instanceof Person); // false

// Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다.
console.log(me instanceof Object); // true
Instanceof演算子がどのように動作するかを理解するために、プロトコルタイプを置き換えましょう.
// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// 프로토타입으로 교체할 객체
const parent = {};

// 프로토타입의 교체
Object.setPrototypeOf(me, parent);

// Person 생성자 함수와 parent 객체는 연결되어 있지 않다.
console.log(Person.prototype === parent); // false
console.log(parent.constructor === Person); // false

// Person.prototype이 me 객체의 프로토타입 체인 상에 존재하지 않기 때문에 false로 평가된다.
console.log(me instanceof Person); // false

// Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다.
console.log(me instanceof Object); // true
meオブジェクトはPersonコンストラクション関数によって生成されたインスタンスであり,プロトタイプが置き換えられたにもかかわらず,プロトタイプとコンストラクション関数との接続が破壊される.しかしmeinstanceofPersonはfalseと評価された.
これはPersonですプロトタイプは私のオブジェクトのプロトタイプチェーンに存在しないからです.
// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// 프로토타입으로 교체할 객체
const parent = {};

// 프로토타입의 교체
Object.setPrototypeOf(me, parent);

// Person 생성자 함수와 parent 객체는 연결되어 있지 않다.
console.log(Person.prototype === parent); // false
console.log(parent.constructor === Person); // false

// parent 객체를 Person 생성자 함수의 prototype 프로퍼티에 바인딩한다.
Person.prototype = parent;

// Person.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다.
console.log(me instanceof Person); // true

// Object.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가된다.
console.log(me instanceof Object); // true
コンストラクション関数にバインドされたprototypeのオブジェクトがprototypeチェーンに存在するかどうかを決定します.

直接継承


Object.create


Object.createメソッドでは、プロトタイプを明示的に指定して新しいオブジェクトを作成します.Object.createメソッドは、他のオブジェクト作成メソッドと同様に、抽象演算OrdinaryObjectCreateを呼び出します.
// 프로토타입이 null인 객체를 생성한다. 생성된 객체는 프로토타입 체인의 종점에 위치한다.
// obj → null
let obj = Object.create(null);
console.log(Object.getPrototypeOf(obj) === null); // true
// Object.prototype을 상속받지 못한다.
console.log(obj.toString()); // TypeError: obj.toString is not a function

// obj → Object.prototype → null
// obj = {};와 동일하다.
obj = Object.create(Object.prototype);
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

// obj → Object.prototype → null
// obj = { x: 1 };와 동일하다.
obj = Object.create(Object.prototype, {
  x: { value: 1, writable: true, enumerable: true, configurable: true }
});
// 위 코드는 다음과 동일하다.
// obj = Object.create(Object.prototype);
// obj.x = 1;
console.log(obj.x); // 1
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true

const myProto = { x: 10 };
// 임의의 객체를 직접 상속받는다.
// obj → myProto → Object.prototype → null
obj = Object.create(myProto);
console.log(obj.x); // 10
console.log(Object.getPrototypeOf(obj) === myProto); // true

// 생성자 함수
function Person(name) {
  this.name = name;
}

// obj → Person.prototype → Object.prototype → null
// obj = new Person('Lee')와 동일하다.
obj = Object.create(Person.prototype);
obj.name = 'Lee';
console.log(obj.name); // Lee
console.log(Object.getPrototypeOf(obj) === Person.prototype); // true
オブジェクト.createメソッドは、最初のパラメータに渡されるオブジェクトに属するプロトタイプチェーンのオブジェクトを作成します.すなわち,オブジェクトを生成しながら直接継承を要求する.
「Object」を参照してください.Prototypeの構築方法Object.prototype.hasOwnProperty, Object.prototype.inPrototypeOf, Object.prototype.PropertyIsEnummerableなどはすべてのオブジェクトのプロトタイプチェーンの終点であるObjectである.prototypeメソッドなので、すべてのオブジェクトを継承して呼び出すことができます.
const obj = { a: 1 };

obj.hasOwnProperty('a');       // -> true
obj.propertyIsEnumerable('a'); // -> true
ESLintでは、オブジェクトは前の例のようになります.オブジェクトが直接プロトタイプを呼び出す構築方法は推奨されません.理由はObjectreateメソッドでプロトタイプチェーンの終点にあるオブジェクトを作成できるためです.
// 프로토타입이 null인 객체, 즉 프로토타입 체인의 종점에 위치하는 객체를 생성한다.
const obj = Object.create(null);
obj.a = 1;

console.log(Object.getPrototypeOf(obj) === null); // true

// obj는 Object.prototype의 빌트인 메서드를 사용할 수 없다.
console.log(obj.hasOwnProperty('a')); // TypeError: obj.hasOwnProperty is not a function

静的構成/メソッド


スタティック(static)Property/メソッドとは、コンストラクション関数によってインスタンスを作成する必要がなく、参照/呼び出すことができるProperty/メソッドです.
// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHello = function () {
  console.log(`Hi! My name is ${this.name}`);
};

// 정적 프로퍼티
Person.staticProp = 'static prop';

// 정적 메서드
Person.staticMethod = function () {
  console.log('staticMethod');
};

const me = new Person('Lee');

// 생성자 함수에 추가한 정적 프로퍼티/메서드는 생성자 함수로 참조/호출한다.
Person.staticMethod(); // staticMethod

// 정적 프로퍼티/메서드는 생성자 함수가 생성한 인스턴스로 참조/호출할 수 없다.
// 인스턴스로 참조/호출할 수 있는 프로퍼티/메서드는 프로토타입 체인 상에 존재해야 한다.
me.staticMethod(); // TypeError: me.staticMethod is not a function
Personコンストラクション関数は、独自のProperty/メソッドを持つオブジェクトです.Personコンストラクション関数オブジェクトが持つProperty/メソッドを静的Property/メソッドと呼ぶ.静的プロパティ/メソッドは、コンストラクション関数で作成されたインスタンスとして参照/呼び出しできません.
// Object.create는 정적 메서드다.
const obj = Object.create({ name: 'Lee' });

// Object.prototype.hasOwnProperty는 프로토타입 메서드다.
obj.hasOwnProperty('name'); // -> false
インスタンス/プロトタイプメソッドでthisを使用しない場合は、静的メソッドに変更できます.インスタンス呼び出しのインスタンス/プロトタイプメソッドでは、これはインスタンスを指します.メソッドでインスタンスを参照する必要がない場合は、静的メソッドに変更できます.プロトタイプメソッドを呼び出すには、インスタンスを作成する必要がありますが、静的メソッドはインスタンスを作成する必要はありません.

Propertyの存在を確認


演算子


key:Propertyキーを表す文字列
object:オブジェクトとして計算される式
key in object
const person = {
  name: 'Lee',
  address: 'Seoul'
};

// person 객체에 name 프로퍼티가 존재한다.
console.log('name' in person);    // true
// person 객체에 address 프로퍼티가 존재한다.
console.log('address' in person); // true
// person 객체에 age 프로퍼티가 존재하지 않는다.
console.log('age' in person);     // false

因文


for in文を使用して、オブジェクトのすべてのpropertyを遍歴およびリストします.
for(オブジェクト内の変数宣言){...}
const person = {
  name: 'Lee',
  address: 'Seoul'
};

// for...in 문의 변수 prop에 person 객체의 프로퍼티 키가 할당된다.
for (const key in person) {
  console.log(key + ': ' + person[key]);
}
// name: Lee
// address: Seoul
for in文は、オブジェクトのproperty個数で巡回し、for in文の変数宣言文で宣言された変数にpropertyキーを割り当てます.for in文はin演算子のように、巡回オブジェクトのpropertyだけでなく、継承されたprototypeのpropertyもリストされます.ただし、上記の例では、toStringなどのオブジェクトがあります.プロトタイプのプロパティはリストされていません.
const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

for (const key in person) {
  // 객체 자신의 프로퍼티인지 확인한다.
  if (!person.hasOwnProperty(key)) continue;
  console.log(key + ': ' + person[key]);
}
// name: Lee
// address: Seoul
上記の例の結果は、personオブジェクトのproperty定義の順序でリストされます.ただし、for in文はpropertyをリストするときに順序を保証しないことに注意してください.