どうも。つじけ(tsujikenzo)です。このシリーズでは2021年8月からスタートしました「ノンプロ研EffectiveJavaScript輪読会」についてお送りします。
前回のおさらい
前回は、「prototype、getPrototypeOf、__proto__の違いを理解する」、「__proto__よりもObject.getPrototypeOfが好ましい」をお届けしました。
今回は、「__proto__は決して変更しないこと」をお届けします。
今日のアジェンダ
- プロトタイププロパティの復習
- プロトタイププロパティの上書き
- setPrototypeOfメソッド
- プロトタイププロパティを指定したオブジェクトの生成
プロトタイププロパティの復習
前回の復習ですが、オブジェクトにはかならずプロトタイププロパティが格納されていました。
function myFunction4_32_01() {
let obj = '';
// console.log('__proto__' in obj); //TypeError: Cannot use 'in' operator to search for '__proto__' in
obj = { name: 'Bob' };
console.log('__proto__' in obj); //true
console.log(Object.getPrototypeOf(obj)); //{}→objの内部プロパティ [[Prototype]] の値
}
プロトタイププロパティは、まずOwn Propertyを参照する性質があります。
function myFunction4_32_02() {
const rabbit = { name: 'Peter', walk() { return 'ウサギ飛び!' } };
const animal = { walk() { return '4足歩行' } };
//rabbitオブジェクトのプロトタイプを、animalに変更する
rabbit.__proto__ = animal;
//own propertyが勝つ
console.log(rabbit.walk()); //'ウサギ飛び!'
}
プロトタイププロパティの上書き
今度は、オブジェクトにOwn Propertyがなく、プロトタイププロパティのみのばあいです。
以下のように、プロトタイププロパティの上書きは、継承元のオブジェクトに影響を与えます。
function myFunction4_32_03() {
//メソッドを3つ用意しておく
const robot = { walk() { return '自動歩行' } };
const human = { sayHello() { return 'Hello' } };
const android = { isHuman() { return '私は人造人間です' } };
//オブジェクトを2つ用意
const yokozuna = {};
const osumosan = {};
//オブジェクトのプロトタイププロパティを上書きする
//横綱をロボットにする
yokozuna.__proto__ = robot;
console.log(yokozuna.walk()); //自動歩行
//お相撲さんのプロトタイププロパティを横綱に上書きする(継承行為)
osumosan.__proto__ = yokozuna;
console.log(osumosan.walk()); //自動歩行
//横綱のプロトタイププロパティを人造人間に上書きする
yokozuna.__proto__ = android;
console.log(yokozuna.isHuman()); //私は人造人間です
//osumosanにも影響を与えてしまう
osumosan.walk(); //TypeError: osumosan.walk is not a function
}
setPrototypeOfメソッド
プロトタイププロパティを変更するsetPrototypeOfメソッドも提供されています。
第1引数はプロトタイプとして設定されるオブジェクトで、第2引数はオブジェクトの新しいプロトタイプ(オブジェクトまたは null)です。
便利そうに見えますが、このメソッドは非推奨です。
基本的に、プロトタイププロパティは変更すべきではありません。
function myFunction4_32_04() {
const robot = { walk() { return '自動歩行' } };
const yokozuna = {};
Object.setPrototypeOf(yokozuna, robot);
console.log(yokozuna.walk()); //自動歩行
}
プロトタイププロパティを指定したオブジェクトの生成
もし、新しいオブジェクトを作るときに、オリジナルなプロトタイププロパティを指定したいばあいもあるかもしれません。
そんなときは、ES5より実装されたObject.createメソッドで、新しいオブジェクトを作成します。
function myFunction4_32_05(){
const robot = { walk() { return '自動歩行' } };
const yokozuna = {};
const newYokozuna = Object.create(robot);
console.log(newYokozuna.walk()); //自動歩行
}
古典的な継承とクラス構文
以上を踏まえまして、クラス構文以前は、どのようにして、お相撲さんクラス(スーパークラス)から横綱クラス(サブクラス)を継承していたのか、コーディングしてみましょう。
function myFunction4_32_06() {
function Osumosan() {
}
Osumosan.prototype.traning = function () { return '四股踏んじゃった' };
function Yokozuna() {
Osumosan.call(this);
}
Yokozuna.prototype = Object.create(Osumosan.prototype);
Yokozuna.prototype.constructor = Yokozuna;
const y = new Yokozuna();
console.log(y.traning()); //四股踏んじゃった
}
ES6からクラス構文でスッキリ書けるようになりました。
function myFunction4_32_07() {
class Osumosan {
traning() { return '四股踏んじゃった' }
}
class Yokozuna extends Osumosan {
}
const y = new Yokozuna();
console.log(y.traning()); //四股踏んじゃった
}
まとめ
以上で、「__proto__は決して変更しないこと」をお届けしました。
今のわたしには「プロトタイププロパティを指定したい」という場面が想像できませんでした。。。
クラスを勉強中なので、大変楽しかったですね。
第4回目の輪読会は、ここまでです。
次回輪読会をお楽しみに。
参考資料・Special Thanks
Object.setPrototypeOf() タカハシノリアキ@ntakahashi0505
このシリーズの目次
- [EffectiveJavaScript輪読会4]固定レシーバを持つメソッドを抽出するにはbindを使う
- [EffectiveJavaScript輪読会4]関数をカリー化するには、bindを使う
- [EffectiveJavaScript輪読会4]コードをカプセル化するには、文字列ではなくクロージャを使う
- [EffectiveJavaScript輪読会4]関数のtoStringメソッドに依存するのは避けよう
- [EffectiveJavaScript輪読会4]非標準のスタック調査プロパティを使うのは避けよう
- [EffectiveJavaScript輪読会4]prototype、getPrototypeOf、__proto__の違いを理解する
- [EffectiveJavaScript輪読会4]__proto__は決して変更しないこと