OOJS: オブジェクト指向 Javaスクリプトチュートリアル(例付き)

⚡ スマートサマリー

オブジェクト指向 Javaスクリプトは、現実世界のエンティティをプロパティとメソッドを持つオブジェクトとしてモデル化します。このページでは、オブジェクトリテラル、コンストラクタ関数、プロトタイプ、ES6クラス、継承、プライベートフィールドについて、実行して検証できるサンプルコードとともに解説します。

  • 🧱 オブジェクト: オブジェクトは関連するプロパティとメソッドをまとめたもので、1つの変数で学生、アカウント、または住宅をモデル化できます。
  • 🏗️ コンストラクター: コンストラクタ関数 この 単一の再利用可能な設計図から多数のインスタンスを作成します。
  • 🔁 列挙: for/inループは列挙可能なプロパティを一つずつ走査し、名前をハードコーディングすることなくキーと値を出力します。
  • 📦 ES6クラス: クラスキーワードは構文糖衣であり、メソッドは共有プロトタイプオブジェクト上に存在します。
  • 🧬 継承: extends はプロトタイプを自動的にリンクし、super は親のコンストラクタまたはオーバーライドされたメソッドを呼び出します。
  • 🔒 カプセル化: # で始まるフィールドは非公開のままで、Object.keys や JSON.stringify からは非表示になります。
  • ⚙️ 実践: このページに掲載されているすべての例は、検証済みの出力結果を示しているので、学習しながら結果を比較することができます。

オブジェクト指向 Javaオブジェクト、コンストラクタ、クラスを用いたスクリプト(OOJS)の概念

OOPSコンセプトとは Javaスクリプト?

多くの場合、変数や配列だけでは実際の状況をシミュレートするには不十分です。 Javaスクリプトを使用すると、現実世界の物体のように動作するオブジェクトを作成できます。学生や家は、それぞれ独自の特性を持つオブジェクトとして扱うことができます。オブジェクトにプロパティとメソッドを作成することで、プログラミングを容易にすることができます。オブジェクトが学生の場合、名、姓、IDなどのプロパティに加え、calculateRankやchangeAddressなどのメソッドを持つことができます。オブジェクトが家の場合、部屋数、塗装色、場所などのプロパティに加え、calculateAreaやchangeOwnerなどのメソッドを持つことができます。

オブジェクト指向プログラミング Javaしたがって、このスクリプトは、このページで紹介する4つのアイデアに基づいています。ping 状態と振る舞いをオブジェクトにまとめ、コンストラクタを通して設計図を再利用し、プロトタイプを通してメソッドを共有し、内部データへの直接アクセスを制限します。この言語を初めて使う場合は、まずここから始めてください。 への紹介 Javaスクリプト そしてここに戻ります。

オブジェクトの作成方法

次のようなオブジェクトを作成できます。

var objName = new Object();
objName.property1 = value1;
objName.property2 = value2;
objName.method1 = function()
{
  // line of code
}

または、より短いオブジェクトリテラル形式を使用する場合:

var objName = { property1: value1, property2: value2, method1: function()
{
  // lines of code
} };

オブジェクトのプロパティとメソッドにアクセスする

オブジェクトのプロパティにはドット表記でアクセスでき、メソッドは括弧を追加することで呼び出すことができます。

objectname.propertyname;   // read a property
objectname.methodname();   // call a method
objectname["propertyname"]; // bracket notation, useful for dynamic keys

この例を自分で試してみてください。

<html>
<head>
	<title>Objects!!!</title>
	<script type="text/javascript">
      var student = new Object();
      student.fName = "John";
      student.lName = "Smith";
      student.id = 5;
      student.markE = 76;
      student.markM = 99;
      student.markS = 87;
      student.calculateAverage = function()
      {
      	return (student.markE + student.markM + student.markS)/3;
      };
      student.displayDetails = function()
      {
        document.write("Student Id: " + student.id + "<br />");
        document.write("Name: " + student.fName + " " + student.lName + "<br />");
        var avg = student.calculateAverage();
        document.write("Average Marks: " + avg);
      };
		student.displayDetails();
	</script>
</head>
<body>
</body>
</html>

出力:

Student Id: 5
Name: John Smith
Average Marks: 87.33333333333333

💡ヒント: 上記の例では、 ドキュメントを書き込む このページではエディタ内で実行されるためです。実際のプロジェクトでは、 console.log デバッグやDOMメソッドなど テキストコンテンツ、理由 ドキュメントを書き込む 読み込み完了後に呼び出されると、ページを消去します。

OOPS コンストラクター

オブジェクトを一つずつ作成するのはあまり便利ではありません。なぜなら、生徒ごとに同じコードを繰り返す必要があるからです。ここでオブジェクトコンストラクタが登場します。コンストラクタは、引数とともに呼び出される通常の関数です。 NEW キーワード。これは、個々のインスタンスごとに再利用できるオブジェクト型を定義します。

コンストラクタ内では、 この これは構築中の新しいオブジェクトを参照するため、各インスタンスは渡された値の独自のコピーを受け取ります。マークなどの関連値は、多くの場合、 配列 そうすれば、一度の通過で合計を計算できる。

この例を自分で試してみてください。

<html>
<head>
	<script type="text/javascript">
		function Student(first, last, id, english, maths, science)
        {
          this.fName = first;
          this.lName = last;
          this.id = id;
          this.markE = english;
          this.markM = maths;
          this.markS = science;
          this.calculateAverage = function()
          {
         	 return (this.markE + this.markM + this.markS)/3;
          }
		  this.displayDetails = function()
		  {
            document.write("Student Id: " + this.id + "<br />");
            document.write("Name: " + this.fName + " " + this.lName + "<br />");
            var avg = this.calculateAverage();
            document.write("Average Marks: " + avg + "<br /><br />");
		  }
		}
        var st1 = new Student("John", "Smith", 15, 85, 79, 90);
        var st2 = new Student("Hannah", "Turner", 23, 75, 80, 82);
        var st3 = new Student("Kevin", "White", 4, 93, 89, 90);
        var st4 = new Student("Rose", "Taylor", 11, 55, 63, 45);
        st1.displayDetails();
        st2.displayDetails();
        st3.displayDetails();
        st4.displayDetails();
	</script>
</head>
<body>
</body>
</html>

出力:

Student Id: 15
Name: John Smith
Average Marks: 84.66666666666667

Student Id: 23
Name: Hannah Turner
Average Marks: 79

Student Id: 4
Name: Kevin White
Average Marks: 90.66666666666667

Student Id: 11
Name: Rose Taylor
Average Marks: 54.333333333333336

オブジェクトのプロパティをループする

構文:

for (variablename in objectname)
{
  // lines of code to be executed
}

for/in ループ は通常、オブジェクトのプロパティをたどるために使用されます。 には任意の名前を付けることができます。 変数ただし、オブジェクト名は既に存在するオブジェクトと一致する必要があります。各パスで、変数にはプロパティが保持されます。 key 文字列として扱われるため、値は次のような括弧表記で読み取られます。 従業員[x].

この例を自分で試してみてください。

<html>
<head>
	<script type="text/javascript">
		var employee={first:"John", last:"Doe", department:"Accounts"};
        var details = "";
        document.write("<b>Using for/in loops </b><br />");
        for (var x in employee)
        {
          details = x + ": " + employee[x];
          document.write(details + "<br />");
        }
	</script>
</head>
<body>
</body>
</html>

出力:

Using for/in loops
first: John
last: Doe
department: Accounts

2 つの点を覚えておく価値があります。for/in ループはプロトタイプチェーンを通じて継承された列挙可能なプロパティも訪問するため、次のようなガードがあります。 Object.prototype.hasOwnProperty.call(employee, x) はよく追加されます。プレーンなデータオブジェクトの場合、 オブジェクト.keys(employee) or オブジェクト.entries(employee) 通常、こちらの方が分かりやすい。なぜなら、どちらもオブジェクト自身のプロパティのみを返すからである。

ES6クラスの構文とは何ですか Javaスクリプト?

ECMAScript 2015(一般にES6と呼ばれる)では、 class キーワード。クラスは、上記のコンストラクタ関数が既に使用しているプロトタイプシステムに対するシンタックスシュガーです。新しいオブジェクトモデルは導入されていません。実行 学生のタイプ クラス上では依然として文字列「function」が返され、クラス本体に記述されたすべてのメソッドはインストールされます 学生プロトタイプつまり、各オブジェクトが独自のメソッドを持つのではなく、すべてのインスタンスがそのメソッドのコピーを1つ共有するということです。

構文により、いくつかの粗雑な部分も解消されます。クラス本体は常に厳格モードで実行され、クラスを呼び出す際に NEW グローバルオブジェクトを黙って汚染するのではなく、TypeErrorをスローし、ゲッター、セッター、静的メンバには専用の場所が確保されます。その結果、同じ意図を表現しながらも、より短いコードが得られます。

class Student {
  constructor(first, last, id, english, maths, science) {
    this.fName = first;
    this.lName = last;
    this.id = id;
    this.marks = [english, maths, science];
  }

  calculateAverage() {
    const total = this.marks.reduce((sum, m) => sum + m, 0);
    return total / this.marks.length;
  }

  displayDetails() {
    console.log("Student Id: " + this.id);
    console.log("Name: " + this.fName + " " + this.lName);
    console.log("Average Marks: " + this.calculateAverage().toFixed(2));
  }
}

const st1 = new Student("John", "Smith", 15, 85, 79, 90);
st1.displayDetails();
console.log(typeof Student);
console.log(st1.calculateAverage === Student.prototype.calculateAverage);

出力:

Student Id: 15
Name: John Smith
Average Marks: 84.67
function
true

最後の2行がそれを証明しています。クラスは依然として関数であり、インスタンス上のメソッドはプロトタイプに格納されている関数オブジェクトと全く同じものです。

コンストラクタ関数とES6クラスの比較

側面 コンストラクタ関数 ES6クラス
宣言 function Student(…) { } class Student { constructor(…) { } }
方法の配置 関数内で割り当てられるため、書き込みを行わない限り、各インスタンスは独自のコピーを取得します。 学生プロトタイプ クラス本体に記述され、プロトタイプに自動的に配置されます
新しい呼び出しなしで 静かに走り、 この 未定義またはグローバルオブジェクト すぐにTypeErrorをスローします
巻き上げ 関数宣言は定義の前に巻き上げられ、使用可能になります。 評価されるまで一時的な死領域にある
厳密モード 周囲の脚本に従う 常に厳格
継承 マニュアル: オブジェクトを作成する プラス コール 親に 拡張する (NAIST) と 素晴らしいです
プライベートデータ クロージャまたは命名規則など _バランス 真のプライベートフィールド #
基礎となるモデル プロトタイプ プロトタイプ(実行時に同一)

extendsとsuperを使った継承の実装方法

継承によって、特殊な型がより汎用的な型のプロパティやメソッドを再利用できるようになります。ES6以前は、プロトタイプチェーンを3つの別々のステップで手動で配線する必要がありましたが、そのうちの1つを忘れてしまうことがよくありました。

function Person(fName, lName) {
  this.fName = fName;
  this.lName = lName;
}

Person.prototype.introduce = function () {
  return "I am " + this.fName + " " + this.lName;
};

function Student(fName, lName, id) {
  Person.call(this, fName, lName);   // 1. borrow the parent constructor
  this.id = id;
}

Student.prototype = Object.create(Person.prototype); // 2. link the chain
Student.prototype.constructor = Student;              // 3. repair constructor

var st = new Student("Kevin", "White", 4);
console.log(st.introduce());
console.log(st instanceof Person);

出力:

I am Kevin White
true

その 拡張する キーワードは、3 つのステップすべてを実行します。 素晴らしいです 親に連絡を取るためのクリーンな方法を提供します。 2 つの明確な用途があります 素晴らしいです:

  • スーパー(…)を呼び出しとして 親コンストラクタを実行します。派生コンストラクタ内でのみ有効であり、変更を加える前に実行する必要があります。 このそうでない場合は、ReferenceError がスローされます。
  • スーパーメソッド() プロパティのルックアップでは、子クラスがオーバーライドしたメソッドの親クラス版が呼び出されるため、動作を置き換えるのではなく拡張することができます。
class Person {
  constructor(fName, lName) {
    this.fName = fName;
    this.lName = lName;
  }

  introduce() {
    return "I am " + this.fName + " " + this.lName;
  }
}

class Student extends Person {
  constructor(fName, lName, id) {
    super(fName, lName);   // must run before "this" is used
    this.id = id;
  }

  introduce() {
    return super.introduce() + ", roll number " + this.id;
  }
}

const st = new Student("Hannah", "Turner", 23);
console.log(st.introduce());
console.log(st instanceof Student, st instanceof Person);
console.log(Object.getPrototypeOf(Student.prototype) === Person.prototype);

出力:

I am Hannah Turner, roll number 23
true true
true

3行目が重要です。それは、 拡張する 手動で構築したバージョンとまったく同じプロトタイプリンクが生成されるため、クラスはプロトタイプチェーンを置き換えません。単に定型文を隠しただけです。 stエンジンはインスタンスを調べ、 学生プロトタイプをタップし、その後、 人物プロトタイプをタップし、その後、 オブジェクト.プロトタイプそして、最初の一致で停止します。その検索パスがプロトタイプチェーンです。

⚠️ 警告: 割り当てない Student.prototype = Person.prototypeこれにより、両方の型が1つのオブジェクトを共有するため、子型に追加したメソッドは親型にも追加されます。 Object.create(Person.prototype)または単に 拡張する.

プライベートクラスフィールドを使用してカプセル化を実現する方法

カプセル化とは、ping 内部状態は手の届かないところにあり、制御可能な方法によってのみ変更可能です。 Javaスクリプト開発者は先頭にアンダースコアを付けて偽装したが、 _バランス これは単なる慣例であり、どんなコードでも上書きできます。 JavaScript は真のプライバシーを提供します: 名前が # それを宣言しているクラス本体内でのみアクセス可能です。

私有地には3つの具体的なメリットがあります。

  • それらは使用前にクラス本体で宣言する必要があり、クラス本体は型の内部状態を1か所にまとめて文書化します。
  • それらは見えない オブジェクト.キー, JSON.文字列化、そしてfor/inを使用することで、内部状態がシリアル化された出力に漏れないようにします。
  • クラス外からプライベートフィールドにアクセスすると、構文エラーとして検出されるため、コードが実行される前に発見されます。これは、サイレントバグではありません。
class BankAccount {
  #balance = 0;

  constructor(owner, opening) {
    this.owner = owner;
    this.#balance = opening;
  }

  deposit(amount) {
    if (amount <= 0) {
      return "Deposit must be positive";
    }
    this.#balance += amount;
    return this.owner + " balance: " + this.#balance;
  }

  get balance() {
    return this.#balance;
  }
}

const acc = new BankAccount("John", 500);
console.log(acc.deposit(250));
console.log(acc.deposit(-40));
console.log("Read via getter: " + acc.balance);
console.log("Own keys: " + Object.keys(acc));
console.log("JSON: " + JSON.stringify(acc));

出力:

John balance: 750
Deposit must be positive
Read via getter: 750
Own keys: owner
JSON: {"owner":"John"}

バランスは決して現れません オブジェクト.キー またはJSON文字列で、負の預金はフィールドを破損する代わりにメソッドによって拒否されます。 残高を取得する() アクセサは読み取り専用のビューを公開します。これは、プライベートな状態を安全に公開するための一般的な方法です。

オブジェクト、プロトタイプ、クラス、継承、カプセル化が理解できたので、さらに開発を進めましょう。これらの基本を練習してください。 実用的 Javaスクリプトコードの例作業を進める 相互作用的 Java脚本練習アルゴリズムにクラスを適用するなど クイックソート Javaスクリプト出力を整理するには Javaスクリプト文字列の書式設定静的タイプがどのようにping オブジェクトのデザインを変更する TypeScript.

よくあるご質問

これは空のオブジェクトを作成し、それをコンストラクタのプロトタイプにリンクし、「this」をそのオブジェクトにバインドしてコンストラクタを実行し、そのオブジェクトを返します。コンストラクタが明示的にオブジェクトを返す場合は、その値が代わりに返されます。

ロジックが個々のインスタンスではなく型全体に属する場合、例えばJSONからオブジェクトを構築するファクトリや、2つのインスタンスを比較するヘルパーなどには、静的メソッドを使用します。静的メンバーはクラス自体に対して呼び出されます。

「this」はメソッドの呼び出し方法によって決まるため、メソッドがどこに記述されているかは関係ありません。メソッドをコールバックとして渡すと、レシーバーが失われます。これを解決するには、コンストラクタでメソッドをバインドするか、アロー関数を使用してクラスフィールドとして宣言してください。

いいえ。同じ名前のメソッドを2つ宣言した場合、最後に宣言したもののみが有効になります。オーバーロードをシミュレートするには、arguments.length を調べたり、デフォルトのパラメータ値を使用したり、名前付きプロパティを持つ単一のオプションオブジェクトを受け入れたりしてください。

はい。AIコーディングアシスタントはマップのおかげでそのリファクタリングをうまく処理します。ping 機械的な処理です。アシスタントはコンストラクタの外側にあるプロトタイプにアタッチされたメソッドを見落としたり、親コンストラクタへの呼び出しを省略したりする可能性があるため、必ず結果を確認してください。

はい。生成されたコードも、読み込んでレビューし、デバッグする必要があります。プロトタイプ、「this」、プライベートフィールドの動作を理解することで、本番環境に反映される前に誤った提案を見抜くことができます。