【古典】 jQueryでSPAする時のポイント。もしくはオブザーバーパターンのサンプル。

TECHDRIVE の佐藤です。今回は書いてみたコードについて解説しつつ進めたいと思っています。

はじめに

歴史は繰り返しているので「またそういう話か」ということはよくあるのですが、いかに流行っている(流行っていそうな)ライブラリやフレームワークでも、多くの開発現場にいきなり導入できる事は少なく、古い技術を使ってやりくりしている事は多くあります。

特にJavaScriptでSPAなどのところは「どうしても必須でこれが無いと成り立たない」というレベルにできる業務領域がまだそれ程大きく無い為か、私や私の知り合いが対応している開発の現場では未だにjQueryでDOM操作がかなり多いと思われます。

そういう中でも「この部分だけ、ここだけで良いからSPAぽくしたい」というような要望はたまにあります。そういう時にいきなりVue.jsReactが導入できるような現場も稀有なのでは無いでしょうか(その背景についてはここでは論じません)。

というわけで今回はjQueryでSPAしたサンプルの解説記事です。jQuery使ってもごちゃごちゃにならない考え方もそこそこ入っていると思います。

作ったもの

https://github.com/CircleAround/message_server_js ここにあります。動作サンプルは以下です。 https://message-server-js-app.herokuapp.com/

内容としては https://message-server-app.herokuapp.com/ に置いてあるごくごく簡単なAPIサーバー(ソースはこちら)の、JavaScriptでのサンプルです。

今回の実装では「jQuery&レガシーバニラJS」縛りでお送りしています(forEachとかfind使ったのは許して下さい)。

ポイント

オブザーバーパターンを利用してViewとロジックを分ける

古式ゆかしいクライアント実装の設計ではMVCがよく用いられていると思いますが、大事なのはMとVの間がオブザーバーパターンで書かれていることだと思います。

「ViewはModelの詳細について知っているが、ModelはViewについて無知であ る」

ということをキッチリすると、どれが大事なロジックであり、どれが描画の為のコードなのかが明瞭になります。

オブザーバーパターンは 「データの状態の変化を自ら通知せずに、勝手に取得させる」 という考え方なので、それに忠実に書きます。 クラス名のプレフィクスはModelにする方が良いかもしれませんが、趣味でMessageServiceとしています*1

https://github.com/CircleAround/message_server_js/blob/master/main.js#L89

MessageService はクライアントで保持しなければいけない以下のような情報を持っています。

  • ログインしたユーザー
  • 表示しなければならない全てのメッセージ

また、情報の変化を受け取るリスナを仕掛けられます。リスナの実装は複数考え方があるとは思いますが、今回はシンプルに「変数に関数を設定できるだけ」にしています。以下のあたりです。

https://github.com/CircleAround/message_server_js/blob/master/main.js#L344

    this.service.onChangeMessages = function() {
      _this.updateMessages(_this.service.messages); // メッセージ一覧に変更がなされたメッセージの見た目を更新する
    };

    this.service.onChangeTargetMessage = function() {
      _this.updateForm(); // 編集のためのターゲットメッセージが変更されたら、フォームを更新する
    };

保有している状態が通信の結果などで変化した際に、このリスナを実行するようにしています。例えば signup関数が呼ばれると、通信が成功すれば onSignUp が呼ばれるようにしています。

  MessageService.prototype.signup = function (email) {
    var _this = this;
    return this.handleError(this.connector.signup(email).then(function(data){
      _this.user = data;
      _this.onSignUp();
    }));
  }

長い文字列はHTMLから取得して編集しやすくする

https://github.com/CircleAround/message_server_js/blob/master/index.html#L48

このあたりです。<script></script> に typeをうまく指定すると中身が文字列であるような扱いをしてくれるので、これを使ってHTML内に文字列を埋め込みます。今回はメッセージ表示のDOM表示部です。

    <script id="message_template" type="text/template">
      <div class="message">
        <div class="user">user:<span class="id"></span></div>      
        <div class="contents"></div>
        <div class="actions">
          <a class="edit" href="javascript:void(0)">編集</a>
          <a class="delete" href="javascript:void(0)">削除</a>
        </div> 
      </div>
    </script>

https://github.com/CircleAround/message_server_js/blob/master/main.js#L194

使う時にはIDでセレクタを辿ってから jQuery.text() を使っています。

文字列連結を避けてXSSを回避する

最近社内でも話題になった事ですが 「HTMLを文字列連結で作成してjQuery.html()でDOM化すると、XSSの危険性が増す」 ということがあります。文字列連結を行うと埋め込まれた変数が本当に安全かを由来を辿って確認する必要が出ててきてしまい、抜け漏れが発生しやすくなるということですね。

そこで私がよく用いる方法は 「jQuery.appendTo()を使ってDOM化して、その戻り値オブジェクトを介してjQuery.text()を利用する」 です。

https://github.com/CircleAround/message_server_js/blob/master/main.js#L220

      var $message = $(_this.messageTemplate).appendTo($messages);
      $message.find('.contents').text(message.contents);
      $message.find('.user>.id').text(message.user_id);

こんな形で、jQuery.appendTo()の戻り値が「今作成されたDOM構造」へのオブジェクトを戻してくれるので、そこからfind()で辿ってtext()で値を入れています。text()なら確実にエスケープされるのでこの効果を利用しています。

クラス的なオブジェクトを作成する時は無名関数で囲むとプライベートな関数が置けて便利

https://github.com/CircleAround/message_server_js/blob/master/main.js#L5

JavaScriptは都合よくスコープを作れると適切に書けそうな気がしています。クラス的なオブジェクトを作る場合にも、下記のようにスコープの中身が外に漏れ出ないことを利用しています。

var Connector = function() {
  
  // プライベートな関数群。これは公開されない。
  function request(params) {
    params['dataType'] = 'json';
    return $.ajax(params);
  }

  function url(path) {
    return endpoint + path;
  }

  function Connector(){} // ここからクラスの内容

  ...

  return Connector; // 最後にコンストラクタを返せばクラスだけ公開できる
}();

おしまいに

まぁ、滅びゆく技術ですよね。ただ、そういうものが必要な開発現場もあるので適切に利用できれば良いかと。

最近だと「フレームワークを使わないとうまく作れない」というような風潮を感じますが、便利なものが利用できない時でも上手に設計することでうまくやれるのではないかなと思いますし、そういう姿を目指していたいです。ReactをViewの部分に入れる変更をしてみるのもいいかなと思いました。差し替え可能感が感じられそうなのと、状態管理フレームワーク無しのReactはこういうところに価値がありそうに思うのです。

このコードはあえて縛っていますが、業務でやるなら外部ライブラリを少し入れます。これだと1ファイルが長過ぎますしね。そういうのも今後書くかもしれません。下記のようなことと組み合わせていく感じです。

dev.techdrive.top

*1:イメージ的にApplicationServiceに近い部分を担うため

JavaScript中級者へのステップアップ!callメソッドの使い所がわからないという方へ

こんにちは。TECH DRIVEの小笠原です。
今回は、JavaScriptのcallメソッドについてご紹介をしたいと思います。
callメソッドの使い所がいまいちわからないという方を対象としております。
また、本記事はJavaScriptにおける以下の知識があることが前提となりますので、予めご了承くださいませ。

  • thisの性質を理解している
  • プリミティブ値以外がオブジェクトであることを理解している
  • prototypeを理解している

callメソッドを知ろう

callメソッドは「関数を実行するための関数」です ※ 。
※ 同様のことを実現するメソッドとしてapplyが存在しますが、引数の渡し方等が異なるものの、実現できることはほぼ同じのため、applyの解説は割愛します。

「関数を実行するための関数」と聞いて「?」が浮かんだ方も多いのではないでしょうか?
順を追ってご説明していきます。

まずは、callメソッドの使い方をみてみましょう。

関数.call(thisに指定する値, 呼び出した関数に渡す引数情報)

callメソッドは、レシーバ(呼び出し元となる関数)を実行するメソッドなのですが、その際、第一引数に「実行する関数内でのthis」を指定することができます。
また第二引数は、callメソッドで呼び出す関数に渡す引数となりますので、関数が引数を必要としない場合は省略可能です。

まずは以下のコードを見てください。

var person = {
  name: 'Ken',
  greet: function () {
    console.log('My name is ' + this.name) 
  }
}

var doc = {
  name: 'Pochi'
}

person.greet() // 結果: My name is Ken
doc.greet() // 結果: Uncaught TypeError: doc.greet is not a function

person/docという変数にオブジェクトを代入し、それぞれgreetメソッドを実行しています。 しかし、変数docに代入されたオブジェクトはgreetというメソッドを持たないため、エラーとなっていますね。 これはみなさんも期待どおりの結果なのではないでしょうか?
次にcallメソッドでgreetメソッドのthisをdocに指定し、実行してみたいと思います。

person.greet.call(doc) // 結果: My name is Pochi

なんと、Pochiがしゃべりました。
personが持つgreetメソッドのthisをdocに指定したことにより、まるでdocがgreetメソッドを持っているかのような振る舞いになりました。
誤解を恐れずに言うなら、callメソッドを使用することにより、あるオブジェクトのメソッドを拝借し実行することができるのです。

しかし、これだけではまだメリットが見えてきませんね。 次に以下のコードをみてください。

HTML

<li class="item">hoge</li>
<li class="item">fuga</li>
<li class="item">piyo</li>

JavaScript

var $els = document.getElementsByClassName('item') // 結果: HTMLCollection(3) [li.item, li.item, li.item]
for(var i = 0; i < $els.length; i++) {
  console.log($els[i].innerText)
} // 結果: hoge fuge piyo

これは、itemという名前のclassをもったli要素のテキスト情報を出力するための処理です。
ここで注目していただきたいのは、getElementsByClassNameメソッドの返り値である「HTMLCollection」です。
このHTMLCollectionは、インデックス番号からデータの参照が行えることからも、配列に非常によく似たオブジェクトと言えます。
そのため、上のコードを以下のようにforEachを使用し、よりスマートに処理を書けるように変更してみたいと思います。

var $els = document.getElementsByClassName('item')
$els.forEach(function(val){
  console.log(val.innerText)
})  // 結果: Uncaught TypeError: $els.forEach is not a function

しかし、上記のコードを実行するとエラーが発生してしまいます。
なぜならforEachは配列(Arrayオブジェクト)のメソッドであり、HTMLCollectionはそのようなメソッドを持っていないためです。
このことから、HTMLCollectionは、フォーマットこそ配列と似ていますが、似ているだけで全く異なるオブジェクトであることがわかります。

しかし、この問題は、以下のようにcallメソッドを使用することで解決します。

var $els = document.getElementsByClassName('item')
Array.prototype.forEach.call($els, function($el){
  console.log($el.innerText)
})  // 結果: hoge fuge piyo

ここでcallメソッドの使い方をもう一度みてみましょう。

関数.call(thisに指定する値, 引数情報)

今回の場合、callメソッドのレシーバとなる関数は Array.prototype.forEach となります。
そして、そして第1引数の$els(HTMLCollection)がforEach実行時のthisとなります。
第2引数は実行する関数(forEach)へ渡す引数です。

この結果、forEachはまるでHTMLCollectionがもつメソッドかのように振る舞います。 今回の様に「あるオブジェクトのメソッドを拝借したい」「関数実行時にthisの値を強制したい」と思った時、callメソッドという選択肢を覚えておくと役立つシーンもあるはずです。

まとめ

いかがでしょうか?
callメソッドは、「thisの性質」や「オブジェクトへのより深い理解」が求められることからも、JavaScript中級者向けのメソッドであることは間違いないでしょう。
またメリットが掴みづらいという点も、理解のハードルを上げてしまう要因かと思います。
本記事を通して、少しでもcallメソッドの理解を深めていただけたのなら幸いです。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発にのぞむにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

JavaScriptのthisの性質を知ろう! ~ 脱初心者をしたい方向け ~

こんにちは。TECH DRIVEの小笠原です。 JavaScriptを書いていると度々目にする「this」ですが、その性質に癖があるため、JS初学者の方にとってハードルになりがちです。
本記事では、JS中級者へのステップアップを目指される方に向けて、thisの性質を知っていただくための記事を書かせていただきました。

thisの性質を知ろう

誤解を恐れず言うなら、thisとは「JavaScriptが予め用意している変数である」と言えます。
基本的にthisは、グローバルオブジェクトを指します。
例えば、以下のようにグローバルスコープでthisを参照するとWindowオブジェクトを返します。(※ クライアントサイドで動作するJavaScriptの場合)

console.log(this) // Window {省略}

また、以下のようにグローバルスコープ直下に定義した関数内でthisを参照した場合も、同じくWindowオブジェクトがthisの値となります。

function hoge () {
  console.log(this)
}
hoge() // Window {省略}

このようにthisは、基本的にグローバルオブジェクトを指します。
※ ただし、「strictモード」が有効となっている場合、関数内のthisは基本的にundefinedとなりますので、注意が必要です。
ここで「基本的に」とつけたのは、thisの値が上記の限りではないためです。
本章の冒頭で述べたようにthisは変数であるため、その値が一定のものを指すとは限らないのです。

thisが変化する4種類のパターンを知ろう

関数内のthisは、関数の呼び出し方によって値が変化することがあります。
抑えておくべきは、以下4パターンで関数が実行された場合、いずれもthisの値が異なるという点です。

  • 1.関数として呼ばれた場合
  • 2.メソッドとして呼ばれた場合
  • 3.コンストラクタ関数として呼ばれた場合
  • 4.call, applyから呼ばれた場合

それでは順番に見ていきましょう。

1. 関数として呼ばれた場合

これは、冒頭で例としてあげたグローバルスコープに定義された関数内での話になります。
先ほどお伝えした通り、この場合thisはグローバルオブジェクトを返します。 ※ 繰り返しとなりますが、「strictモード」が有効となっている場合、thisはundefinedとなります。

2. メソッドから呼ばれた場合

次は、あるオブジェクトに属するメソッド内で参照されるthisについてです。
例えば以下のようなオブジェクトがあるとします。

var person = {
  name: 'Ken',
  greet: function() {
    console.log('My name is ' + this.name)
  }
}

さて、上記のコードの次に person.greet() を実行した場合、結果はどうなるでしょうか?
「1. 関数の中で呼ばれた場合」同様に、thisがグローバルオブジェクトを指すのであれば、Windowオブジェクトのnameプロパティが参照され「My name is Window」となってしまいそうですね。

しかし、greetメソッドの実行結果は「My name is Ken」となります。
これは、あるオブジェクトのメソッド内で呼ばれたthisは、メソッドが所属するオブジェクトを指すようになるためです。
そのためgreet内でのthisは、person(厳密には変数person代入したオブジェクト)となるため、上記のような結果となります。

3. コンストラクタ関数から呼ばれた場合

次にコンストラクタ関数内でのthisについてです。
本章では、thisと合わせコンストラクタ関数に関する最低限の情報も合わせてお伝えしたいと思います。
コンストラクタ関数とは、乱暴な言い方をしてしまうとnewというワードを使用し実行する関数になります。
まずは、以下のコードを見てください。

function Person (gender) {
  this.gender = gender
  this.isMale = function() {
    return this.gender === 'male'
  }
}

// 通常の関数として実行
Person('male') // 結果: undefined

// コンストラクタ関数として実行
var person = new Person('male') // 結果: Person {gender: "male", isMale: ƒ}
// コンストラクタ関数の返り値(インスタンス)に含まれるメソッドを実行
person.isMale() // 結果: true

上のコードでは、Personという関数を定義し、まずは通常の関数として実行しています。
この結果は「undefined」※ となります 。
※ JavaScriptでは、関数内で明示的に返り値を指定(return)していない場合、関数はundefined(未定義値)を返す仕様になっているのです。

しかし、コンストラクタ関数として実行した場合、関数の返り値がオブジェクトになっていることが確認できます。
これは、コンストラクタとして実行した関数は、内部で動的に生成したオブジェクト(これをインスタンスと言います)を返すようになるためです。
そしてこのインスタンスの中には、関数内で「this.プロパティ名=値」とした結果が、プロパティ/メソッドとして入っていることが確認できます。
上のコードではnew Person('male’)の結果(インスタンス)を変数personに代入していることになります。
故に最終行では person.isMale() が正常に動作していることが確認できます。
ここでは、コンストラクタ関数内でのthisは、自身が返すインスタンスを指すことを抑えておきましょう。

4. call, applyから呼ばれた場合

さて、ここまでthisの性質と合わせ様々な関数の呼び出し方をみてきました。
しかし、関数の呼び出し方は、これだけではありません。
本記事において詳細は割愛しますが、JavaScriptでは、callやapplyといった「関数を実行するための関数」が存在します。
callやapplyは以下のように使用され、関数を実行する際にその関数内におけるthisを第一引数で渡した任意の値にすることができます。

関数.call(thisに指定する値, 呼び出した関数に渡す引数情報)

call/applyの理解については、本記事の内容はもちろん、オブジェクトに関するより深い理解も求められます。
そのため本記事で詳細に触れることはしませんが、call/applyが気になるという方は、以下の記事をご一読ください。

dev.techdrive.top

ここでは、call/applyを使用し関数を実行した場合、関数内でのthisの値は、プログラマが指定した任意の値になるということを覚えておきましょう。

おまけ Class内でのthisについて

JavaScriptのClassを使用したコードを見たことがある方は、その内部でもthisが使われているのを目にしたことがあるのではないでしょうか?

Class内でのthisは、先程ご紹介した「コンスラクタ関数内でのthis」とほぼ同じと言えます。
Class内のメソッドから参照するthisは 「new Class名」 実行時に生成されるインスタンスを指すことを覚えておきましょう。

まとめ

いかがでしょうか? JavaScriptの学習を進めていると必ず出てくるthisですが、本記事でもご紹介した通り、関数内のthisは「関数の呼出し方」によって値が変わります。
このthisの柔軟性が学習時のハードルになりがちです。
本記事を通して、少しでもthisへの理解を深めていただけたのなら幸いです。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発にのぞむにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

いまさら聞き辛い!WEB開発におけるサーバサイド言語とクライアントサイド言語の違い

こんにちは。TECH DRIVEの小笠原です。
本コミュニティの協賛企業でもあるサークルアラウンドが開催している「プログラミング相談所」にて、以前より参加者の方から「サーバサイドとクライアントサイド、それぞれのプログラミング言語の違い」についてよく質問をいただきます。

今回は「最近WEB開発を始めた」「これから始めたい」と言う方に向けて、WEBシステムの仕組みも含め、サーバサイドとクライアントサイドそれぞれの言語の役割について、解説をしたいと思います。

WEBシステムを知ろう

さて、本題に入る前に、前提知識となるWEBシステムについて軽く触れておきたいと思います。
WEBシステムとは、クライアント(ブラウザ)とサーバが通信を行い「WEBサイトの閲覧」ないしは「WEBサービスの利用」を行うための仕組みを指します。

以下のようにブラウザがあるサーバに対し欲しい情報をリクエストし、サーバがリクエストに応じHTML/CSS/JavaScriptファイル等の必要なデータ(レスポンスデータと言います)を返します。

f:id:kabaneshi:20180816035759p:plain

多くの場合、ブラウザはサーバから返却されたレスポンスデータを解釈し、ページの描画を行います。
では、ブラウザはどのような手段でリクエストを送るのでしょうか?
これには、いくつか手段があるのですが、わかりやすい例として「ブラウザのアドレスバーへのURLの入力」が挙げられます。この意味でURLは、クライアントとサーバが通信を行うために必要な情報の集合体と言えます。

サーバサイド言語の役割を知ろう

WEBシステムについて軽く触れたところで、本題に入りたいと思います。 サーバサイド言語とは、読んで字のごとくサーバ上で動作するプログラミング言語です。
サーバサイド言語は、クライアントからのリクエストに応じ、多くの場合「何かしらの処理」を実行した後、HTML等のレスポンスデータを作成します。
また、サーバサイド言語は複数あり、日本ではPHPやRubyといった言語が多く利用されています。

f:id:kabaneshi:20180816040030p:plain

上で述べたサーバサイド言語が行う「何かしらの処理」は多義にわたるため、一概に「これ」と言いづらいのですが、以下に代表的なものを挙げます。

  • データの操作
    サーバサイド言語は、必要に応じてデータベースという「サービスにおいて使用される様々なデータが保存される領域」との通信を行い、データの操作(作成/更新/参照/削除)を行います。 例えば、WordPress等のCMSにおける「記事の作成」や「作成した記事の取得」も、このデータ操作に当たります。

  • メールの送信
    メール送信といっても様々なケースが存在するのですが、サーバサイド言語が使用される例としては、フォームから問い合わせがあったユーザーへの自動返信や、サービスの新規登録を行ったユーザーへの認証メールの送信等が挙げられます。

  • ログイン等の会員認証
    普段SNSやECサイト等をWEBサービスを利用している方にとって、ログイン/ログアウト処理は、馴染みが深いのではないでしょうか? これらの機能を実現するのもサーバサイド言語の役割となります。

繰り返しとなりますが、サーバサイド言語は、上記のような処理を行った後、クラアイントに返却するレスポンスデータを作成することが主な仕事です。

クライアントサイド言語の役割を知ろう

複数種類のあったサーバサイド言語とは事情が異なり、クライアントサイドのプログラミング言語は、現状JavaScriptが唯一と言って良いかと思います。
そのため「WEB開発におけるクライアントサイドのプログラミング言語 = JavaScript」という認識で問題ないでしょう。
JavaScriptのプログラムは、基本的にはサーバからレスポンスデータとして返却され、ブラウザ上で動作します。

JavaScriptの主な仕事は、大きく以下の2つが挙げられます(※)。
※ この限りではありませんが、JavaScriptの主な役割を知るという段階においては、一旦以下の理解で問題ないかと思います

  • WEBサイト訪問者の操作等に応じて、ページの見た目を変更する
    JavaScriptでは、クリックやスクロール等、ブラウザ上で行われる「何かしらのユーザー操作」に応じてページの見た目を変更することができます。例えばWEBサイト上でユーザー操作に応じて特別な演出を行うといったことは、JavaScriptの得意分野という訳です。

  • ブラウザに変わりサーバへリクエスト行う
    WEBシステムついてに触れた際に、ブラウザがサーバへのリクエストを行うと述べましたが、実はJavaScriptはブラウザに代わりサーバと通信を行う機能が備わっています。 本記事で詳しく触れることはしませんが、このJavaScriptがサーバと通信を行う技術をAjax(エイジャックス)と言います。

本章ではJavaScriptがブラウザ上で動作する言語(※)であり、上で述べたような役割を担っている言語であることを覚えておきましょう。
※ 近年では、JavaScriptが担う領域が広がりをみせているため、一概にJavaScriptをクライアントサイドの言語とは言い切れません。本記事で触れているJavaScriptは、あくまで「クライアントサイドの言語」としてのJavaScriptであることをお伝えしておきます。

また、サーバサイド言語を使用する場合、多くはサーバ環境に言語のインストールを行う必要があるのですが、JavaScriptはブラウザ上で動作する言語であることから「言語そのものがブラウザに実装されている」少々特殊な言語であることも覚えておきましょう。(そのためインストール作業は不要です)

まとめ

いかがでしたでしょうか?
WEB開発を始めようと思った際に、WEB上で検索をすると複数のプログラミング言語がヒットし、混乱してしまうこともあるかと思います。
しかし、WEB開発で使用するプログラミング言語の役割は、大きくサーバサイドかクライアントサイドのいずれに分類されることが、本記事を通してお分かりいただけたのではないかと思います。
混乱を招くのは、多くの場合サーバサイド言語の選択肢の多さにあるように思います。
サーバサイド言語の選択については、TECH DRIVEのメンバーでもある佐藤が執筆した「プログラミング言語と業界での使われ方の関係こんな感じで合ってます?私目線書いてみます。」 を合わせて読んでいただけると、より良い理解が得られるかと思います。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発にのぞむにあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

JavaScriptのクロージャって何が嬉しいの?という皆様へ

f:id:kabaneshi:20160318114239j:plain

はじめに

TECHDRIVEの小笠原です。
JavaScriptを書いていると様々な場面で見聞きする「クロージャ」というワードですが、こいつが中々のくせ者です。
調べて概要を読んでみてもイマイチ理解できないという方は、多いのではないでしょうか。
私も初めてクロージャに触れた際に、理解に苦戦したのを覚えています。

本記事は、上で述べたような経験も踏まえ、なるべくクロージャをわかりやすくお伝えすることを目的に書かせていただきました。
また、以下の理解があることを前提に本記事を書きましたので、あらかじめご了承くださいませ。

  1. オブジェクトを理解している(プロパティ/メソッドの用途やオブジェクト内でのthisの性質は理解している)
  2. 関数単位で変数のスコープが作成されることを知っている
  3. スコープチェーンを理解している

1. 変数の寿命を知ろう

突然ですが、ある関数に属する変数の寿命(いつまで参照可能か)を問われた時、みなさんはどのように答えますか?
正解は「変数が属する関数(本記事では親関数とする)の実行が終了するまで」ですね。
なぜこのような話を挟んだかというと、JavaScriptでは、親関数の実行後も変数を参照する手段が存在するためです。
以下のコードを見てください。

function person () {
  var name = 'Ken';

  return {
    greet: function() {
      'My name is ' + name;
    }
  }
};

var ken = person();
ken.greet(); // 結果: My name is Ken

greetメソッドの結果は皆さんの期待通りでしょうか? 「当たり前だ」という方は、先ほど上で述べた変数の寿命は「変数が属する関数の実行が終了するまで」を思い出してください。
上記のコードでは親関数の終了後も、greetの内部から変数nameの値を参照できていることになります。
これは、関数内の変数の寿命が「変数が属する関数の実行が終了するまで」という事実と矛盾します。
詳細は後述に譲りますが、この理由がクロージャです。

2. クロージャを知ろう

さて、いよいよ本題のクロージャの話になります。
まずはじめにクロージャとは「関数がもつ性質である」ということをお伝えしておきます。

関数には「自身が定義された時のコンテキスト(スコープとほぼ同義)に存在する変数を参照し続けられる」という性質があります。 多くの場合、この性質を指してクロージャと言います。

ここで、先ほどのコードを思い出してください。 greetは親関数personの実行が終了した後も、変数nameを参照することができていました。 これは、personの返り値に含まれるgreetが関数であるため、上で述べた性質を持っていることを意味します。 greetは、自身が定義された時のスコープ内の情報を持っているのです。
故に、person実行後も、親関数(person)のスコープに存在する変数nameを参照し続けることが出来る訳ですね。

3. クロージャのメリットを知ろう

クロージャが「定義時のスコープ内の変数を参照し続ける」という関数の性質であることは、お分かりいただけたかと思います。
とはいえ、それの何が嬉しいのでしょうか?
実用的な例の一つとして、モジュールパターンが挙げられます。
例えば、プログラミング時にある用途ごとに処理を切り出したい場合、Class等を使用することが多くあります。

しかし、JavaScriptに置いてClassはES6から実装された機能であり、現状ES6が実装されていないブラウザも存在します。そのため対応ブラウザによっては、Classの使用が難しいシーンがあるかと思います。※ 但し、Babel等のトランスパイラの使用することで、この問題は解決できます。
このような背景があり、JavaScriptでは、関数を使用し「Classのようなもの(※)」を実現することがあります。
※ 本記事で述べる「Classのようなもの」とは、ある用途ごとに処理をまとめる手段を指します(継承やコンストラクタ相当の機能は持たないものとします)

その手段の1つとして、クロージャを使用したモジュールパターンが挙げられます。

例えば「URLからクエリストリングを取得して、オブジェクトに変換して返す」という処理を実装したいとします。
これを実現する方法は複数あるのですが、まずはクロージャの使用例と比較するため、オブジェクトを用いてこの処理を書いてみたいと思います。

var SplitQueryString = {
  init: function(){
    this.queryString = location.search;
  },
  toObj: function() {
    var queries = this.queryString.slice(1).split('&');
    var results = {};
    for(var i = 0; i < queries.length; i++) {
      var query = queries[i].split('=');
      var key = query[0];
      var val = query[1];
      results[key] = val;
    }
    
    return results;
  }
}

// ex: URLがhttp://example.com?hoge=hogehogeの場合
SplitQueryString.init();
SplitQueryString.toObj(); // 結果: {hoge: "hogehoge"}

上記のコードは一見問題なさそうに見えます。
しかし、オブジェクトはプライベートなプロパティを持つことができません。
例えば、初期化処理(initメソッド)でオブジェクトに追加しているqueryStringというプロパティがあります。
このプロパティはオブジェクト内でクエリストリング(location.searchの値)を持ち回るためのプロパティであり、初期化時(init実行時)に一度だけ値の代入を行えば良いので、以降の処理で値が変更されることを望みません。
しかし、初期化処理の実行後も、以下のようにすることでこのプロパティへの変更は可能となります。

SplitQueryString.queryString = "huge"

※ 今回のコードにおいて、queryStringにはlocation.searchの結果が代入されることが前提となっているため、上記の変更を行なった上でtoObjメソッドを実行すれば、当然エラーとなります。

上で述べた例は少々極端ではありますが、このようにJavaScriptで「Classのようなもの」を実現する際に、プロパティやメソッドをプライベートにしておきたい(外部から変更されたくない)シーンがあります。
この問題を解決する手段の一つとして、モジュールパターンがあります。
モジュールパターンではクロージャの性質を利用し、プライベートなメソッドやプロパティを実現します。
先ほどのコードをモジュールパターン使用した実装に変更してみます。

var SplitQueryString = (function() {
  var queryString = location.search 
  
  return {
    toObj: function() {
      var queries = queryString.slice(1).split('&')
      var results = {}
      for(var i = 0; i < queries.length; i++) {
        var query = queries[i].split('='); 
        var key = query[0]
        var val = query[1]
        results[key] = val
      }
    
      return results   
    }
  }
})()

// ex: URLがhttp://example.com?hoge=hogehogeの場合
SplitQueryString.toObj() // {hoge: "hogehoge"}

上記のコードでは、変数SplitQueryStringに即時関数を代入しています。
※ 即時関数がオブジェクトを返しているため変数SplitQueryStringの値は、オブジェクトということになります。

1つ目のコードとの大きな違いとしては、以下の2点が挙げられます。

  • オブジェクトを即時関数でラップして返している
  • 即時関数の中でinit相当の処理を行なっているため、返り値となるオブジェクトからinitメソッドが消えている

ここで注目すべきは、1つ目の差分です。
なぜ、オブジェクトを即時関数でラップしているかというと、返り値であるオブジェクト内のメソッド(関数)のクロージャの性質を利用するためです。

toObj(クロージャ)は、親関数(即時関数)の実行終了後も、定義時のスコープ内に存在する変数queryStringを参照し続けることができます。
※ ややこしいのですが、クロージャの性質を利用することを目的とした関数自体を指してクロージャと呼ぶこともあるため、toObjをクロージャと表記しています。
queryStringはクロージャが内側で保持している変数であるため、クロージャ以外からは参照/変更することができません。

このクロージャの性質により、関数でプライベートなプロパティやメソッドを実現することができます。

4. まとめ

いかがでしょうか。
クロージャは、その性質を見聞きしただけでは理解が難しく、JavaScript中級者へのステップアップにあたり、鬼門となりがちです。 本記事が少してでもクロージャへの理解にお役立ていただけたのなら幸いです。
また、今回はクロージャのメリットに焦点を当ててきましたが、クロージャがもたらすものは、メリットだけではありません。
本記事でもお伝えした通り、クロージャの「スコープ内の変数を参照し続ける」という性質は、見方を変えると本来関数の実行終了と共にメモリ上から消えるはずのデータが、残り続けることを意味します。
この点に関して、本記事で詳しく触れることはしませんが、この性質はメモリリーク等を引き起こす要因にもなり得ます。
そのため、JavaScriptの担う領域が大きければ大きいほど、クロージャへの正しい理解が求められます。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントサイドからバックエンドまで各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発に臨にあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

チーム開発トレーニング

既にある程度コーディングが可能なのでチーム開発を経験してみたいという方にはチーム開発トレーニングがおすすめです。 GitHub Flowを使用し、実践の開発フローを体験していただきながらスキルアップ可能なトレーニングとなっています。

WEBサービスチーム開発トレーニング

とりあえずググれ?もうちょっとコツとかないんですか?(Google検索のコツ)

はじめに

TECHDRIVEの佐藤です。 プログラミングするのに、初学者でも上級者でも安定して必要とするスキルと言えば検索力ではないでしょうか。 ググり力 なんて言ったりもしますね。

ただ、「検索しよう」と言われても実際どういう風にすると望む情報が効果的に取得できるのか、ということはありそうです。 ここではプログラミングにまつわるググりをテーマに何点かポイントをお伝えしたいと思います。

検索ワードをどうするか

とりあえず単語を繋げる

何か方法がわからない時に多いやりかたです。

  • rails ファイルアップロード
  • php7 クラス コンストラクタ
    • 場合によってはバージョンを入れると良い

エラーメッセージを貼る

どうしても解消できなさそうなエラーの時(エラーの英語はちゃんと読んでみた上で)、特徴的な文章を探して貼ります。

Uncaught SyntaxError: Unexpected token =

こういうのをベタッと検索窓に貼ります。ただしこの場合には、Uncaught(捕まえられない=今は処理できないという理解で良いです)、Syntax(文法)、Error(エラー)なので、何か文法を間違えたことはエラーメッセージからはわかります。本来はもう少しよくわからなそうなのを扱います。

英語で文章を作ってみる

carrierwave failed save on validation 全部英語にします。この方法で良いのは、英語版のstack over flow が当たりやすくなることです。英語で解説が出ますが、英語は頑張ります。英語だけは頑張ります。何度もやると慣れるので頑張りましょう。

そのほか

  • 使っている言語、フレームワーク(もちろんそれとは関係ない場合には入れませんが)の名前を入れる。 PHPRails
  • 期間を限定する 1年以内など
  • 対象でない言葉が多い - を頭につけて省く -slack

どれくらい絞るかは言葉次第です。一般名詞の場合には補わないと難しい。 例えば express だと広すぎる。 node.js express とか。

検索結果

結果は膨大な量が引っかかるので、ここから効率的に探していくことが必要です。タイトルと、一部文章の引用を見ます。この時ヒットした言葉は太字になっているので前後の雰囲気を見ていきます。明らかに違いそうなのはこの時点で省けます。 f:id:ms2sato:20180809215257p:plain

以下、サイトごとによく見るべきかどうかをザックリ挙げてみます。私はこんな感じです。

よく読む

課題解決系

  • 本家のGitHub
  • Stackoverflow 英語

上記には劣る場合もありますが、以下も見ます。英語がよく理解できないとか、ちょっと概念が難しいものは日本語でわかる方が楽だったりしますよね。

  • Qiita
  • 個人のブログ

概念、知識系

例えば「トランザクション」とか、そもそも何なのかわからない言葉を調べようとしている場合です。IT系の用語はこういうのが多いので、何かしらの概念の解説が必要になると思います。最近はこの辺少なそうですが、記事形式で中身の丁寧なものが多いです。例えば以下でしょうか。

あまりよく読まない。飛ばすこと多い。

WIkipediaとか何かのまとめ系サイト

おしまいに

検索が上手だとプログラミングの上達もはやいと思うので、ぜひ検索上手になりましょう。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントサイドからバックエンドまで各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発に臨にあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

Ruby Climbing

週1からはじめられる「Ruby」でWEB開発の基礎が習得できる塾です。現役のプログラミング講師&Rubyエンジニアがプログラミング入門からフレームワーク(Sinatra/Ruby on Rails)を使用した本格的なWEB開発の学習までをしっかりとサポートします。

ruby climbing

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

チーム開発トレーニング

既にある程度コーディングが可能なのでチーム開発を経験してみたいという方にはチーム開発トレーニングがおすすめです。 GitHub Flowを使用し、実践の開発フローを体験していただきながらスキルアップ可能なトレーニングとなっています。

WEBサービスチーム開発トレーニング

「jQueryで書くとグチャグチャになる」への処方箋、もしくは昔々のJSでのモジュール化について

f:id:ms2sato:20180805232858j:plain

はじめに

TECHDRIVE の佐藤です。

ちょっとあるところで盛り上がったので、ちゃんと書いたらどうなるかなと*1。 「『jQueryで書くとグチャグチャになる』という話を聞くけれど、それってモジュール化をちゃんとしていないからじゃないか」 という話から発しています。

以前からJavaScriptと付き合っている人にとっては、ただの古めかしいイディオムです。ただ、SPAするレベルでなく「ちょっとした動き」をさせたいような場合もソコソコあると思うので、そういう人には参考になるかもしれません。

というわけでこれはレガシーなJavaScriptと付き合う場合のあるあるな方法を今更紹介したものです。特に何か目新しいものがあるわけでもなく、最新のJavaScriptを相手にしていける環境の人には全く関係のないものです(周辺技術が既にこういう部分をカバーして進んでいますし、今後ブラウザにも追加されていくだろうからです)。

解決したい問題

  • RailsなどでAssetsCompileしていると、そもそも全てのJSが動いてしまって「どれが動くのか」わからない。「他の画面の為の内容が影響してバグる」
  • 外部ライブラリ等と名前が被ってしまう問題を回避したい。
  • 動作するものの単位がわかりやすいようにしたい。

どうやるの?

サンプルプロジェクトがあった方が解りやすそうだったのでババっと書いてみました。 github.com 今回のイメージは下記です。

  • index.html(画面)
  • views/index.js (index.htmlで呼ばれるエントリポイントとしてのJavaScript)
  • components/*.js (画面から呼ばれる部品達)

モジュール化

この一言でほぼ答えになるのですが、ちゃんとモジュール化をしましょうという話です。モジュール化と名前空間の適用がこの課題へのコスパの高い対応ではないでしょうか。自分の場合には昔々に名前空間を作れるライブラリを書いているので、これをよく使います。このサンプルでは、viewscomponents配下のJS達がモジュール化されています。

// ネームスペースを指定して、外に露出させない。
// 以下のように書くと、 MYAPP.COMPONENTS.Popupper が使えるようになる。
LIMO.namespace('MYAPP.COMPONENTS')(function(ns){

  function Popupper(el){
    this.$el = $(el)
  }

  Popupper.prototype.setup = function() {
    // 簡単のためalertだけにしているが、ボタンを押したら起きる動作を
    // ここでまとめるようなイメージ。
    this.$el.on('click', function(){
      alert('test');
      return false;
    });
  }

  // ns に付けたものはネームスペースに結合されて公開される
  ns.Popupper = Popupper;
});

エントリポイントを1つにする

一つの画面についてイベントハンドラを付けたりするような、最初の「仕掛け」をするコードの入り口を一つにします*2。index.htmlには下記の1行が書かれています。入り口は必ずこのような簡単な関数一つです。名前空間はプロジェクトで適切に決めれば良いと思いますが、Railsだと、 MYAPP.VIEWS.[Controller名].initShow() などのようにして、 initNew()initEdit() を同じ内容にすると便利だったりするかもしれません。

<script>MYAPP.VIEWS.INDEX.init()</script>

エントリポイントから各コンポーネントを呼ぶ

基本的には上記のエントリポイントの関数内には $.ready が書かれるイメージです。そして、その関数の中から各コンポーネントが呼ばれます。GitHubに載せたサンプルではもう少し複雑な内容も書いてみました。下記だと簡単すぎて恩恵がよく見えないかもしれませんので。

イメージとして複雑な処理はコンポーネントの中に封じ込めて、エントリポイントはなるべくそれを使うだけにするような形です。また、コンポーネントがDOMツリーへの情報を必要とする場合には、ルートのCSSセレクタを設定するなどします(動きが複雑だと複数のセレクタを設定するかもしれませんね)。

LIMO.namespace('MYAPP.VIEWS.INDEX')(function(ns){
  ns.init = function(){
    $(function(){
      // クラスっぽくしているが、簡単ならもちろん関数でも良い。
      // 大抵はコンポーネントのルートになるCSSセレクタを指定する。
      var popupper = new MYAPP.COMPONENTS.Popupper('.mytrigger');
      popupper.setup();
    });
  }
});

おしまいに

「各画面にちょっとした演出のJS書きたいんだけどなぁ」くらいならこの程度がわかっていると簡単ですよってことで。ガッツリやりこむのが見えている場合には今は色々便利な道具があると思うのでちゃんとやってくだされ。

PR

TECH DRIVE協賛企業のサークルアラウンド株式会社では、プログラマーの成長を加速させるためのトレーニングを行なっています。フロントサイドからバックエンドまで各種バリエーションがございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

JavaScript Climbing

JavaScriptに特化したトレーニング「JavaScript Climbing」を行なっています。
this、クロージャ、Class等、中〜大規模のフロントエンドの開発に臨にあたり、必須となるスキル習得を現役のWEBエンジニアが徹底してサポートします。
2週間の無料体験期間がございますので、ご興味がある方は是非以下のリンクより詳細をご覧ください。

circlearound.co.jp

個別トレーニング

短期間でぐっと成長したい方は弊社主催の個別トレーニングがおすすめです。 トレーニング内容は、受講者の方の課題/要望をお伺いした上で、フルオーダメイドで作成させていただきます。 詳細は以下のリンクよりご確認ください。(応募者多数の場合には時間を別途ご用意する予定です)。

WEBプログラミング個別トレーニング

チーム開発トレーニング

既にある程度コーディングが可能なのでチーム開発を経験してみたいという方にはチーム開発トレーニングがおすすめです。 GitHub Flowを使用し、実践の開発フローを体験していただきながらスキルアップ可能なトレーニングとなっています。

WEBサービスチーム開発トレーニング

*1:Rails使っている人ばかりだったので、ちょいちょいRailsの話ありますけど興味ない人は読み飛ばしてくだされ

*2:実は概念化されていれば一つの関数にまとめたりする必要も無いですが、簡単に強制するなら一つの関数ですね

TECH DRIVEについて

TECH DRIVEは「技術者の成長を加速させる」をキーワードに都内で活動をしているコミュニティです。
TwitterやFacebookにて技術ネタやイベント情報の発信を行っていますので、ご興味があれば、いいねやフォローをお願いいたします。