Click here to load reader
Upload
ryunosuke-sato
View
22.592
Download
11
Tags:
Embed Size (px)
DESCRIPTION
SaCSS vol.29( http://atnd.org/events/20768 ) での発表資料です。
Citation preview
Clean JavaScript
佐藤 竜之介(Ryunosuke SATO)Sapporo.js
SaCSS vol.29 - 2011.11.26
http://www.flickr.com/photos/hoshikowolrd/5151171470/
提供
Sapporo.js
Community for people who like JavaScript.
Sapporo.js
Now learning
I’m a programmer
佐藤竜之介
@tricknotes
よろしくお願いします
Clean JavaScript
佐藤 竜之介(Ryunosuke SATO)Sapporo.js
SaCSS vol.29 - 2011.11.26
http://www.flickr.com/photos/hoshikowolrd/5151171470/
今日のテーマ
良いコード
とは?
コードだけが良いのではなく、良い振る舞いを行えるのが良いコードである
よく出てくる問題に対応するための法則性
良い習慣
どういうときにどうするのが良いコードにつながっていくか
対象者
普段 JavaScript を書いているひと良いコードを書きたいと思っているひと良いコードに興味があるひと
普段 JavaScript を書いているひと良いコードを書きたいと思っているひと良いコードに興味があるひと
about JavaScript
Works in everywhere!
interesting!
http://www.flickr.com/photos/peco-sunnyday/2752403413/
I like
JavaScript is most popular!
https://github.com/languages
but...
difficult
http://www.flickr.com/photos/maynard/6105929170/
Now training...
??How to face the difficulty
良い習慣
装備を整えて、挑む準備が必要
Oops... :(
http://www.flickr.com/photos/cholovak/4500742434/
some ideas
DOMwith
point
HTML と JavaScript をいかに分けるか
画面 ロジック
HTML JavaScript
画面 ロジック
HTML JavaScript
here!
practice
problem
どこで JavaScript のイベントが設定されているのかわかりづらい
イベントは外側から - Attach events from outer -
solution
HTML の 属性として設定するのをやめてJavaScript からのみイベントを設定する
イベントは外側から - Attach events from outer -
<a onclick=”showMessage()”>Click me</a>
code smell
イベントは外側から - Attach events from outer -
<a id=”showMessage”>Click me</a>
var a = document.getElementById(‘showMessage’);a.addEventListener(‘click’, showMessage);
イベントは外側から - Attach events from outer -
improvement
HTML の idやclassに依存したコードになっていて、画面を修正するたびに動かなくなる部分がある
セレクタからの分離 - Separate from Selector -
problem
HTML の id や class に極力依存しない設計にする
セレクタからの分離 - Separate from Selector -
solution
function showPhoto(photoId) { var photo = document.getElementById(‘photo-’ + photoId); // do ...}
セレクタからの分離 - Separate from Selector -
function showPhoto(photoId) { var photo = document.getElementById(‘photo-’ + photoId); // do ...}
セレクタからの分離 - Separate from Selector -
code smell
var showPhoto = function(element) { // do ...}
var photo = document.getElementById(‘photo-12’);showPhoto(photo);
セレクタからの分離 - Separate from Selector -
improvement
モジュール化 - modulized functions -
ファイルの数が多く、探したい関数がなかなか見つけられない
problem
ファイル名を表すモジュールにまとめる
モジュール化 - modulized functions -
solution
// users.jsvar showUserName = function() { // do somethig}
var showUserAge = function(){ // do somethig}
var validateUserForm = function() { // do somethig}
モジュール化 - modulized functions -
code smell
// users.jsvar showUserName = function() { // do somethig}
var showUserAge = function(){ // do somethig}
var validateUserForm = function() { // do somethig}
global
モジュール化 - modulized functions -
// users.jsvar users = {};
users.showName = function () { // do somethig}
users.showAge = function (){ // do somethig}
users.validateForm = function () { // do somethig}
global
モジュール化 - modulized functions -
// users.js(function(global) { global.users = {};
users.showName = function () { // do somethig }
// ...)(this);
global
モジュール化 - modulized functions -
improvement
JavaScript のイベントで、DOM の元々の動作を上書きしたい
振る舞いの上書き - overwrite behavior -
problem
DOM が元々持っている属性を利用する足りなければ、DOM に属性を追加する
振る舞いの上書き - overwrite behavior -
solution
振る舞いの上書き - overwrite behavior -
// using jQuery
$(‘a#showDialog’).click(function() { $.ajax(‘/about’, { success: function(html) { // do something... } });});
code smell
// using jQuery
$(‘a#showDialog’).click(function() { $.ajax($(this).attr(‘href’), { success: function(html) { // do something... } });});
振る舞いの上書き - overwrite behavior -
improvement
コールバック関数を深く設定してしまっていて、一つの処理が大きくなってしまっている。同じ変数があちこちにちらばっていて、
処理をわけるのが困難である
浅いスコープ - shallow scope -
problem
変数が、2段以上の function のスコープをまたがないようにする
→引数や、 this で渡すようにする
浅いスコープ - shallow scope -
solution
// using jQuery
var $elements = $(‘a#remote’);$elements.click(function() { var url = $(this).attr(‘href’); $.ajax(url, { success: function(html) { var text = $(html).text(); $element.text(text); } }); return false;});
浅いスコープ - shallow scope -
// using jQuery
var $elements = $(‘a#remote’);$elements.click(function() { var url = $(this).attr(‘href’); $.ajax(url, { success: function(html) { var text = $(html).text(); $element.text(text); } }); return false;});
deep
浅いスコープ - shallow scope -
code smell
// using jQuery
var $elements = $(‘a#remote’);$elements.click(function() { var url = $(this).attr(‘href’); $.ajax(url, { success: function(html) { var text = $(html).text(); $(this).text(text); }, context: this }); return false;});
浅いスコープ - shallow scope -
improvement
DOMをモデルオブジェクトへ - DOM to model -
DOM にいくつかのフラグを設定したり、まとめて操作を行いたい部分がある
problem
DOM をラップするオブジェクトを作成し、一連の処理をメソッドにする
DOMをモデルオブジェクトへ - DOM to model -
solution
var element = document.getElementById(‘message’);element.addEventListener(‘click’, function() { // this == element if (this.getAttribute(‘data-is-checked’)) { this.setAttribute(‘data-is-checked’, true); this.innerText = ‘clicked!’; }});
DOMをモデルオブジェクトへ - DOM to model -
code smell
var domToModel = function(element, Model) { var method, name, object, parent, proto; model = Object.create(element); proto = Model.prototype; for (name in proto) { method = proto[name]; model[name] = method; } Model.apply(model); return model;};
var CustomElement = function() {};CustomElement.prototype.showText = function() { if (!this.getAttribute(‘data-is-checked’)) { this.setAttribute(‘data-is-checked’, true); this.innerText = ‘clicked!’; }};
var element = document.getElementById(‘message’);var model = domToModel(element, CustomElement);model.addEventListener(‘click’, function() { model.showText();});
DOMをモデルオブジェクトへ - DOM to model -
var element = document.getElementById(‘message’);var model = domToModel(element, CustomElement);model.addEventListener(‘click’, function() { model.showText();});
DOMをモデルオブジェクトへ - DOM to model -
var domToModel = function(element, Model) { var method, name, object, parent, proto; model = Object.create(element); proto = Model.prototype; for (name in proto) { method = proto[name]; model[name] = method; } Model.apply(model); return model;};
var CustomElement = function() {};CustomElement.prototype.showText = function() { if (!this.getAttribute(‘data-is-checked’)) { this.setAttribute(‘data-is-checked’, true); this.innerText = ‘clicked!’; }};
var element = document.getElementById(‘message’);var model = domToModel(element, CustomElement);model.addEventListener(‘click’, function() { model.showText();});
DOMをモデルオブジェクトへ - DOM to model -
improvement
var domToModel = function(element, Model) { var method, name, object, parent, proto; model = Object.create(element); proto = Model.prototype; for (name in proto) { method = proto[name]; model[name] = method; } Model.apply(model); return model;};
var CustomElement = function() {};CustomElement.prototype.showText = function() { if (!this.getAttribute(‘data-is-checked’)) { this.setAttribute(‘data-is-checked’, true); this.innerText = ‘clicked!’; }};
画面とロジックの分離 - separate logic with view -
ロジックと画面への情報更新が強く結びついていて、どちらか片方だけの変更が困難である
problem
ロジックと画面への情報更新を分割し、イベントを通じて変更を通知する
solution画面とロジックの分離 - separate logic with view -
function createTimer() { var timerId; var startTimer = function(millisec) { timerId = setTimeout(function() { $(‘.timeout’).text(‘finished’); }, millisec); } var stopTimer = function() { clearTimeout(timerId); } return { startTimer: startTimer, stopTimer: stopTimer }}
画面とロジックの分離 - separate logic with view -
function createTimer() { var timerId; var startTimer = function(millisec) { timerId = setTimeout(function() { $(‘.timeout’).text(‘finished’); }, millisec); } var stopTimer = function() { clearTimeout(timerId); } return { startTimer: startTimer, stopTimer: stopTimer }}
画面とロジックの分離 - separate logic with view -
view
code smell
function Timer(millisec) { this. millisec = millisec; this.callbacks = []; this.timerId = null;}
Timer.prototype.afterFinish = function(callback) { return this.callbacks.push(callback);};
Timer.prototype.start = function() { var callbacks = this.callbacks; this.timerId = setTimeout(function() { var callback, i, length; for (i = 0, length = callbacks.length; i < length; i++) { callback = callbacks[i]; callback(); } }, this. millisec);};
Timer.prototype.stop = function() { clearTimeout(this.timerId);}
var timer = new Timer(1000);timer.afterFinish(function() { $('.timer .message').text('Finished!!');});timer.start();
画面とロジックの分離 - separate logic with view -
var timer = new Timer(1000);timer.afterFinish(function() { $('.timer .message').text('Finished!!');});timer.start();
model
view
function Timer(millisec) { this. millisec = millisec; this.callbacks = []; this.timerId = null;}
Timer.prototype.afterFinish = function(callback) { return this.callbacks.push(callback);};
Timer.prototype.start = function() { var callbacks = this.callbacks; this.timerId = setTimeout(function() { var callback, i, length; for (i = 0, length = callbacks.length; i < length; i++) { callback = callbacks[i]; callback(); } }, this. millisec);};
Timer.prototype.stop = function() { clearTimeout(this.timerId);}
画面とロジックの分離 - separate logic with view -
improvement
A tiny fraction
独立しているわけではない相互に関連している
others...
Pure JavaScript
Application
https://gist.github.com/1362110元記事: http://blog.nodejitsu.com/scaling-isomorphic-javascript-code
http://documentcloud.github.com/backbone/
良いコードを書くためには
http://www.flickr.com/photos/bonguri/4610536789/
interest
reading
Good text
writing
http://www.flickr.com/photos/tsukacyi/4848685561/
Shall we learn about
Clean JavaScript?