## JavaScript とのつきあい方 ### ここがダメだよJavaScript ### http://goo.gl/ovgS7Z Created by Shogo Kawase
## 自己紹介 - 河瀨翔吾(SHOGO) - 株式会社アークスタイル CTO - twitter: [@shogogg](http://twitter.com/shogogg) - PHP, Java, **Scala**, JavaScript, **TypeScript** - Android - AngularJS - Play Framework
## 本日の内容 - JavaScript の抱える問題点 - その解決方法があったりなかったり
## 問題点-1 ### 名前
## JavaScript の名前の由来 - 元々は **Mocha** という名前で開発されていた - ライバルが Java アプレットだったのでそれを意識していた模様 - 1995年9月, Netscape Navigator 2.0 のベータ版に搭載されるときに **LiveScript** に改名 - 同年12月, Netscape 社は Java の開発元である Sun Microsystems 社と業務提携し, LiveScript を **JavaScript** に改名することを発表 - ライバルだったはずの Java の人気にあやかろうとした模様

その結果...

- Q. ホームページでJAVAが動きません>< - A. Java と JavaScript は別物です...

解決方法

あきらめましょう

## 問題点-2 ### 処理系の互換性
## JavaScript 処理系の互換性 - サーバーサイド - node.js 一強状態なのでスルー - クライアントサイド - ブラウザごとにAPIはおろか文法エラーすら異なる状態 - レンダリングの差も考えると絶望できるよね! - 最近はまだマシになってきた方

JavaScript エンジニアの殺し方

「IE6対応よろしく」

解決方法

フレームワークを使う

## フレームワークを使おう! - 主要フレームワークは基本的にクロスブラウザで動作する - 直接APIを叩かずにフレームワーク経由で利用することで処理系ごとの差を意識せずにコードが書ける - ブラウザ向けに JavaScript を書くなら **[jQuery](http://jquery.com/)** はすでに必修 - jQuery 2.x系は**IE8未満を切り捨てた**ので要注意 - ここ最近 **[D3.js](http://d3js.org/)** も台頭してきている - **[Underscore.js](http://underscorejs.org/)** も覚えておくといいかも - **[Backbone.js](http://backbonejs.org/)** などのMVCフレームワークも充実してきた - 個人的イチオシは **[AngularJS](http://angularjs.org/)**
## 問題点-3 ### クラスが使えない
## 独特のオブジェクト指向 - JavaScript はクラスベースではなく**プロトタイプベース**の言語 - JavaScript 以外には Lua とか Self とか - JavaScript は Self の影響を受けている - プロトタイプベースであることには利点もあるけど…… - クラスがあった方がシンプルに書けるケースは多い - マイナーなので学習コストが高い - 結局みんながオレオレクラスもどきライブラリを作ってしまう - prototype.js とか JS.Class とか

解決方法

ECMAScript6

## ECMAScript6 - ECMAScript = JavaScript のベースとなる仕様 - 主要モダンブラウザは ECMAScript5 準拠 - IEもIE9で ECMAScript5 にほぼ準拠、IE10で完全準拠 - 各ブラウザの ECMAScript5 準拠状況 → http://goo.gl/ObnxoG - ECMAScript6 では様々な構文・機能が追加予定 - ECMAScript6 ならクラスも書けるよ! - ただし準拠状況はまだまだ → http://goo.gl/NJiiTl

待てない

真・解決方法

TypeScript

または CoffeeScript

## TypeScript - Microsoft が開発したスクリプト言語 - コンパイルすると JavaScript が生成される - JavaScript のスーパーセット - 学習コストが低い - 既存の JavaScript 資産の移行や共用もかんたん - モジュールやクラスなど ECMAScript6 の仕様を一部先行導入 - **静的型付け**が可能
## コード例 ```actionscript class ClassName extends SuperClass implements IInterface, IInterface2 { // メンバ変数 varName: string = 'Hello World!'; // コンストラクタ constructor(arg1: string, args2: number) { ... } // メンバ関数 sum(arg1: number, arg2): number { return arg1 + arg2; } // アクセス修正子(public or private)もあるよ! private privateMethod(arg1: string, arg2: string): string { ... } // 他にも色々あるけど省略 } ```
## CoffeeScript - Ruby や Python, Haskellに影響を受けた文法を持つスクリプト言語 - コンパイルすると JavaScript が生成される - 様々な糖衣構文によりコード量を大幅に削減できる
## コード例 ```coffeescript class ClassName extends SuperClass # メンバ変数 varName: 'Hello World!' # コンストラクタ constructor: (arg1, arg2) -> ... # メンバ関数 sum: (arg1, arg2) -> arg1 + arg2 ```
## 問題点-4 ### 巻き上げにハマる
## コード例 ```javascript var name = "GLOBAL"; function foo() { console.log(name); // GLOBAL ……と思いきや undefined var name = "LOCAL"; console.log(name); // LOCAL } foo(); ```
## コード例 ```javascript var name = "GLOBAL"; function foo() { var name; console.log(name); // 初期化されてないので undefined name = "LOCAL"; console.log(name); // LOCAL } foo(); ```
## 巻き上げ - 他の言語ではあまり見かけない JavaScript 独特の仕様 - 関数内のどこかでvar宣言すると先頭で宣言したものとみなされる - 初期化はされないので値は `undefined` となる - 関数内のどこかで関数を定義すると先頭で定義したものとみなされる - 定義も行われるので、関数定義より前に呼び出してもエラーとはならない - 問題は言い過ぎかもしれないがハマりやすい

解決方法

単独varパターン

## 単独varパターン - var宣言と初期化を関数の先頭ですべて行う - 実際の挙動とコードを併せることで無用な混乱を避けることができる
## コード例 ``` function sum(n) { // ローカル変数は先頭ですべて宣言し初期化する var total = 0, i = 0; for (i = n; i > 0; --i) { total += i; } return total; } console.log(sum(10)); ```
## 問題点-5 ### スコープがショボすぎる
## JavaScript のスコープ - JavaScript のスコープは2種類 - グローバルと関数ローカル - ブロックスコープ?なにそれおいしい? - 関数でvar宣言を忘れるとグローバル変数を使ってしまう
## コード例 ```javascript var x = 100; // グローバル変数 // 1..n の総和を求める function sum(n) { var total = 0; // var x しないまま x を使って for ループ for (x = n; x > 0; --x) { total += x; } return total; } console.log(x); // 100 が表示される console.log(sum(10)); // 55 が表示される console.log(x); // 100 ではなく 0 が表示される ```

解決方法

- グローバル変数は使わない - 単独varパターン - let宣言
## let宣言 - var宣言と異なり、宣言を行った**ブロック**がスコープとなる - ECMAScript6, JavaScript 1.7 の仕様 - 各種ブラウザで実装が進んでいるが…… - Firefox では script 要素の type 属性にオプションの指定が必要 - Chrome では chrome://flags から JavaScript の試験運用機能を有効にする - IE は IE11 からの対応 - node.js でも起動時にオプションを指定しないと有効にならない

ぶっちゃけまだ使えない

## 代替案 - for ループの代わりに `Array.prototype.map` などを使う - IE8 未満に対応する場合は `jQuery.map` などで代用可能 - 無名関数の即時実行で擬似的なブロックスコープを実現可能 - どうしてもブロックスコープ的な事がしたい場合の最終手段 - 素直に if 文の中身を別関数にしちゃうのも手
## 問題点-6 ### this問題
## this問題 - JavaScript の this はとにかくわかりづらい - 記述した位置はおろか、関数の呼び出し方によっても変わる - Strict Mode を使ってると同じコードでも変わったりする
## コード例 ```javascript var obj = { value: 'Hello World!!', hello: function() { var log = function() { console.log(this.value); }; console.log(this.value); // Hello World!! log(); // undefined } }; ```

解決方法

this の使用を控える

## this の使用を控える - this を使わないで済む場面では this を使わないようにする - コードを読むとき「ここの this ってなんだろう?」って考えなきゃいけないのを避ける - 特に jQuery の各種APIにおける this の挙動は凶悪

どうしても使いたいなら

解決方法

覚える

## JavaScript の this 覚え方 - 基本は `this = グローバルオブジェクト` - ブラウザの場合は `window` がグローバルオブジェクト - 関数がオブジェクトに属する場合は関数が属するオブジェクト - グローバル関数の場合はグローバルオブジェクトに属する - 関数をコンストラクタとして使用した場合は生成されるオブジェクト - apply, call などを使って呼び出された場合は指定されたオブジェクト - 実はこれだけじゃなくて本当はもっと複雑 → http://goo.gl/QONn4T

覚えられないと思うので

宿題

帰ったら「JavaScript this」でググれ

## 問題点-7 ### コールバック地獄
## コールバック地獄 - JavaScript で本格的にコーディングしているとコールバックの嵐に悩まされる事になる - ブラウザでAjax通信したりアニメーションさせたり - node.js では非同期APIが基本, かつコールバックを使うことがルール化されている
## コード例 jQuery でコンテンツをAjaxで読み込んでアニメーションして... ```javascript $.get('hoge.txt', function(data) { var div = $('<div>').text(data).appendTo('body'); div.fadeIn(200, function() { setTimeout(function() { div.fadeOut(500, function() { alert('終わったよ!'); }); }, 1000); }); }); ```

ネストしすぎ

解決方法

Deferred/Promise

## コード例 さっきのコードを Deferred/Promise スタイルで書き直す ```javascript $.get('hoge.txt').then(function(data) { return $('<div>').text(data).appendTo('body').fadeIn(200).promise(); }).then(function(div) { var deferred = $.Deferred(); setTimeout(function() { deferred.resolve(div); }, 1000); return deferred.promise(); }).then(function() { return div.fadeOut(500).promise(); }).then(function() { alert('終わったよ!'); }); ```

ネストが減ったよ!

※なんか複雑になったように見えるのは例が悪い気がします

## Deferred/Promise って何? - **jQuery** が Deferrd/Promise スタイルのAPIを提供 → 一躍有名に - jQuery の Ajax系APIの戻り値はすべて Promise インターフェースを持つ - `$.Deferred()` で Deferred オブジェクトを生成できる - **node.js** では [Q](http://documentup.com/kriskowal/q/) が有名 - 実は node.js だけでなくブラウザ環境でも Q は使える - **AngularJS** には前述の Q をベースにした $q というサービスが標準で含まれている

Deferred わからないとお話にならない時代

## Deferred を使った非同期関数 ```javascript function asyncFunction() { // Deferred オブジェクトを生成する var deferred = new $.Deferred; // 非同期処理 setTimeout(function() { var result = null; // Do something... // 完了したら Deferred オブジェクトの resolve() を呼び出す // 結果を引数に渡すことができる ※省略してもOK deferred.resolve(result); }, 5000); // Promise オブジェクトを生成して返す return deferred.promise(); } ```
## Deferred を使った非同期関数を利用する ```javascript // 非同期関数を呼び出す var promise1 = asyncFunction(); // コールバックを登録する var promise2 = promise.then(function(result) { // Do something... }); // promise.then の戻り値は新たな Promise オブジェクトなので // 上記コールバックが完了したときのコールバックも登録できる promise2.then(function() { // Do something... }); // つまりこう書ける asyncFunction().then(function(result) { // Do something... }).then(function() { // Do something... }); ```

Deferred/Promise の仕組み

Deferred/Promise フローチャート
## Deferred/Promise すごいよ! - Deferred/Promise はネストを減らすだけではなく、並列処理や後からの変更にも強い - `$.when([...])` や `Q.all([...])` を使って並列処理 - 処理の順番が変わっても `.then(...)` を入れ替えるだけ - 処理の内容が変わっても各関数が独立しているので変更しやすい
## Deferred/Promise は銀の弾丸ではない... - 処理の内容によってはネストが増えるケースもある - 単純にコールバックを受け取る関数に比べてコードが煩雑になる - そしてこうなる → http://goo.gl/QONn4T ※マニア向け
## 問題点-8 ### function地獄
## Function地獄 - Deferred/Promise でコールバック地獄からは脱出できる(かも) - でも function って文字数多くてタイピング辛いしコードも見難い - もっともっとラクにコーディングがしたい!

解決方法

アロー記法

## アロー記法を使うと... ```javascript var f = function(a, b, c) { return a + b + c; }; someFunction(1, 2, function(a) { // Something to do... }); ``` ↓ ```javascript var f = (a, b, c) => a + b + c; someFunction(1, 2, (a) => { // Something to do ... }); ```

ただし

ECMAScript6 の仕様です

- Firefox のみ23以降で使用可能 - つまりまだ使えない……

そこで

- TypeScript または CoffeeScript ならアロー記法が使える! - TypeScript なら静的型付けもあるし JS との互換性も高いよ!

TypeScript 最高!

## 問題点-9 ### 型システム
## JavaScript はご存じの通り動的型付け - まぁ、この辺は宗教論争になりがちですが…… - 動的型付けであっても, 小規模なアプリケーション/開発体制なら事足りる場合もあるかも - 中〜大規模なアプリケーション/開発体制なら静的型付けのメリットは大きい

静的型信者のための解決方法

TypeScript 使おう

## TypeScript なら…… - 既存 JavaScript ライブラリとの併用もできるよ! - 既存ライブラリに型情報を提供する方法もあるよ! - ジェネリクスもあるよ! - 基本的な構文エラーとかはコンパイル時に検出できるよ!

TypeScript 最高!

以上

ご静聴ありがとうございました

書籍紹介