Class pt.1

22984 ワード

JSは原型に基づく言語で、「継承」という概念はありません.これは他のクラスベースの言語を使う人にとっては困惑しており、彼らの動作とクラスを類似させるために様々な方法が誕生し、2016年にES 6でクラス文法が誕生した.でもここでもプロトタイプを使っているので、プロトタイプを学ぶのは全く悪いことではありません…!

クラスとインスタンスの概念を理解する


クラスは...説明しにくい...
インスタンスは、クラス内の属性を持つエンティティです.
現実的には、クラスは共通要素を持つ集合を分類するための概念であり、このクラスはインスタンスの共通点を発見することによって定義されるが、プログラミングの面では、インスタンスを生成するには、まずクラスを定義しなければならない.

JavaScriptのクラス


もう一度言うと、JSは原型言語に基づいており、クラスの概念は存在しない.しかし、一般的な意味ではクラスの観点から、似たような要素が存在している.
let arr = new Array(1,2,3)
コンストラクション関数をnew演算子とともに呼び出すと、インスタンスが生成されます.この場合、Arrayをクラスと呼び、Arrayのprototypeオブジェクトの内部要素を継承と見なすことができます.厳密には,プロトタイプフィルタの参照であるが,結果は同じであり,同じ概念と考えられる.ただし、Array内部のプロパティでは、Prototype以外の部分はインスタンスに継承されません.
インスタンスが継承されるかどうかに応じて、静的メンバーとインスタンスメンバーに分けます.これはクラスの観点から使用オブジェクトによって区別されます.しかし、他のクラスベースの言語とは異なり、JSはインスタンスで直接メソッドを定義することができます.したがって、インスタンスメソッドの名前は、プロトタイプに定義されたメソッドとインスタンスに定義されたメソッドのどちらに混乱をもたらす可能性があります.したがって,この名称をJSの特徴で代用し,プロトタイプメソッドと呼ぶ.
/******생성자 *******/
let Rectangle = function (width, height) {
  this.width = width;
  this.height = height;
};

/******(프로토타입)메소드 *******/
Rectangle.prototype.getArea = function () {
  return this.width * this.height;
};

/******스태틱메소드 *******/
Rectangle.isRectangle = function (instance) {
  return (
    instance instanceof Rectangle && instance.width > 0 && instance.height > 0
  );
};
/****** 출력 *******/
let rect1 = new Rectangle(3, 4);
console.log(rect1.getArea()); // 12
console.log(rect1.isRectangle(rect1)); // Error
console.log(Rectangle.isRectangle(rect1)); // true
上記の例は、典型的なコンストラクション関数およびインスタンスです.
生成されたインスタンスは、newキーワードを使用してRectangle関数を呼び出すことでrect 1に割り当てられます.この例ではwidth、height propertyに3,4の値を指定します.
プロトタイプオブジェクトに割り当てられたメソッドは、出力コメントの下のgetArea()が__proto__に接続されているため、3*4=12のインスタンスを呼び出すことができる.このようなインスタンスで直接呼び出すことができる方法をプロトタイプメソッドと呼ぶ.
次のコードは、rect 1インスタンスでisRectangleというメソッドにアクセスしようとしたが、__proto__に接続されず、親プロトタイプ(Object.prototype)にも対応するメソッドがないため、undefinedを実行する必要があるがundefined関数ではないためエラーが発生した.インスタンスから直接アクセスできないメソッドを静的メソッドと呼びます.静的メソッドは、最後の行のように呼び出されるべきです.
プログラミング言語のクラスは抽象的であるか、具体的なオブジェクトである可能性があります.
すなわち、クラスが特定のインスタンスで使用されるメソッドを定義する「フレームワーク」の役割を持つ場合、抽象的な概念であるが、クラス自体が直接アクセスしなければならない静的メソッドを呼び出す場合、それ自体がオブジェクトとして処理される.

クラス継承


クラス継承は、オブジェクト向けの最も重要な要素の1つです.前に習ったPrototype-chainでクラスの真似をします.
let Grade = function () {
  let args = Array.prototype.slice.call(arguments);
  for (let i = 0; i < args.length; i++) {
    this[i] = args[i];
  }
  this.length = args.length;
};
Grade.prototype = [];
let g = new Grade(100, 80);

ES 5のJSにはクラスがありません.すなわち,クラス継承はプロトコルタイプに基づいて実現されなければならず,最終的にはプロトコルタイプがフィルタによく接続されていると理解できる.
ただ基本的にはそうであり,細部でスーパークラスとサブクラスを完璧に実現したわけではない.
上のトッドにはいくつかの問題があります.Length Propertyは削除可能なポイントとGradeですすなわち,プロトタイプには空の配列が参照されている.
問題のlengthを見てみましょう...!
g.push(90)
console.log(g) // Grade [100, 80, 90] length:3

delete g.length;
g.push(70)
console.log(g) // Grade [70, 80, 90] length:1
以上のように、結果は良好で、length propertyを削除して再びpushすると、pushの値が0番目のインデックスに入り、lengthが1になります.埋め込みオブジェクトの配列インスタンスであるlength propertyは削除可能(構成可能)属性なので削除できませんが、Gradeクラスのインスタンスは配列メソッドを継承しますが、基本的には通常のオブジェクトの性質を維持しているため、削除につながる可能性があります.
しかし、なぜpushの場合、0番目のインデックスに70個があり、lengthは1なのでしょうか.g.__proto__、つまりGradeです.Prototypeが空の配列を参照しているからです.Pushコマンドに従って、JSエンジンはg.lengthを読み込もうとしたが、g.lengthがないため、プロトタイプフィルタによりg.__proto__.lengthを読み込んだ.空の配列のlengthは0なので、ここで値を指定し、lengthが1のコマンドは正常に動作します.
ではGradeプロトタイプに要素を含む配列が一致したらどうなりますか?
Grade.prototype = ['a', 'b', 'c', 'd'];
let g = new Grade(100,80)

g.push(90)
console.log(g) // Grade(3) [100, 80, 90] length:3

delete g.length;
g.push(70)
console.log(g) // Grade(5) [100, 80, 90, empty, 70] length: 5
Grade.プロトタイプには長さ4の配列が割り当てられている.90を追加すると、上記と同じように出力は正常ですが、長さパーセントが削除され、70を追加すると出力されるので、動作が異なります.g.lengthがないので、g.__proto__.lengthを検索し、値が4であるため、インデックス4に70を入れ、g.lengthに5を付与する順番で操作する.
クラスの値は、インスタンスの動作に影響を与えることはできません.
この影響自体がクラスの抽象性を損なっている.すなわち、インスタンスの関係にインスタンスのみが使用するメソッドを含む抽象的な「フレームワーク」を記述しないと、いつでもどこでも予想外のエラーが発生する可能性があります.
今回は,ユーザ定義の2つのクラス間で継承関係を実現した.
正方形クラスと矩形クラスを作成し、各クラスにgetAreaと呼ばれる方法を追加し、幅を求めます.
let Rectangle = function (width, height) {
  this.width = width;
  this.height = height;
};

Rectangle.prototype.getArea = function () {
  return this.width * this.height;
};

let rect = new Rectangle(3, 4);
console.log(rect.getArea()); // 12

let Square = function (width) {
  this.width = width;
};
Square.prototype.getArea = function () {
  return this.width * this.width;
};
let sq = new Square(5);
console.log(sq.getArea()); // 25
ここにはRectangleとSquareクラスの共通要素width propertyがあります.
Squareでwidthを使用せずにheight propertyにwidth値を割り当てる場合、getAreaも同様に変更できます.
let Square = function (width) {
  this.width = width;
  this.height = width;
};
Square.prototype.getArea = function () {
  return this.width * this.height;
};
もとは正方形は矩形で具体的な条件を加えて、4回の長さはすべて等しいです.
ソースコードでは、SquareをRectangleのサブクラスとしても使用できるようです.getAreaは同じ動作をするため、親クラスで定義し、子クラスでメソッドを継承し、heightではなくwidthを使用します.
let Square = function (width) {
	Rectangle.call(this,width, width)
};

Square.prototype = new Rectangle()
Squareのコンストラクション関数内部でRectangleのコンストラクション関数を関数で呼び出した.このときパラメータheight位置にwidthが伝達される.また,メソッドを継承するためにSquareのプロトタイプオブジェクトにRectangleインスタンスを与えた.そうすると、あなたが望むように動作します.
しかし、上記のコードだけでは完璧なレベル体系が確立されているとは言えない...!
前述したように、クラスの値はインスタンスの構造に影響を与える可能性があるためです.
console.dir(sq)

sqの構造を出力すると、最初の行はSquareのインスタンスとして表示され、幅と高さは5を含む.__proto__は、Rectangleインスタンスを表し、width、heightのいずれも未定義であると判断することができる.理由はスクエアプロトタイプに値があるからです.あとは任意にスクエアすればprototype.width(|height)に値を割り当て、sq.width(||height)値をクリアすると、プロトタイプフィルタは重大な結果を生成します.
また,構造関数はRectangleの問題に注目している.sq.コンストラクション関数を使用してアクセスする場合、プロトコルタイプに沿ってsq.__proto__.__proto__、すなわちRectangleがフィルタされる.Rectangleを指すため、プロトタイプで見つけました.
let rect2 = new sq.constructor(2,3)
console.log(rect2) // Rectangle {width: 2, height: 3}
このように,サブクラスのコンストラクタとしてのプロトタイプに親クラスのインスタンスを付与するだけでメソッドを継承できるが,様々な問題があるため,構造上の安定性が低下する…!
その理由はまた今度调べましょう.