孤独プログラマー譚

孤独死が近い。

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のことは考えたくありません。さようなら。

Observerパターンについて考える

Observerパターン、名前は知ってるけど、いまいち使いどころがよく分かりませんでした。

「状態の変化を通知する仕組み」とのことですが、あまりそういう状況に出くわさないというか、頭にピンとこないというか…。

調べてみたんですが、おそらく自分は勘違いしてました。

これ、別に通知とか関係ないのでは。

結局のところ、

<?php
$objects = [
    new ObjectA(),
    new ObjectB(),
    new ObjectC()
];
foreach ($objects as $object) {
    $object->commonMethod();
}

上記のように、「同名のメソッドを持つなら、ループできるよ」というだけのように思います。

<?php
interface Object
{
    public function commonMethod();
}

インターフェースを作って、共通のメソッドを持たせます。


そしてもう一点、「状態変化をどのObserverに通知するか(共通メソッドをどのオブジェクトに実行させるか)」ですが、新人の頃の自分なら、わざわざフラグ用の変数を作っていたように思います。

<?php
$objectA->isObserving = true;
$objectB->isObserving = false;
$objectC->isObserving = true;

$objects = [$objectA, $objectB, $objectC];

foreach ($objects as $object) {
    if ($object->isObserving) { $object->commonMethod(); }
}

でもそうじゃなく、Subject(メインルーチン)にObserver(メソッドを実行させるオブジェクト)を配列で持たせてやれば、それでいいんですよね。

<?php
$subject->observerList = [
    $ObjectA, $ObjectC
];
foreach ($subject->observerList as $object) {
    $object->commonMethod();
}

フラグ用の変数を作るのは、今後控えようと思います。フラグを立てたいオブジェクトを配列に持たせてやれば、それでよい!

結局、JavaScriptはどう継承すればいいのか

結局、JavaScriptはどう継承すればいいのか。
調べてもサイトによって書いてる事が違うので、ひとまず、信用できそうな方法を以下にまとめます。
(非 ES6 です。)


乃木坂46秋元真夏を親クラス、相楽伊織を子クラスとします。
2期生の相楽は、1期生秋元の意思を継いでるわけですね。


prototype継承用の関数。
一時的なプロキシコンストラクタを使います。

// prototype継承用の関数
var inherit = (function() {
    var F = function() {}; // クロージャを利用
    return function(C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.uber = P.prototype;
        C.prototype.constructor = C;
    }
}());


こちら、親クラス。

// 親クラス
var Parent = function() {
    this.group = '乃木坂46';
    this.name = '秋元真夏';
}
Parent.prototype.shoot = function() {
    console.log(this.group + 'の' + this.name + 'です。ズッキュン!');
}


子クラスです。
applyメソッドで、親コンストラクタのプロパティを継承。
inherit関数で、親のprototypeに設定されたメソッドを継承してます。

// 子クラス
var Child = function() {
    Parent.apply(this, arguments); // 親のプロパティを継承
    this.name = '相楽伊織';
}
inherit(Child, Parent); // 親のメソッドを継承


実行。相楽伊織がズッキュンを継承してくれました。

var child = new Child;
child.shoot(); // 乃木坂46の相楽伊織です。ズッキュン!


うーん、まだprototypeを使いこなしてる感じがしないなぁ。
もう少し粘着して覚えていきます。

デコレータパターンでテンプレートメソッドを使う(PHP)

うーん、今までデコレータパターンを勘違いしてた。
デコレータパターンは継承と書き換え可能でした。


デコレータは動的に継承っぽい関係を作れるので、その点メリットがあります。


ただ、どの程度書き換え可能なのか…?


自分はテンプレートメソッドをよく使うんですが、そういうこともデコレータは出来るのか…?


というわけで、デコレータパターンでテンプレートメソッドを再現してみました。


ひとまず、一旦、普通の継承を。

<?php
class Super {
    public function render() {
        echo '<p>2</p>';
        $this->tmplMethod();
        echo '<p>4</p>';
    }
}
class Child extends Super {
    public function render() {
        echo '<p>1</p>';
        parent::render();
        echo '<p>5</p>';
    }
    public function tmplMethod() {
        echo '<p>3</p>';
    }
}

$obj = new Child();
$obj->render();

こいつを、デコレータで書き換える。


親となるクラス。

<?php
class Super {
    public function render(callable $callback) {
        echo '<p>2</p>';
        $callback();
        echo '<p>4</p>';
    }
}


子供のクラス。

<?php
class Child {
    public function __construct(Super $super) {
        $this->super = $super;
    }
    public function render() {
        echo '<p>1</p>';
        $this->super->render([$this, 'tmplMethod']);
        echo '<p>5</p>';
    }
    public function tmplMethod() {
        echo '<p>3</p>';
    }
}


実行。

<?php
$obj = new Child(new Super);
$obj->render();

 
 
 
うーん、引数でコールバックを渡すくらいしか、思いつかないなぁ。
ひとまず、この方法で使っていきます。