55
JavaScript Study for learners 高高 高高 @rika_t / rika-t

JavaScript study for learners

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: JavaScript study for learners

JavaScript Study for learners高橋 健太@rika_t / rika-t

Page 2: JavaScript study for learners

Who am I?

•rika-t▫Twitter: @rika_t▫ドワンなんとかという会社でニコニなんとかを

作っています▫それより以前は IBM で Dojo Toolkit を使った簡

単なお仕事をしていました▫JavaScript 歴は 3 年くらい ?

最近はほとんど書いてない

Page 3: JavaScript study for learners

What’s this presentation?

•対象▫JavaScript で何となく動くものを作ったことが

ある、位の人•目的

▫もう少し JavaScript のことを深く理解できるようになる

▫他の人のコードでも、一人で原因調査ができる&解決できるようになる

Page 4: JavaScript study for learners

Agenda

•JavaScript 仕様の話•JavaScript の用語•JavaScript の特徴

▫レキシカルスコープ▫プロトタイプベースのオブジェクト指向言語

•JavaScript のデバッグ▫見通しの良いコードを書く▫開発者ツールの使い方

•将来の JavaScript•JavaScript を継続して学ぶ

Page 5: JavaScript study for learners

JavaScript仕様の話

Page 6: JavaScript study for learners

JavaScript の歴史• JS history : http://

www.slideshare.net/badatmath/js-shistory を読みましょう

• どうでもいいこと▫JavaScript は Java に似ているらしい

仕様書にそう書いてある どこが

▫"JavaScript" の S は大文字 JAVAScript とか Javascript とか Java Script とかダメです

Page 7: JavaScript study for learners

JavaScript の仕様はどこで決まる?• 困ったら仕様を調べるのがイケてるエンジニア

▫ JavaScript には ECMAScript というベースとなる言語仕様がある これを決めているのが Ecma International という団体

http://www.ecma-international.org/publications/standards/Ecma-262.htm

▫ ここに、 DOM ツリーの操作やブラウザー固有の仕様に伴うオブジェクトが追加される これを最終的に勧告するのが W3C

例えば、 localStorage の動きを調べたかったら、以下のページを見ることになる▫http://www.w3.org/TR/webstorage/

Page 8: JavaScript study for learners

ブラウザーの仕様実装状況•で、それを実装するのが Google であったり、

Mozilla であったり、 Microsoft であったり▫実装状況に差異が生まれる

•調べる▫http://caniuse.com/

Page 9: JavaScript study for learners

JavaScriptの用語

Page 10: JavaScript study for learners

用語• オブジェクト

▫ 0 以上の属性を持つ、プロパティの集合• プロパティ

▫ 他のオブジェクト , プリミティブ値 , 関数の入れ物 Object properties in JavaScript :

http://www.2ality.com/2012/10/javascript-properties.html?m=1• 属性

▫ 書き込み可能、列挙可能などのプロパティの振る舞いを規定するもの

• 関数▫ 呼び出し可能なオブジェクトの一種( Object 型)

• メソッド▫ オブジェクトのプロパティとして関連付けられている関数

Page 11: JavaScript study for learners

JavaScriptの特徴レキシカルスコープ

Page 12: JavaScript study for learners

変数のスコープ• 基本的に関数に入るとスコープが変わる

▫ ただし、そのスコープで存在しない変数にアクセスすると、 " 構文上の "1 つ上のスコープを見に行く 静的スコープとかレキシカルスコープとか呼ぶ

動的スコープでは実行コンテキスト上の 1 つ上のスコープを見る(なにそれ怖い)

var maxLimit = 10;var console_array = function(target, from, to) { for (var i = from; i < to; i++) { if (i >= maxLimit) { break; } console.log(target[i]); }};console_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 0, 100);maxLimit = 5;console_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 0, 100);

Page 13: JavaScript study for learners

var self = this;• 別の関数に入ると this が変わってしまう

▫ 前の this を覚えておきたい時に使う

var myObj = {};myObj.wait = 1000;myObj.waitFor = function(clos) { this.isFinished = false; var self = this; var func = function(clos) { setTimeout(function() { if (!self.isFinished) { clos(); } else { func(); } }, self.wait); }; func(clos);};

myObj.waitFor(function(){ console.log("hogehoge");});

Page 14: JavaScript study for learners

(function(){})();• スクリプト内に JavaScript をいきなり書き始めると、変数がグ

ローバル空間に定義される▫ グローバル空間の汚染を防ぐために、最初に関数ブロックを作る

このブロックの中で、グローバル空間からアクセスできるようにしたいものだけ、指定してエクスポートする

(function(){ var myPrivateVariable = 42; var hogehoge = { myFunction: function() { … }; window.hogehoge = hogehoge;})();

var myPrivateVariable = 42;var hogehoge = { myFunction: function() { …};

Page 15: JavaScript study for learners

(function(global){})(this);• 同様に、グローバル領域から対象を絞ってブロック内にオブジェク

トを取り込みたい時は、以下のようにする

(function(global){ var myPrivateVariable = 42; var hogehoge = { myFunction: function() { … }; global.hogehoge = hogehoge;})(window);

(function($){ var myPrivateVariable = 42; var hogehoge = { myFunction: function() { … }; $.hogehoge = hogehoge;})(jQuery);

Page 16: JavaScript study for learners

グローバル領域の汚染• var を付け忘れるとグローバル空間の変数として定義される

var util = { copy: function(from, to) { for (prop in from) { //var つけ忘れ to[prop] = from[prop]; } return to; }};

var prop = "secret_key";function mytest() { console.log(prop); };

Page 17: JavaScript study for learners

JavaScriptの特徴プロトタイプベースのオブジェクト指向言語

Page 18: JavaScript study for learners

JavaScript の型•5つのプリミティブと object

▫number, string, boolean, null, undefined ただし、 typeof null; //”object” …

▫object•組み込みオブジェクト

▫18 個(後述) 逆に言えば、これ以外のオブジェクトがコードに出

てきたらブラウザー側のオブジェクトと思えば良い

Page 19: JavaScript study for learners

組み込み型と組み込みオブジェクトObject

Object

Undefined

Null

Boolean

Number

String

: 組み込み型

Function

Array

String

Boolean

NumberMath

Date

RegExp

JSON

: 組み込みオブジェクト

global Error

SyntaxError

ReferenceError

EvalError

RangeError

TypeError

URIError

Page 20: JavaScript study for learners

プリミティブと object• プリミティブは値渡し、

オブジェクトは参照渡し

• プリミティブとラッパーオブジェクトは全く異なるもの

▫ ラッパーオブジェクトを new するのはあんまり良くない場合が多い▫ ラッパーオブジェクトのメソッド利用はよくある▫ でもプリミティブに対してもメソッドが呼べるけど…

これは、実行環境が内部的にラッパーオブジェクトを作ってそのメソッドを呼んでいるだけで、プリミティブにメソッドが生えているわけではない

var a = {};var b = {};a.prop = b;b.hoge = "hoge";a.prop.hoge; //"hoge"

var x1 = "hoge";var x2 = "hoge";var x3 = new String("hoge");x1 === x2; //truex1 === x3; //falsetypeof x1; //"string"typeof x3; //"object"

Page 21: JavaScript study for learners

Object オブジェクト• {} と書くと作成できる

▫ new Object(); は普通使わない• Array も Function も Object オブジェクトの子クラス

• Object は Map ではない▫ そんな感じで使えなくもないが、普通に "Map" と呼ぶものではない

Index を作ったりしないので Map として使うと遅いです▫ たまに「 JavaScript では連想配列=Object」と説明しているページがあるが、間違い▫ Map は ES6 で入ります( WeakMap も)(きっと)

http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps

var hoge = {};hoge instanceof Object //truevar hoge = [];hoge instanceof Object //truevar hoge = function(){};hoge instanceof Object //true

Page 22: JavaScript study for learners

Array オブジェクト• [] と書くと作成できる

▫new Array(); は普通使わない というか普通に使うのはやめましょう

new Array("5").length; //配列の値の初期化なので 1 new Array(5).length; //配列のサイズ指定なので 5 new Array(5, 1).length; //配列の値の初期化なので 2 new Array(variable).length // 何ができるかは variable依

存• Object の数字がキーになっている版、ではない

▫色々考えられてる、角度とか▫ 生えているメソッドも違うし、実行速度も早い

Object で Array の再実装みたいなものを作るのは推奨しないです

Page 23: JavaScript study for learners

Function オブジェクト• 関数はオブジェクト

▫ 代入したり、受け渡ししたり、インスタンス作ったり

• 関数オブジェクトは new できる▫ prototype

new されたインスタンスが持つプロパティ、メソッドを定義したもの 普通のオブジェクトにはない

▫ constructor new される時に呼ばれるメソッド 関数オブジェクト定義時に作成される

普通のオブジェクトにはない▫ this

実行しているコンテキストを示すもの(後述) オブジェクトのメソッドの場合、大抵はオブジェクトのインスタンスを指す

Page 24: JavaScript study for learners

prototype と constructor

• constructor プロパティについて - hogehoge @teramako ▫ http://d.hatena.ne.jp/teramako/20120927/p1

var MylistItem = function(title, desc) { this.title = title; this.desc = desc;};MylistItem.prototype.getTitle = function() { return this.title;};

MylistItem === MylistItem.prototype.constructor; //truevar mylistItem = new MylistItem("hoge", "hogehogehoge");mylistItem.getTitle(); //"hoge"mylistItem.getTitle === MylistItem.prototype.getTitle; //true

MylistItem

mylistItem

prototype

getTitle

getTitle

Page 25: JavaScript study for learners

this

var MylistGroup = function(id, title) { this.id = id; this.title = title;};MylistGroup.prototype.getTitle = MylistItem.prototype.getTitle; //普通やらないけど例なので

var mylistGroup = new MylistGroup(1, "Vocaloid");mylistGroup.getTitle(); //"Vocaloid"

MylistItem

mylistItem

prototype

getTitle

getTitle

MylistGroup

prototype

getTitle

mylistGroup

getTitle

Page 26: JavaScript study for learners

プロトタイプチェーンvar MylistVideo = function(title, desc, userId){ this.title = title; this.desc = desc; this.userId = userId;};MylistVideo.prototype = new MylistItem();MylistVideo.prototype.getUserId = function() { return this.userId;};

var sm9 = new MylistVideo('陰陽師 ', 'hoge', 2525);sm9.getUserId(); //2525sm9.getTitle(); //"陰陽師 "

•自分が持っていないプロパティを、遡って探す▫あったらそれを使う

sm9.getTitle = function() { return "Mylist: " + this.title;};

sm9.getTitle(); //"Mylist: 陰陽師 "mylistItem.getTitle(); //"hoge"

Page 27: JavaScript study for learners

プロトタイプチェーン

MylistItem

mylistItem

prototype

getTitle

getTitle

MylistGroup

prototype

getTitle

mylistGroup

getTitle

MylistVideo

sm9

prototype

getTitle

getUserId

getUserId

getTitle

Page 28: JavaScript study for learners

ダメな例var MylistVideo = function(title, desc, userId){ this.title = title; this.desc = desc; this.userId = userId;};MylistVideo.prototype = MylistItem.prototype;MylistVideo.prototype.getUserId = function() { return this.userId;};

var sm9 = new MylistVideo('陰陽師 ', 'hoge', 2525);sm9.getUserId(); //2525sm9.getTitle(); //"陰陽師 "

•MylistItem に getUserId というメソッドが増えてしまう ( 参照なので )

Page 29: JavaScript study for learners

ES 仕様書での prototype の説明CF コンストラクタ

prototype と P1, P2 というプロパティを持っている

CF コンストラクタのプロトタイプCFP1 というプロパティを持っている

CF コンストラクタから生成されたオブジェクト達

q1, q2 というプロパティを持っている

Page 30: JavaScript study for learners

ES 仕様書での prototype の説明補足•この図をコードで書くと、こんな感じ

▫普通にわかりづらいと思うvar CF = function(){};CF.P1 = 1;CF.P2 = "hoge";CF.prototype = function(){};CF.prototype.CFP1 = "foo";var cf1 = new CF();cf1.q1 = "cf1.q1";cf1.q2 = "cf1.q2";var cf2 = new CF();cf2.q1 = "cf2.q1";cf2.q2 = "cf2.q2";

//cf1-5 から P1, P2 にアクセスすることはできないcf1.P1; // undefined

//cf1-5 から CFP1 にアクセスすることはできるcf1.CFP1; // "foo"cf2.CFP1; // "foo"

//CFp の CFP1 を書き換えると、//cf1-5 からアクセスすると、// 全ての値が変わった ( ように見える )

CF.prototype.CFP1 = "bar";cf1.CFP1; // "bar"cf2.CFP1; // "bar"

//cf1 に CFP1 というプロパティを作ると、// その値は共有されない

cf1.CFP1 = "test";cf1.CFP1; // "test"cf2.CFP1; // "bar"

Page 31: JavaScript study for learners

JavaScriptのデバッグ見通しの良いコードを書く

Page 32: JavaScript study for learners

見通しの悪いコード• JavaScript を普通に書いていくと、イケてないコードになりがち

▫ Why? 言語仕様上、どこからでもグローバルオブジェクトにアクセスできる 同様に、 HTML がグローバルオブジェクトのように利用できてしまう HTML や CSS と連携するため、元々書きたいコードに外部リソースへの依存が激しい

• 以下のようなルールを定義する▫ コーディング規約を守る▫ クラス化( Widget化)する▫ JsDoc を書く▫ 依存を定数化する or 最初に解決する▫ グローバルへの依存を減らす

Page 33: JavaScript study for learners

コーディング規約•色々参考になるものがあるので、何か決める

▫ Google http://google-styleguide.googlecode.com/svn/trunk/

javascriptguide.xml 和訳 : http://cou929.nu/data/google_javascript_style_guide/

▫ Mozilla https://developer.mozilla.org/ja/docs/

JavaScript_style_guide▫ jQuery

http://docs.jquery.com/JQuery_Core_Style_Guidelines▫ その他いろいろ

JavaScript のいろいろなコーディングルールをまとめてみた | Web scratch : http://efcl.info/2011/0527/res2764/

Page 34: JavaScript study for learners

クラス化( Widget化)する• ある単位で、パーツとして分離する

▫ そのパーツの DOMノードは、常にこのクラスのインスタンスが抱え込んで管理する = Widget プログラミング

▫ 自身の管理するノードが決まれば、色々キャッシュもできる

function updateMylistItem() { //マイリストアイテムを更新する $("#" + mylistContainer_id).load(); //ID依存 !};function loadAllMylistItem() { // ページ内の全てのアイテムを読み込む $("#" + mylistContainer_id).load(); //ID依存 !};// 後から↓を追加すると、//updateMylistItem がおかしくなりそう…function showAllMylistItemDesc() { // 読込済みのマイリストアイテムの説明文を出す};

var MylistItem = function(targetId) { this._node = $("#" + this.containerId).find("#"+targetId);};MylistItem.prototype.containerId = window.mylistContainerId;

MylistGroup.prototype.load = function() {};

MylistItem.prototype.update = function() {};

MylistItem.prototype.load = function() {};

Page 35: JavaScript study for learners

JsDoc を書く• メソッドやプロパティの説明を書く• グローバルへの依存性があるなら明記する• 他から利用されたくないメソッド等を private であることを明記する

/** * @requires window.csrfToken; * @requires window.mylistContainerId; */var MylistItem = function(targetId) { this.targetId = targetId;};/** * アイテムが格納される DOMノードの ID */MylistItem.prototype.containerId = window.mylistContainerId;/** * ページに書き出される CSRF Token*/MylistItem.prototype.token = window.csrfToken;

/** * マイリストアイテムの情報を更新する */MylistItem.prototype.update = function(key, value) { (事前処理) this._update(key, value);};

/** * 実際に DOM を書き換える * @private */MylistItem.prototype._update = function(key, value) { ( DOM 書き換え処理)};

Page 36: JavaScript study for learners

依存を定数化する or 最初に解決する• 依存する DOM の ID などは、集中管理する

▫ 決まった値であれば prototype にする▫ 変数によって変わる値であれば、 constructor で処理する▫ 動的に変わるノードであれば、 DOM を取得するメソッドを1つ用意し、必ずそ

こ経由で取るようにする

/** * @requires window.csrfToken; * @requires window.mylistContainerId; */var MylistItem = function(targetId) { this._node = $("#" + this.targetId);};

/** * @requires window.csrfToken; * @requires window.mylistContainerId; */var MylistItem = function(targetId) { this.targetId = targetId;};MylistItem.prototype.getNode = function(id) { if (this._cache[id]) { return this._cache[id]; } else { var node = $("#" + this.targetId + "_" + id); if (node) { this._cache[id] = node; return node; } else { return null; }};

Page 37: JavaScript study for learners

グローバルへの依存を減らす • HTML 内に JavaScript の関数名を書く必要があるケースはほとん

どないはず▫ ある機能を持たせたい " 特定のノード " の印を付けるには、 Data

Attribute などを使うと良い 独自データ属性 - グローバル属性 - HTML5 タグリファレンス -

HTML5.JP : http://www.html5.jp/tag/attributes/data.html

//window.loadMylistNextPage が出来てしまう…function loadMylistNextPage() { (処理)}

(function() { var nextButtons= $("[data-nico-mylist-action='next']"); nextButtons.click(function() { (処理) });});

<button onclick="loadMylistNextPage">次のページ</button>

<button data-nico-mylist-action="next">次のページ</button>

Page 38: JavaScript study for learners

これを続けていくと…•見やすく&管理しやすくなったのは良いが…

▫JsDoc でファイルサイズが増える… JavaScriptビルドへの道

Closure Compiler

▫オブジェクトの初期化コストが高い…&ページ表示時の処理が重い… JavaScript パターンへの道

初期化遅延などのパターン適用 デバッグツールへの道

重い処理を見つけてボトルネック解消

Page 39: JavaScript study for learners

JavaScriptのデバッグ開発者ツールの使い方

Page 40: JavaScript study for learners

Internet Explorer

•開発者ツール▫F12 で起動する

IE8 から使える IE7 以前は…外部の開発ツールを入れるしかない

•機能▫JavaScript コンソール▫JavaScript ブレークポイントの設定▫コールスタックを見る▫プロファイルが取れる

Page 41: JavaScript study for learners

Firefox

•Firebug を入れる▫F12 で起動する

•機能▫JavaScript コンソール▫JavaScript ブレークポイントの設定▫DOM ブレークポイントの設定▫コールスタックを見る▫プロファイルが取れる

Page 42: JavaScript study for learners

Chrome• Developer Tools

▫F12 で起動する

•機能▫ JavaScript コンソール▫ JavaScript ブレークポイントの設定▫DOM ブレークポイントの設定▫ コールスタックを見る▫ プロファイルが取れる▫ パフォーマンス等の問題チェック

昔は Firebug もできたが…

Page 43: JavaScript study for learners

将来の JavaScript

Page 44: JavaScript study for learners

将来ではない、 "今 " のJavaScript• こんなことが今既にできる

▫ ECMAScript 5 : http://www.slideshare.net/ferrantes/ecmascript-5-10575898

• もう少しまともな継承っぽいことができる▫ Object.create

•安全なオブジェクトが作れる▫ Object.prototype.seal, defineProperty, freeze…

• プロパティの読み書き時にフック出来る▫ Getter/Setter

• JSON オブジェクト!▫ JSON

Page 45: JavaScript study for learners

将来の JavaScript•現在の最新仕様は 5.1•次のバージョン 6 が策定中

▫色々考えられてる、k( ry

•策定中の仕様や仕様案などは ES Wiki にまとまっている▫harmony:proposals [ES Wiki]

http://wiki.ecmascript.org/doku.php?id=harmony:proposals

▫harmony:specification_drafts [ES Wiki] http://wiki.ecmascript.org/doku.php?

id=harmony:specification_drafts

Page 46: JavaScript study for learners

arrow function - examples

• harmony:arrow_function_syntax [ES Wiki]▫ http://wiki.ecmascript.org/doku.php?id=harmony:arrow_function_syntax

• The Exciting Future of Javascript | Web Builder Zone▫ http://css.dzone.com/articles/exciting-future-javascript-0

• What is the meaning of this? » Yahoo! User Interface Blog (YUIBlog)▫ http://www.yuiblog.com/blog/2012/03/30/what-is-the-meaning-of-this

• ECMAScriptで提案されている arrow function について - hogehoge @teramako ▫ http://d.hatena.ne.jp/teramako/20120403/es_proposal_arrow_function

function (x) { return x * x;}

(x) => x * x

var self = this;list.forEach(function(item){ self.hoge(item);});

list.forEach((item) => this.hoge(item))},

Page 47: JavaScript study for learners

arrow function

•特徴▫this が固定化される& new できない

ES Wiki の記述 Because "this" is lexically bound, "arrow.call"

and "arrow.apply" cannot bind a different "this" parameter value, but they can pass arbitrary arguments, of course.

動的な this を使いたかったら以下のように明示的に渡す (that, x) => { that.property = x; }

Page 48: JavaScript study for learners

Class Definitions• ES Wiki にある例

function SkinnedMesh(geometry, materials) { THREE.Mesh.call(this, geometry, materials); this.identityMatrix = new THREE.Matrix4(); this.bones = []; this.boneMatrices = []; ...}; SkinnedMesh.prototype = Object.create(THREE.Mesh.prototype);SkinnedMesh.prototype.constructor = SkinnedMesh; SkinnedMesh.prototype.update = function(camera) { ... THREE.Mesh.prototype.update.call(this);};

class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials); public identityMatrix = new THREE.Matrix4(); public bones = []; public boneMatrices = []; ... } update(camera) { ... super.update(); }}

▫ class が入るなら、残りの予約語系も使えるようになってほしい(というかそれがないとあんまり意味がなさそう)けど、それをやり始めると止まらなくなる予感 implements, private, public, interface, package, protected, static ES4ェ…

Page 49: JavaScript study for learners

Quasi Literals / Tagged Quasis• Quasi

▫ 【形】疑似の、類似の、外見上の、うわべだけの▫ 【副】外見上、ある程度

• ES Wiki の記述▫ http://wiki.ecmascript.org/doku.php?id=harmony:quasis

Page 50: JavaScript study for learners

JavaScriptを継続して学ぶ

Page 51: JavaScript study for learners

Next Step > Web ページを読む• 既にたくさんいいドキュメントがあるので読みましょう

▫ 入門 JavaScript 「再」入門 - JavaScript | MDN

https://developer.mozilla.org/ja/docs/JavaScript/A_re-introduction_to_JavaScript 型とかオブジェクトとか基本的なことを理解する - あと味

http://taiju.hatenablog.com/entry/20110416/1302939377▫ ベストプラクティス

JavaScriptベストプラクティス30選 -jsEdu | Web scratch http://efcl.info/2010/1015/res1985/

JavaScript Garden http://bonsaiden.github.com/JavaScript-Garden/ja/

コーディング規約 Google

▫ http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml▫ 和訳 : http://cou929.nu/data/google_javascript_style_guide/

Mozilla▫ https://developer.mozilla.org/ja/docs/JavaScript_style_guide

jQuery▫ http://docs.jquery.com/JQuery_Core_Style_Guidelines

▫ 理解度を測る JavaScriptの業務スキルレベル 判別表 (5段階) - 主に言語とシステム開発に関して

http://d.hatena.ne.jp/language_and_engineering/20100111/p1 JavaScriptで,オブジェクトやクラスの初歩を理解しているか,実力を確かめるための7つの質問 (サンプル

コード付き) - 主に言語とシステム開発に関して http://d.hatena.ne.jp/language_and_engineering/20100921/p1

Page 52: JavaScript study for learners

Next Step > 本を読む• 本を読む

▫ JavaScript第 6版 ( 通称 "サイ本 ") http://www.amazon.co.jp/dp/4873115736

▫ ステートフル JavaScript http://www.amazon.co.jp/dp/487311554X

▫ パーフェクト JavaScript http://www.amazon.co.jp/dp/477414813X

▫ JavaScript パターン http://www.amazon.co.jp/dp/4873114888

▫ JavaScript: The Good Parts http://www.amazon.co.jp/dp/4873113911

Page 53: JavaScript study for learners

Next Step > JS 関係の情報を追う•僕がよく見ている JS絡みの人たち

▫https://twitter.com/rika_t/js•JavaScript 関連のニュース

▫JSer.info : http://jser.info/ 世界中の JS 関連ニュースが紹介される

Page 54: JavaScript study for learners

Next Step > 勉強会に出る•ECMAScript勉強会

▫http://atnd.org/events/25793▫http://atnd.org/events/30676

Page 55: JavaScript study for learners

Thank you!