89

Click here to load reader

Clean Javascript

Embed Size (px)

DESCRIPTION

SaCSS vol.29( http://atnd.org/events/20768 ) での発表資料です。

Citation preview

Page 1: Clean Javascript

Clean JavaScript

佐藤 竜之介(Ryunosuke SATO)Sapporo.js

SaCSS vol.29 - 2011.11.26

http://www.flickr.com/photos/hoshikowolrd/5151171470/

Page 2: Clean Javascript

提供

Sapporo.js

Community for people who like JavaScript.

Page 3: Clean Javascript

Sapporo.js

http://sapporojs.org

Page 4: Clean Javascript

Sapporo.js

Now learning

Page 5: Clean Javascript
Page 6: Clean Javascript

I’m a programmer

Page 7: Clean Javascript

佐藤竜之介

@tricknotes

Page 8: Clean Javascript

よろしくお願いします

Page 9: Clean Javascript

Clean JavaScript

佐藤 竜之介(Ryunosuke SATO)Sapporo.js

SaCSS vol.29 - 2011.11.26

http://www.flickr.com/photos/hoshikowolrd/5151171470/

Page 10: Clean Javascript

今日のテーマ

Page 11: Clean Javascript

良いコード

Page 12: Clean Javascript

とは?

Page 13: Clean Javascript

コードだけが良いのではなく、良い振る舞いを行えるのが良いコードである

Page 14: Clean Javascript

よく出てくる問題に対応するための法則性

良い習慣

Page 15: Clean Javascript

どういうときにどうするのが良いコードにつながっていくか

Page 16: Clean Javascript

対象者

Page 17: Clean Javascript

普段 JavaScript を書いているひと良いコードを書きたいと思っているひと良いコードに興味があるひと

Page 18: Clean Javascript

普段 JavaScript を書いているひと良いコードを書きたいと思っているひと良いコードに興味があるひと

Page 19: Clean Javascript

about JavaScript

Page 20: Clean Javascript

Works in everywhere!

Page 21: Clean Javascript

interesting!

http://www.flickr.com/photos/peco-sunnyday/2752403413/

Page 22: Clean Javascript

I like

Page 23: Clean Javascript

JavaScript is most popular!

https://github.com/languages

Page 24: Clean Javascript

but...

Page 25: Clean Javascript

difficult

http://www.flickr.com/photos/maynard/6105929170/

Page 26: Clean Javascript

Now training...

Page 27: Clean Javascript

??How to face the difficulty

Page 28: Clean Javascript

良い習慣

Page 29: Clean Javascript

装備を整えて、挑む準備が必要

Page 30: Clean Javascript

Oops... :(

http://www.flickr.com/photos/cholovak/4500742434/

Page 31: Clean Javascript

some ideas

Page 32: Clean Javascript

DOMwith

Page 33: Clean Javascript

point

Page 34: Clean Javascript

HTML と JavaScript をいかに分けるか

Page 35: Clean Javascript

画面 ロジック

HTML JavaScript

Page 36: Clean Javascript

画面 ロジック

HTML JavaScript

here!

Page 37: Clean Javascript

practice

Page 38: Clean Javascript

problem

どこで JavaScript のイベントが設定されているのかわかりづらい

イベントは外側から - Attach events from outer -

Page 39: Clean Javascript

solution

HTML の 属性として設定するのをやめてJavaScript からのみイベントを設定する

イベントは外側から - Attach events from outer -

Page 40: Clean Javascript

<a onclick=”showMessage()”>Click me</a>

code smell

イベントは外側から - Attach events from outer -

Page 41: Clean Javascript

<a id=”showMessage”>Click me</a>

var a = document.getElementById(‘showMessage’);a.addEventListener(‘click’, showMessage);

イベントは外側から - Attach events from outer -

improvement

Page 42: Clean Javascript

HTML の idやclassに依存したコードになっていて、画面を修正するたびに動かなくなる部分がある

セレクタからの分離 - Separate from Selector -

problem

Page 43: Clean Javascript

HTML の id や class に極力依存しない設計にする

セレクタからの分離 - Separate from Selector -

solution

Page 44: Clean Javascript

function showPhoto(photoId) { var photo = document.getElementById(‘photo-’ + photoId); // do ...}

セレクタからの分離 - Separate from Selector -

Page 45: Clean Javascript

function showPhoto(photoId) { var photo = document.getElementById(‘photo-’ + photoId); // do ...}

セレクタからの分離 - Separate from Selector -

code smell

Page 46: Clean Javascript

var showPhoto = function(element) { // do ...}

var photo = document.getElementById(‘photo-12’);showPhoto(photo);

セレクタからの分離 - Separate from Selector -

improvement

Page 47: Clean Javascript

モジュール化 - modulized functions -

ファイルの数が多く、探したい関数がなかなか見つけられない

problem

Page 48: Clean Javascript

ファイル名を表すモジュールにまとめる

モジュール化 - modulized functions -

solution

Page 49: Clean Javascript

// users.jsvar showUserName = function() { // do somethig}

var showUserAge = function(){ // do somethig}

var validateUserForm = function() { // do somethig}

モジュール化 - modulized functions -

code smell

Page 50: Clean Javascript

// users.jsvar showUserName = function() { // do somethig}

var showUserAge = function(){ // do somethig}

var validateUserForm = function() { // do somethig}

global

モジュール化 - modulized functions -

Page 51: Clean Javascript

// users.jsvar users = {};

users.showName = function () { // do somethig}

users.showAge = function (){ // do somethig}

users.validateForm = function () { // do somethig}

global

モジュール化 - modulized functions -

Page 52: Clean Javascript

// users.js(function(global) { global.users = {};

users.showName = function () { // do somethig }

// ...)(this);

global

モジュール化 - modulized functions -

improvement

Page 53: Clean Javascript

JavaScript のイベントで、DOM の元々の動作を上書きしたい

振る舞いの上書き - overwrite behavior -

problem

Page 54: Clean Javascript

DOM が元々持っている属性を利用する足りなければ、DOM に属性を追加する

振る舞いの上書き - overwrite behavior -

solution

Page 55: Clean Javascript

振る舞いの上書き - overwrite behavior -

// using jQuery

$(‘a#showDialog’).click(function() { $.ajax(‘/about’, { success: function(html) { // do something... } });});

code smell

Page 56: Clean Javascript

// using jQuery

$(‘a#showDialog’).click(function() { $.ajax($(this).attr(‘href’), { success: function(html) { // do something... } });});

振る舞いの上書き - overwrite behavior -

improvement

Page 57: Clean Javascript

コールバック関数を深く設定してしまっていて、一つの処理が大きくなってしまっている。同じ変数があちこちにちらばっていて、

処理をわけるのが困難である

浅いスコープ - shallow scope -

problem

Page 58: Clean Javascript

変数が、2段以上の function のスコープをまたがないようにする

→引数や、 this で渡すようにする

浅いスコープ - shallow scope -

solution

Page 59: Clean Javascript

// 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 -

Page 60: Clean Javascript

// 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

Page 61: Clean Javascript

// 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

Page 62: Clean Javascript

DOMをモデルオブジェクトへ - DOM to model -

DOM にいくつかのフラグを設定したり、まとめて操作を行いたい部分がある

problem

Page 63: Clean Javascript

DOM をラップするオブジェクトを作成し、一連の処理をメソッドにする

DOMをモデルオブジェクトへ - DOM to model -

solution

Page 64: Clean Javascript

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

Page 65: Clean Javascript

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 -

Page 66: Clean Javascript

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!’; }};

Page 67: Clean Javascript

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!’; }};

Page 68: Clean Javascript

画面とロジックの分離 - separate logic with view -

ロジックと画面への情報更新が強く結びついていて、どちらか片方だけの変更が困難である

problem

Page 69: Clean Javascript

ロジックと画面への情報更新を分割し、イベントを通じて変更を通知する

solution画面とロジックの分離 - separate logic with view -

Page 70: Clean Javascript

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 -

Page 71: Clean Javascript

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

Page 72: Clean Javascript

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 -

Page 73: Clean Javascript

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

Page 74: Clean Javascript

A tiny fraction

Page 75: Clean Javascript

独立しているわけではない相互に関連している

Page 76: Clean Javascript

others...

Page 77: Clean Javascript

Pure JavaScript

Page 78: Clean Javascript
Page 79: Clean Javascript

Application

Page 82: Clean Javascript

http://www.sproutcore.com/

Page 83: Clean Javascript

良いコードを書くためには

Page 84: Clean Javascript

http://www.flickr.com/photos/bonguri/4610536789/

interest

Page 85: Clean Javascript

reading

Page 86: Clean Javascript

Good text

Page 87: Clean Javascript

writing

Page 88: Clean Javascript
Page 89: Clean Javascript

http://www.flickr.com/photos/tsukacyi/4848685561/

Shall we learn about

Clean JavaScript?