はじめに
例えばクラスAがある別のクラスBをオブジェクトの形でメンバに持っているとする。オブジェクトに名前でクラスインスタンスを登録する。そのインスタンスのメソッドを実行する形で、クラスAのメソッドをクラスBから作りたいが、同じようなコードを書きたくない。そういうときの小ネタ。
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
const obj_A = new A();
const b1 = new B(10);
const b2 = new B(20);
const b3 = new B(30);
obj_A.regist("one", b1);
obj_A.regist("two", b2);
obj_A.regist("three", b3);
// obj_AのメソッドとしてBのメソッドを要素に対して実行するものを作りたいが、
// いちいち似たようなコードを書きたくない。
// こうする
const methods = ["dog", "cat", "fox"];
for(const method of methods){
A.prototype[method] = (function(name){
const args = [...arguments];
args.shift();
if(this.bs[name] === undefined){ return; }
this.bs[name][method](...args);
});
}
obj_A.dog("one");
obj_A.cat("one", 0.1);
obj_A.fox("one", 1000, 100);
obj_A.dog("two");
obj_A.cat("two", 0.2);
obj_A.fox("two", 2000, 200);
obj_A.dog("three");
obj_A.cat("three", 0.3);
obj_A.fox("three", 3000, 300);
}
class A{
constructor(){
this.bs = {};
}
regist(name, obj_B){
this.bs[name] = obj_B;
}
}
class B{
constructor(x){
this.x = x;
}
dog(){
console.log(`${this.x}_わんわん`);
}
cat(u){
console.log(`${this.x}_にゃーにゃー_引数${u}`);
}
fox(u,v){
console.log(`${this.x}_ぎゃみゃーにゃ!ぎゃぎゃんぎゃん!!!こゃぁゃゃ!引数${u}と${v}`);
}
}
実行結果:
10_わんわん
10_にゃーにゃー_引数0.1
10_ぎゃみゃーにゃ!ぎゃぎゃんぎゃん!!!こゃぁゃゃ!引数1000と100
20_わんわん
20_にゃーにゃー_引数0.2
20_ぎゃみゃーにゃ!ぎゃぎゃんぎゃん!!!こゃぁゃゃ!引数2000と200
30_わんわん
30_にゃーにゃー_引数0.3
30_ぎゃみゃーにゃ!ぎゃぎゃんぎゃん!!!こゃぁゃゃ!引数3000と300
解説
クラスBにはcat,dog,foxというメソッドが入っている。クラスAはとある事情でクラスBのインスタンスをオブジェクト形式で保持することになっている。そこでクラスBのメソッドを名前でインスタンスを呼び出す形で定義したい。いちいちクラスBのインスタンスを呼び出したくないので。しかし似たようなメソッド定義をつらつらと書きたくない。
そこでprototypeを使う。
const methods = ["dog", "cat", "fox"];
for(const method of methods){
A.prototype[method] = (function(name){
const args = [...arguments];
args.shift();
if(this.bs[name] === undefined){ return; }
this.bs[name][method](...args);
});
}
Aのprototypeに直接クラス名で関数を定義してしまう。この場合の「this」というのはAのインスタンスである。それゆえthis.bsでAのインスタンスのbsにアクセスできる。さらにそれのメソッドを実行するように書けば、実質的にクラスBのメソッドを移植できる。なお、引数が指定されている場合でも、nameの後ろに連ねる形でそのまま実行できる。便利!
おわりに
クラス定義に慣れてしまうとprototypeで書くことを忘れてしまうんですが、こんな感じで役に立つこともあるので、両方できるといいかもしれないです。
ここまでお読みいただいてありがとうございました。
どうでもいいネタ
配列に対して
const tail = (a) => a[a.length-1]
っていう関数を用意すると便利なんですが、なんかこうしなくてもいい楽な方法があったら知りたいです(どうでもいい)。