ユーザ用ツール

サイト用ツール


ecmascript:class

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
ecmascript:class [2025/09/20 08:56] – [prototypeプロパティ] nullponecmascript:class [2025/09/20 13:41] (現在) nullpon
行 59: 行 59:
 function Fuga(x) { function Fuga(x) {
   this.x = x;   this.x = x;
 +}
 +
 +Fuga.prototype.inspect = function() {
 +  console.log(`x: ${this.x}`);
 } }
  
 const fuga = new Fuga(1); const fuga = new Fuga(1);
 +fuga.inspect();
  
 console.log(typeof Fuga); // => function console.log(typeof Fuga); // => function
行 69: 行 74:
 ``` ```
  
-以下のコードは実は上記のコードとほぼ同等。class構文で定義したクラスは実は関数なのである。このような関数はコンストラクタ関数と呼ばれる+これはclassキーワードを使った以下のクラス定義とほぼ同等。
  
 ```javascript ```javascript
行 75: 行 80:
   constructor(x) {   constructor(x) {
     this.x = x;      this.x = x;
 +  }
 +  
 +  inspect() {
 +    console.log(`x: ${this.x}`);
   }   }
 } }
 const fuga = new Fuga(1); const fuga = new Fuga(1);
 +fuga.inspect();
  
 console.log(typeof Fuga); // => function console.log(typeof Fuga); // => function
行 85: 行 95:
 ``` ```
  
-※ ただfunctionるコンストラクタ定義過去コードとの互換性ため残されている現在非推奨でclassキーワードを使うべき+classキーワードで定義たクラスの正体は関数で、newキーワードととも新しいオブジェクトを生成す関数をコンストラクタ関数と呼ぶ。コンストラクタ関数で生成されたオブジェクトクラスインスタンスであるかよう振る舞うが、実際にはクラスとは異なる**prototype chain**という仕組みで動作している。 
 + 
 +なお、functionコンストラクタ関数(クラス)を定義すること可能だが、えてそうする理由はなくclassキーワードを使うべきである
  
-これは何を意味しているかというとclassと銘打っているが、その実体はクラスではないということだ。クラスのような振る舞いはprototype chainという仕組みで行われている。 
  
 ## constructorプロパティ ## constructorプロパティ
行 95: 行 106:
 ```javascript ```javascript
 class Hoge { class Hoge {
-  constructor(name) { +  constructor(x) { 
-    this.name name; +    this.x;
-  } +
-   +
-  getName() { +
-    return this.name;+
   }   }
 } }
行 116: 行 123:
  
 ```javascript ```javascript
 +class Hoge {
 +  constructor(x) {
 +    this.x = x;
 +  }
 +  
 +  getNumber() {
 +    return this.x;
 +  }
 +}
 +
 console.log(Hoge.prototype); console.log(Hoge.prototype);
 ``` ```
  
-classで定義したメソッドは、このprototypeプロパティが持っている。+classで定義したメソッドは、このprototypeプロパティに所属している。
  
 ```javascript ```javascript
-console.log(Hoge.prototype,getName);  // => [function getName] +console.log(Hoge.prototype,getNumber);  // => [function getNumber]
- +
-console.log(Hoge.prototype,getName.call({ name: "にゃん" })); // => にゃん +
 ``` ```
  
-コンストラクタ関数を通して生成されたオブジェクトは`__proto__`というプロパティを持っており、コンストラクタ関数のprototypeにアクセスできる+コンストラクタ関数によって生成されたオブジェクトは`__proto__`というプロパティを持っており、コンストラクタ関数のprototypeにアクセスできる
  
 ```javascript ```javascript
-console.log(h.__proto__.getName); // => [function getName]+console.log(h.__proto__.getNumber); // => [function getNumber]
 ``` ```
  
行 138: 行 153:
 console.log(h.__proto__);            // => {} console.log(h.__proto__);            // => {}
 console.log(h.__proto__.__proto__);  // => [Object: null prototype] {} console.log(h.__proto__.__proto__);  // => [Object: null prototype] {}
-``` +```
  
-何も継承してないクラスの場合prototype chain2つで終わりだが継承すると継承した回数だけprototype chain長くなる。+`h.getNumber()`とうメソッド呼び出しは、`h.__proto__`に委譲するで実現ている。そして`new Hoge()`で生成されオブジェクトはprototypeを共有することで同じメソッドを共有し、Hogeクラスのように動作する。
  
-prototype chainメソッド呼び出しに関係している。例えば`x.m()`というコードを実行すると次のようにメソッドが検索される+## prototype chainメソッド呼び出し
  
-1. `x`自身プロパティ一覧からmという名前のプロパティを探し、見つかったらメソッドとして実行す。なけば2へ +JSのメソッドは関数が入ったプロパティである。よってプロパティに関数をセットすることでもメソッドを定義できる。 
-2. `x,__proto__`のプロパティ一覧からmとう名前・・・、なければ3へ + 
-3. `x.__proto__.__proto__`プロパティ一覧からmという名前・・・、なければ4へ +```javascript 
-4. prototype chainの末端まで到達しても見つからない場合はTypeErrorとなる+class Hoge { 
 + 
 +  // プロパティの定義 
 +  name = '';  
 +   
 +  // コンストラクタ 
 +  constructor(name) { 
 +    this.name = name || ''; 
 +  } 
 +   
 +  // メソッド 
 +  getName() { 
 +    return this.name; 
 +  } 
 +   
 +  // プロパティに関数入れてもメソッドにな(注意:こは実アプリでは避けるべき方法) 
 +  getCount = function() { 
 +    return this,name?.length || 0; 
 +  } 
 +
 +``
 + 
 +※ 通常メソッドはprototypeにセットされて複数のHogeオブジェクト間で共有されるが、プロパティにセットしたgetCountはnewするたびに関数が作成されて共有されず各Hogeオブジェクトが個別に持つ。これはリソースの無駄なので実際のアプリ構築時には避けるべきコードである 
 + 
 + 
 +```javascript 
 +h = new Hoge('example'); 
 + 
 +h,getCount();  // h自身がgetCountを持ってる 
 +h,getName();   // h.__proto__がgetNameを持っている 
 +h,toString();  // h.__proto__.__proto__がtoStringを持っている 
 +``` 
 + 
 +getCountはオブジェクト自身が持っているメソッドなで直接呼び出される。getNameはprototypeが持っているメソッドなのでprototypeに処理が委譲される。 
 + 
 +オブジェクトがメソッドを持っていなければprototypeに処理が委譲され、prototypeも持っていなければprototypeのprototypeに処理が委譲される。JSはprototype chainを遡ってメソッドを検索し、見つかったらそのprototypeに処理を委譲する。こうしてクラスのような振る舞いを実現している。 
 + 
 +したがって、getNameやtoStringの呼び出しは次のコードと概念的には同等である。 
 + 
 +```javascript 
 +h.__proto__.getName.call(h); 
 +h.__proto__.__proto__.toString.call(h); 
 +``` 
 + 
 +プロパティやメソッド所有者は`Object.hasOwn`で調べることができる 
 + 
 +```javascript 
 +console.log(Object.hasOwn(h, "name"));     // true 
 +console.log(Object.hasOwn(h, "getCount")); // true 
 +console.log(Object.hasOwn(h, "getName"));  // false 
 +console.log(Object.hasOwn(h, "toString")); // false 
 + 
 +console.log(Object.hasOwn(h.__proto__, "getName"));  // true 
 +console.log(Object.hasOwn(h.__proto__, "toString")); // false 
 + 
 +console.log(Object.hasOwn(h.__proto__.__proto__, "toString"));  // true 
 +``` 
 + 
 +prototype chainの末端までしてもメソッドが見つからない場合はTypeErrorが発生する。何も継承していないクラスの場合prototype chainは2つで終わりだが、継承する継承した回数だけprototype chainは長くなる。toStringやvalueOfなど定義しなくても存在しているメソッドはprototype chain末端が持っているメソッドである。 
 + 
 +### オーバーライド 
 + 
 +オーバーライドはprototype chainのより近い位置に同名のメソッドを定義することで実現される 
 + 
 +```javascript 
 +class Hoge { 
 +  toString() { 
 +    return super.toString() + "にゃん" 
 +  } 
 +
 + 
 +const h = new Hoge(); 
 + 
 +// toStringはprototype chainの末端が持っている 
 +console.log(Object.hasOwn(h.__proto__.__proto__, "toString");  // => true 
 + 
 +// しかしオーバーライドしたのでHogeのprototypeも持っている 
 +console.log(Object.hasOwn(h.__proto__, "toString");  // => true 
 + 
 +// 呼び出されるのは、prototype chainのより近い側にあるメソッド 
 +console.log(h.toString());  // => [object Object]にゃん 
 +```
  
  
行 180: 行 276:
  
  
-</markdow +</markdown> 
-n>+ 
ecmascript/class.1758358606.txt.gz · 最終更新: by nullpon