孤独プログラマー譚

孤独死が近い。

JavaScript Decoratorパターン

Decoratorパターンの良いところは、
主体となるオブジェクトと、処理を委譲しているオブジェクトの間に、スッと割り込ませることが出来ること。
そして、その処理を委譲しているオブジェクトには、傷をつけなくて済む。

Decoratorでラッピングするか否か、動的に決めることも出来る。

処理を付け足したり、付け足さなかったり…そんな時に使えるかも。使いたい。けど使いにくい。

function Component() {
  this.showNumber = function () {
    console.log(2)
  }
}

function Decorator(component) {
  this.component = component

  this.showNumber = function () {
    console.log(1)
    this.component.showNumber()
    console.log(3)
  }
}

var decorator = new Decorator(new Component())
decorator.showNumber()

1
2
3


Componentの処理のど真ん中に、Decoratorからの処理を埋め込みたい…そんな時もあるかもしれない。

function Component() {
  this.showNumber = function () {
    console.log(2)
    this.decorator.showInnerNumber()
    console.log(4)
  }
}

function Decorator(component) {
  this.component = component
  this.component.decorator = this

  this.showNumber = function () {
    console.log(1)
    this.component.showNumber()
    console.log(5)
  }

  this.showInnerNumber = function () {
    console.log(3)
  }
}

var decorator = new Decorator(new Component())
decorator.showNumber()

1
2
3
4
5

DecoratorはComponentを持ち、ComponentもDecoratorを持つ。お互いに委譲し合っている。
イマイチかもしれない。急遽、応急処置が必要な時に使おう。

ループの中のif文が現れたら、Observerパターンを検討する

やはり自分はObserverパターンを理解していなかった。

Observerパターンは、多種多様な輩をループで回す時、真価を発揮する。

以下、よく見るケース。JavaScriptで。

function Student(name, sex) {
  this.name = name
  this.sex = sex
}

var students = [
  new Student('野比のび太', 'boy'),
  new Student('源静香', 'girl'),
  new Student('剛田武', 'boy'),
  new Student('骨川スネ夫', 'boy')
]

for (var i = 0; i < students.length; i++) {
  if (students[i].sex === 'boy') {
    console.log('ムフフ、女子の着替え覗いちゃおうかなぁ。')
  } else if (students[i].sex === 'girl') {
    console.log('変態男子!ケダモノ!')
  }
}

ムフフ、女子の着替え覗いちゃおうかなぁ。
変態男子!ケダモノ!
ムフフ、女子の着替え覗いちゃおうかなぁ。
ムフフ、女子の着替え覗いちゃおうかなぁ。

ループの中にif文。分岐が多いのは良くない。
これがネストしたり、else if が何個も続くと、見てるだけで気分が悪くなってくる。

こういう時は、Observerパターンを使う。

function Boy() {
  Student.apply(this, arguments)
}
Boy.prototype.say = function () {
  console.log('ムフフ、女子の着替え覗いちゃおうかなぁ。')
}

function Girl() {
  Student.apply(this, arguments)
}
Girl.prototype.say = function () {
  console.log('変態男子!ケダモノ!')
}

var students = [
  new Boy('野比のび太', 'boy'),
  new Girl('源静香', 'girl'),
  new Boy('剛田武', 'boy'),
  new Boy('骨川スネ夫', 'boy')
]

for (var i = 0; i < students.length; i++) {
  students[i].say()
}

ムフフ、女子の着替え覗いちゃおうかなぁ。
変態男子!ケダモノ!
ムフフ、女子の着替え覗いちゃおうかなぁ。
ムフフ、女子の着替え覗いちゃおうかなぁ。

最後のfor文からifが消えた。

Observerパターンが実はとんでもない汎用性があることに今さら気付いた。

デザインパターンは言語を跨いで活用できるから、学習のコスパが良い!

Javascript 関数のデフォルト値設定の罠

今まで何気なくやってた、関数のデフォルト値設定。

function foo(a) {
  var a = a || 1;
  ...
}

実は愚かな行動だったと知った。

JavaScriptの「&&」「||」について盛大に勘違いをしていた件 - Qiita

例えば、

foo(0); // 1

引数が 0 や false の場合、意図どおりに動作しない。

以下のようにしてやればOK。

// 1
typeof a === 'undefined' && (a = 1)
// 2
if (typeof a === 'undefined') a = 1;
// 3
a = typeof a !== 'undefined' ? a : 1;

実はJavascript、「短絡評価」をできる、数少ない過激な言語だったと知った。

論理演算子(&&, ||)の短絡評価 - Qiita

そこで、すべてのif文は、and or に書き換えられるのでは…、と危険な思想が頭をよぎった。

例えば、

if (a === 1) {
  console.log(1);
} else if (a === 2) {
  console.log(2);
} else {
  console.log('unknown');
}

個人的には、else if とか大っ嫌い。

以下のように書き換えられるか?

a === 1 && console.log(1) || a === 2 && console.log(2) || console.log('unknown')

うーん、console.log()の関数実行部分が、true を返すか、false を返すかで、挙動が変わってしまう。

いまいちだが、以下のような関数を定義した。

function t() {
  return true;
}
function f() {
  return false;
}

&& に続く関数は、絶対に true としたい。
|| に続く関数は、絶対に false としたい。

a === 1 && t(console.log(1)) || a === 2 && t(console.log(2)) || console.log('unknown')

一応、意図した動作になってるっぽい。

独自関数をグローバルに定義してるのはキモいので、何とかするとして、

この Javascript の過激な仕様に感激した。

もともとイカれた(改め、クセの強い)言語だと思っていたが、そこを遥か超えてきやがった。すげぇ。

ECMAScript6 let・ループ・ブロックスコープ

ES5以下がだいだい分かってきたような気がしたから、ES6に手を出していくことにした。

以下の挙動が意味不明だった。

var callback = [];
for (let i = 0; i < 3; i++) {
  callback[i] = function () {
    console.log(i);
  };
}
callback[0](); // 0
callback[1](); // 1
callback[2](); // 2

だいだい、letはブロックスコープ内で再定義できないはずなのに、何でループできてるのか…?

ついでに、let を var に変えるとこうなる。

var callback = [];
for (var i = 0; i < 3; i++) {
  callback[i] = function () {
    console.log(i);
  };
}
callback[0](); // 3
callback[1](); // 3
callback[2](); // 3

変数iはグローバル変数なので、関数実行時の値が参照される。

0, 1, 2 ... としたい場合、ES5なら以下のように書く。

var callback = [];
for (var i = 0; i < 3; i++) {
  (function (i) {
    callback[i] = function () {
      console.log(i);
    };
  }(i));
}
callback[0](); // 0
callback[1](); // 1
callback[2](); // 2

即時関数に引数から変数iを渡す際、即時関数内で変数iが新たに定義される。
グローバルの変数iと、即時関数スコープ内の変数iは別物となる。
ついでに言うと、callback配列内の各関数はクロージャとなる。変数iは、各関数ごとに独立している。

話は初めのletに戻るが、そもそもループの解釈を勘違いしていた。
以下のように、ループ毎に、新たにブロックスコープが作られるイメージが正しいと思う。

{
  let i = 1;
  function callback1() {
    console.log(i);
  }
}
{
  let i = 2;
  function callback2() {
    console.log(i);
  }
}
callback1(); // 1
callback2(); // 2

ループ毎にletが定義されていると考えると、初めの挙動にも納得がいく。

JavaScript バブリングの活用

例えば、以下のようなHTMLがあるとする。

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  ... x 1000 !
</ul>

<li>をクリックした時のイベントハンドラを登録したいとする。

jQueryで普通に書くとこうなる。

$('li').on('click', function () {
  console.log(this.textContent);
});

(19ms)

ピュアJSで書くとこうなる。

var liArray = document.querySelectorAll('li');
for (var i = 0; i < liArray.length; i++) {
  liArray[i].addEventListener('click', function () {
    console.log(this.textContent);
  });
}

(17ms)

この場合、1000個の<li>にイベントハンドラが登録される。

1000個に登録なんて…、色々と効率が悪い気がする。(多分)

そこで、バブリングを利用して、同様の機能を実現する。

親ノードの<ul>にイベントハンドラを登録する。

var ul = document.querySelector('ul');
ul.addEventListener('click', function (event) {
  event.stopPropagation();
  console.log(event.target.textContent);
}, true);

(0.04ms)

addEventListenerの第3引数にtrueを設定することで、イベントが発火されたノード<li>をすっ飛ばして、<ul>が発火される。

stopPropagation() とすることで、そこで処理は終了。<li>のイベントは発火されない。

後々、DOM操作で<li>が追加されても、<li>にイベントハンドラを登録し直す必要は無い。

バブリング最高や!

Drupal8 FormクラスでGET通信を使う

カスタムモジュールで、Formクラスを使う時の話。

デフォルトはPOST通信になる。

「設定を変えればGET通信なんて余裕だぜ、はははー」
なんて思いつつ、以下に設定。

<?php
$form['#method'] = 'get';

うーん、だめ。動かない。なぜ?

悶々と調べた結果、Drupal8コアのバクとしか思えないんだが…。

以下、コアから引用。

<?php
// 'GET' が設定される?
$form_state->setRequestMethod($request->getMethod());

// 'GET' 時の分岐
<?php
$input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();

実際、一つ目のメソッドで 'GET' は設定されない。

以下のように、setRequestMethod ではなく、setMethod を使う必要がある。

<?php
$form_state->setMethod($request->getMethod());

ということで、この辺りの処理を、コアから外に出して、手動で行う。

コントローラにて。

<?php
$formState = new FormState();
$formState->setMethod('get');
$form = \Drupal::formBuilder()->buildForm('\Drupal\sample_form\Form\ExampleForm', $formState);

上記のように書くと、何事も無かったかのように、FormクラスがGET通信で動く様になる。

JavaScriptのコンストラクタとプライベート変数

まず、コンストラクタを使うこと前提で。

コンストラクタ内でメソッドを定義する場合、プライベート変数を使うことができる。

よくあるカウント機能を持つサンプル。

function Nogi() {
    var count = 0;
    this.say = function () {
        count++;
        console.log(count);
    };
}

インスタンス毎に、プライベート変数 count が別々にカウントアップされていることが分かる。

var nogi = new Nogi();

nogi.say(); // 1
nogi.say(); // 2
nogi.say(); // 3

var nogi2 = new Nogi();

nogi2.say(); // 1
nogi2.say(); // 2
nogi2.say(); // 3

しかし、インスタンスを作るごとに、this.say()がコピーされてしまう。
普通、メソッドは prototype に定義する。

だが、prototypeでメソッドを定義する場合、プライベート変数を使うことはできない。

それっぽいことをやってみる。

function Nogi() {}

Nogi.prototype.say = (function () {
    var count = 0;
    return function () {
        count++;
        console.log(count);
    }
}());

インスタンスを複数作り、カウントアップ。

var nogi = new Nogi();

nogi.say(); // 1
nogi.say(); // 2
nogi.say(); // 3

var nogi2 = new Nogi();

nogi2.say(); // 4
nogi2.say(); // 5
nogi2.say(); // 6

変数 count が共用されている。そりゃ prototype 自体が共用されているので当たり前か。
count はクラス変数になっているとも言える。



ES6ならやりたいことが出来るかも。
でも今はES6のことは考えたくありません。さようなら。