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

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.
