Download - JavaScript study for learners
JavaScript Study for learners高橋 健太@rika_t / rika-t
Who am I?
•rika-t▫Twitter: @rika_t▫ドワンなんとかという会社でニコニなんとかを
作っています▫それより以前は IBM で Dojo Toolkit を使った簡
単なお仕事をしていました▫JavaScript 歴は 3 年くらい ?
最近はほとんど書いてない
What’s this presentation?
•対象▫JavaScript で何となく動くものを作ったことが
ある、位の人•目的
▫もう少し JavaScript のことを深く理解できるようになる
▫他の人のコードでも、一人で原因調査ができる&解決できるようになる
Agenda
•JavaScript 仕様の話•JavaScript の用語•JavaScript の特徴
▫レキシカルスコープ▫プロトタイプベースのオブジェクト指向言語
•JavaScript のデバッグ▫見通しの良いコードを書く▫開発者ツールの使い方
•将来の JavaScript•JavaScript を継続して学ぶ
JavaScript仕様の話
JavaScript の歴史• JS history : http://
www.slideshare.net/badatmath/js-shistory を読みましょう
• どうでもいいこと▫JavaScript は Java に似ているらしい
仕様書にそう書いてある どこが
▫"JavaScript" の S は大文字 JAVAScript とか Javascript とか Java Script とかダメです
JavaScript の仕様はどこで決まる?• 困ったら仕様を調べるのがイケてるエンジニア
▫ JavaScript には ECMAScript というベースとなる言語仕様がある これを決めているのが Ecma International という団体
http://www.ecma-international.org/publications/standards/Ecma-262.htm
▫ ここに、 DOM ツリーの操作やブラウザー固有の仕様に伴うオブジェクトが追加される これを最終的に勧告するのが W3C
例えば、 localStorage の動きを調べたかったら、以下のページを見ることになる▫http://www.w3.org/TR/webstorage/
ブラウザーの仕様実装状況•で、それを実装するのが Google であったり、
Mozilla であったり、 Microsoft であったり▫実装状況に差異が生まれる
•調べる▫http://caniuse.com/
JavaScriptの用語
用語• オブジェクト
▫ 0 以上の属性を持つ、プロパティの集合• プロパティ
▫ 他のオブジェクト , プリミティブ値 , 関数の入れ物 Object properties in JavaScript :
http://www.2ality.com/2012/10/javascript-properties.html?m=1• 属性
▫ 書き込み可能、列挙可能などのプロパティの振る舞いを規定するもの
• 関数▫ 呼び出し可能なオブジェクトの一種( Object 型)
• メソッド▫ オブジェクトのプロパティとして関連付けられている関数
JavaScriptの特徴レキシカルスコープ
変数のスコープ• 基本的に関数に入るとスコープが変わる
▫ ただし、そのスコープで存在しない変数にアクセスすると、 " 構文上の "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);
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");});
(function(){})();• スクリプト内に JavaScript をいきなり書き始めると、変数がグ
ローバル空間に定義される▫ グローバル空間の汚染を防ぐために、最初に関数ブロックを作る
このブロックの中で、グローバル空間からアクセスできるようにしたいものだけ、指定してエクスポートする
(function(){ var myPrivateVariable = 42; var hogehoge = { myFunction: function() { … }; window.hogehoge = hogehoge;})();
var myPrivateVariable = 42;var hogehoge = { myFunction: function() { …};
(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);
グローバル領域の汚染• 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); };
JavaScriptの特徴プロトタイプベースのオブジェクト指向言語
JavaScript の型•5つのプリミティブと object
▫number, string, boolean, null, undefined ただし、 typeof null; //”object” …
▫object•組み込みオブジェクト
▫18 個(後述) 逆に言えば、これ以外のオブジェクトがコードに出
てきたらブラウザー側のオブジェクトと思えば良い
組み込み型と組み込みオブジェクトObject
Object
Undefined
Null
Boolean
Number
String
: 組み込み型
Function
Array
String
Boolean
NumberMath
Date
RegExp
JSON
: 組み込みオブジェクト
global Error
SyntaxError
ReferenceError
EvalError
RangeError
TypeError
URIError
プリミティブと 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"
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
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 の再実装みたいなものを作るのは推奨しないです
Function オブジェクト• 関数はオブジェクト
▫ 代入したり、受け渡ししたり、インスタンス作ったり
• 関数オブジェクトは new できる▫ prototype
new されたインスタンスが持つプロパティ、メソッドを定義したもの 普通のオブジェクトにはない
▫ constructor new される時に呼ばれるメソッド 関数オブジェクト定義時に作成される
普通のオブジェクトにはない▫ this
実行しているコンテキストを示すもの(後述) オブジェクトのメソッドの場合、大抵はオブジェクトのインスタンスを指す
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
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
プロトタイプチェーン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"
プロトタイプチェーン
MylistItem
mylistItem
prototype
getTitle
getTitle
MylistGroup
prototype
getTitle
mylistGroup
getTitle
MylistVideo
sm9
prototype
getTitle
getUserId
getUserId
getTitle
ダメな例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 というメソッドが増えてしまう ( 参照なので )
ES 仕様書での prototype の説明CF コンストラクタ
prototype と P1, P2 というプロパティを持っている
CF コンストラクタのプロトタイプCFP1 というプロパティを持っている
CF コンストラクタから生成されたオブジェクト達
q1, q2 というプロパティを持っている
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"
JavaScriptのデバッグ見通しの良いコードを書く
見通しの悪いコード• JavaScript を普通に書いていくと、イケてないコードになりがち
▫ Why? 言語仕様上、どこからでもグローバルオブジェクトにアクセスできる 同様に、 HTML がグローバルオブジェクトのように利用できてしまう HTML や CSS と連携するため、元々書きたいコードに外部リソースへの依存が激しい
• 以下のようなルールを定義する▫ コーディング規約を守る▫ クラス化( Widget化)する▫ JsDoc を書く▫ 依存を定数化する or 最初に解決する▫ グローバルへの依存を減らす
コーディング規約•色々参考になるものがあるので、何か決める
▫ 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/
クラス化( 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() {};
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 書き換え処理)};
依存を定数化する 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; }};
グローバルへの依存を減らす • 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>
これを続けていくと…•見やすく&管理しやすくなったのは良いが…
▫JsDoc でファイルサイズが増える… JavaScriptビルドへの道
Closure Compiler
▫オブジェクトの初期化コストが高い…&ページ表示時の処理が重い… JavaScript パターンへの道
初期化遅延などのパターン適用 デバッグツールへの道
重い処理を見つけてボトルネック解消
JavaScriptのデバッグ開発者ツールの使い方
Internet Explorer
•開発者ツール▫F12 で起動する
IE8 から使える IE7 以前は…外部の開発ツールを入れるしかない
•機能▫JavaScript コンソール▫JavaScript ブレークポイントの設定▫コールスタックを見る▫プロファイルが取れる
Firefox
•Firebug を入れる▫F12 で起動する
•機能▫JavaScript コンソール▫JavaScript ブレークポイントの設定▫DOM ブレークポイントの設定▫コールスタックを見る▫プロファイルが取れる
Chrome• Developer Tools
▫F12 で起動する
•機能▫ JavaScript コンソール▫ JavaScript ブレークポイントの設定▫DOM ブレークポイントの設定▫ コールスタックを見る▫ プロファイルが取れる▫ パフォーマンス等の問題チェック
昔は Firebug もできたが…
将来の JavaScript
将来ではない、 "今 " のJavaScript• こんなことが今既にできる
▫ ECMAScript 5 : http://www.slideshare.net/ferrantes/ecmascript-5-10575898
• もう少しまともな継承っぽいことができる▫ Object.create
•安全なオブジェクトが作れる▫ Object.prototype.seal, defineProperty, freeze…
• プロパティの読み書き時にフック出来る▫ Getter/Setter
• JSON オブジェクト!▫ JSON
将来の 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
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))},
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; }
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ェ…
Quasi Literals / Tagged Quasis• Quasi
▫ 【形】疑似の、類似の、外見上の、うわべだけの▫ 【副】外見上、ある程度
• ES Wiki の記述▫ http://wiki.ecmascript.org/doku.php?id=harmony:quasis
JavaScriptを継続して学ぶ
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
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
Next Step > JS 関係の情報を追う•僕がよく見ている JS絡みの人たち
▫https://twitter.com/rika_t/js•JavaScript 関連のニュース
▫JSer.info : http://jser.info/ 世界中の JS 関連ニュースが紹介される
Next Step > 勉強会に出る•ECMAScript勉強会
▫http://atnd.org/events/25793▫http://atnd.org/events/30676
Thank you!