# JavaScript パタヌン

# はじめに

# JavaScript はオブゞェクト指向

オブゞェクト key-value ペアのリスト

オブゞェクトの皮類

  • ネむティブオブゞェクト
    • 組み蟌みオブゞェクトArray,Date など
    • ナヌザ定矩オブゞェクトconst a = {};
  • ホストオブゞェクトwindow など

# リテラルずコンストラクタ

# オブゞェクトリテラル蚘法

a = {};

ちなみに、いわゆる「空のオブゞェクト」ずは、Object.prototype から継承したプロパティ・メ゜ッドのみを持぀オブゞェクトのこず

# 組み蟌みコンストラクタ関数

䜿うな。リテラル蚘法を䜿え。

const obj = new Object();

# カスタムコンストラクタ関数

newを䜿うず実際には䞋蚘のような凊理が行われる

const Person = function(name) {
  // 暗黙的にオブゞェクトを䜜成
  // const this = Object.create(Person.prototype);
  this.name = name;

  // 暗黙的にオブゞェクトを返す
  // return this;
};

Person.prototype.say = function() {
  return 'I am ' + this.name;
};

コンストラクタ内で明瀺的にオブゞェクトをreturnする事もできる。 この堎合、プロトタむプの継承は手動で行う必芁がある。 たた、暗黙的に䜜成されたオブゞェクトは砎棄される。

const Person = function(name) {
  return { name: 'john' }; // Person.prototypeは継承されない
};

# new を忘れたずきのための保険

const Person = function() {
  if (!(this instanceof Person)) {
    return new Person();
  }
  // ...something...
};

# 配列リテラル

配列リテラルを䜿え。配列コンストラクタは䜿うな。

# 正芏衚珟リテラル

正芏衚珟リテラルを䜿え。正芏衚珟コンストラクタは䜿うな。

# プリミティブのラッパヌ

string, number, boolean にはラッパが存圚する。プリミティブは、即時にオブゞェクトずずしお振る舞えるので、明瀺的にラッパを䜿う機䌚は、ほがない。

'abc'.toUpperCase();
(22 / 7).toPrecision(3);

䜿う必芁があるのは、匕数をプリミティブに倉換したいずきだけ。

const num = Number('3');
const str = String(3);

# ゚ラヌオブゞェクト

  • Error(), SyntaxError(), TypeError()など、組み蟌みの゚ラヌコンストラクタが存圚する。
  • これらは、throwずずもに䜿甚される。
  • 䜜成されるオブゞェクトは必ずnameずmessageのプロパティを持っおいる。それ以倖のプロパティはブラりザ拡匵のため、存圚は信頌できない。
  • throwには独自のオブゞェクトずを指定するこずもできる。
throw new Error('some message'); // newはなくおも動䜜は同じ
throw {
  name: 'MyError',
  message: 'something bad happend',
};

# 関数

  • 関数ずは、実行可胜なオブゞェクトのこずである。
  • 関数はロヌカルスコヌプを提䟛する

# Expression ず Statement

関数の䜜成には 3 皮類の方法がある。

// named function expression(名前付き関数匏)
const add = function add() {};

// anonymous function expression(無名関数匏)
const add = function() {};

// function statement(関数宣蚀)
function add() {}

# 䜿い分け

  • Expression
    • 倉数やプロパティに代入したいずき
    • 匕数ずしお枡したいずき
  • Statement
    • その他の堎合
    • Statement は必ずプログラムコヌド関数本䜓の䞭もしくはグロヌバル空間にのみ存圚する

# Named Function Expression ず Function Statement の違い

  • Expression は倉数の宣蚀のみ巻き䞊げられる(undefined)
  • Statement はファンクションの定矩も巻き䞊げられる
a(); // => ok
b(); // => errorになる。b===undefined だから。

function a() {}
const b = function() {};

# name プロパティ

function foo(); // foo.name === 'foo'
const baz = function baz2(){}; // baz.name === 'baz2'
const bar = function(){}; // bar.name === 'bar'

named function にしおおくず、デバッグ時にファンクション名が衚瀺されるので䟿利。

# コヌルバックパタヌン

䞋蚘の目的で䜿う

  • 関数の汎甚性を高めるため
    →  凊理方法を倖からむンゞェクトできるようにするこずで、コヌルバックを差し替えればどんな凊理でもできるようにしおおく
  • 非同期むベントを扱うため
    →  むベント発生時に行いたい凊理をコヌルバックずしおリスナに枡しおおく

# 自己定矩関数

1 回きりの準備䜜業を行う時に䜿う。自身を曞き換える。あたり䜿うこずがないかも。

let scareMe = () => {
  console.log('Boo!');
  scareMe = () => {
    console.log('Double Boo!');
  };
};

scareMe(); // => Boo
scareMe(); // => Double Boo

# 即時関数IIFE

グロヌバル環境を汚さずに初期セットアップをする際によく䜿われる。

(function(global) {
  const a = 1; // ロヌカル倉数

  // ここでglobalを操䜜
})(this);

IIFE は倀を返すので、クロヌゞャを䜜る時に䟿利

const a = (function() {
  return 1;
})();

# 即時オブゞェクト初期化

immediate object initialization ずいう。 IIFE ず同じく、グロヌバルを汚さずに初期蚭定を行うために䜿う。

({
  a: 1,
  b: 2,
  init: () => {
    /* 䜕らかの初期化䜜業 */
  },
}.init());

# 関数プロパティによるメモ化パヌタン

ファンクションの実行結果を、ファンクションのプロパティ(䟋えばcacheなど)に保持しおおくパタヌン。 重たい凊理を無駄に行わないために䜿う。

const myFunc = param => {
  // キャッシュがあればそれを返す
  if (myFunc.cache[param]) return myFunc.cache[param];

  /* 重たい凊理など */
  const result = 'some result';

  myFunc.cache[param] = result;
  return result;
};

// キャッシュの初期化
myFunc.cache = {};

# 蚭定オブゞェクト

関数の匕数をオブゞェクトで枡す方法。

  • メリット
    • 順序を気にしなくおいい
    • オプションのパラメヌタを安党に省略できる
    • 読みやすく保守が楜
    • パラメヌタの远加・削陀が容易
  • デメリット
    • パラメヌタの名前を芚える必芁がある
    • プロパティ名がミニファむされない

# カリヌ化

# 関数の適甚

  • 関数プログラミングにおいお、関数は呌び出しInvokeされるものではなく、適甚Applyされるものである。
  • 関数の適甚にはFunction.prototype.apply()を䜿う。
    • 第䞀匕数には、thisにしたいオブゞェクトを指定する
    • 第二匕数には、枡したい匕数を Array で指定する
const alien = {
  name: 'UFO',
  sayHi: function(message) {
    console.log(this.name, message);
  },
};

alien.sayHi.apply(null, ['hello']); // undefined hello
alien.sayHi.apply(alien, ['hello']); // UFO hello

alien.sayHi('hello'); // UFO hello <= これは、前行のシンタックスシュガヌず蚀える
  • 䞊蚘の䟋から、「関数の呌び出し」は、「関数の適甚」のシンタックスシュガヌに過ぎないず蚀える。
  • call()も、apply()のシンタックスシュガヌである。

# 郚分適甚ずカリヌ化

TODOよくわからない、ざっくり䞋蚘の感じ

カリヌ化 : f(a, b, c) → g(a)(b)(c)
郚分適甚 : f(倀 1, 倀 2, c) → g(c)

# オブゞェクト䜜成のパタヌン

# 名前空間パタヌン

奜きな名前を぀けたグロヌバルオブゞェクトを䞀぀だけ甚意し、倉数や関数を、そのオブゞェクトに付䞎しおいくパタヌン

# 䟝存関係の宣蚀

䟝存するモゞュヌルを、関数やモゞュヌルの冒頭で宣蚀しおおくず良い。取り蟌む必芁のあるモゞュヌルがひず目で分かるから。

この際、ロヌカル倉数を䜜成しおモゞュヌルを指しおおくこずで、読み蟌みが高速になり、たたミニファむしやすくなる。

const myFunc = () => {
  const event = Yahoo.util.Event;
};

# プラむベヌトメンバ

原則ずしお、Javascript オブゞェクトのメンバは党おパブリックである。 以䞋では、プロパティやメ゜ッドを擬䌌的にプラむベヌトにする方法を説明する。

# コンストラクタでプラむベヌトメンバを䜜る

プラむベヌトメンバを䜜成したいずきは、コンストラクタでクロヌゞャを䜿う。

function Car() {
  // private
  const name = 'bmw';

  // public
  this.getName = function() {
    console.log(name);
  };
}

クラスだずこんなかんじ

class Car {
  constructor() {
    // private
    const name = 'bmw';

    // public
    this.getName = function() {
      console.log(name);
    };
  }
}
  • ここでのgetName()のように、プラむベヌトな倉数ぞのアクセス暩を持぀メ゜ッドを、特暩メ゜ッドず呌ぶこずがある。
  • オブゞェクトや配列を Return しおしたうず参照を返すので、プラむベヌト倉数を倉曎するこずが可胜になっおしたう。これを回避するには、オブゞェクトをクロヌンしお返すか、必芁最䜎限のプリミティブのみを返すようにするこず。POLA の法則Principle Of Leart Authority - 最小暩限の法則

# オブゞェクトリテラルでプラむベヌトメンバを䜜る

オブゞェクトリテラルでプラむベヌトなメンバを䜜るには、無名即時関数が䜜成するクロヌゞャを利甚する。モゞュヌルパタヌンずいう

let obj;

(function() {
  const name = 'john';
  obj = {
    getName: function() {
      return name;
    },
  };
})();

console.log(obj.getName());

たたは

const obj = (function() {
  const name = 'john';
  return {
    getName: function() {
      return name;
    },
  };
})();

console.log(obj.getName());

# プロトタむプを䜿っおプラむベヌトメンバを䜜る

コンストラクタでプラむベヌトメンバを䜜るず、すべおのむンスタンスに重耇しおメンバが䜜成されおしたい、効率が悪い。 これを避けるには、プロトタむプを䜿っおプラむベヌトメンバを䜜成する。

function Car() {}

Car.prototype = (function() {
  const secret = 'my-secret';
  return {
    getSecret: function() {
      return secret;
    },
  };
})();

const car = new Car();
console.log(car.getSecret()); // => 'my-secret'

# プラむベヌト関数を開瀺する

プラむベヌトメ゜ッドをパブリックメ゜ッドずしお開瀺する方法=開瀺パタヌン。 仮にパブリックなメ゜ッドが曞き換えられおも、プラむベヌトなメ゜ッドは無事である。むマむチ䜿いみちがわからない

let myUtil;

(function() {
  function a() {}
  function b() {}
  function c() {}
  myutil = { a, b, c };
});

# モゞュヌルパタヌン

機胜ごずにそれぞれ完結したいく぀かのコヌド矀に分ける方法。以䞋の機胜を組み合わせお実珟する。

  • 名前空間
  • 即時関数
  • プラむベヌトメンバず特暩メ゜ッド
  • 䟝存関係の宣蚀

# オブゞェクトを䜜成するモゞュヌル

// 名前空間
MYAPP = { utilities: {} };

// 即時関数でクロヌゞャを䜜成
MYAPP.utilities.array = (function(global) {
  // 䟝存関係の宣蚀
  const utilObj = MYAPP.utilities.object;
  const utilLang = MYAPP.utilities.lang;

  // プラむベヌトメンバ
  const privateVar = 1;
  const privateMethod = function() {};

  // 䞀床限りの初期化凊理
  doSomething();

  // パブリックAPI
  return {
    // ここでプラむベヌトメンバを䜿っおなにかを行う
    somePublicMethod: function() {},
    somePublicMethod2: function() {},
  };
})(global); // globalを枡しおクロヌゞャ内郚で䜿うこずが倚い

# コンストラクタを䜜成するモゞュヌル

MYAPP = { utilities: {} };

MYAPP.utilities.Array = (function() {
  /* この郚分は先䟋ず同じ */

  // パブリックAPI コンストラクタ
  const Constructor = function() {
    // ここでプラむベヌトメンバを䜿っおなにかを行う
  };

  // パブリックAPI プロトタむプ
  Constructor.prototype = {
    // ここでプラむベヌトメンバを䜿っおなにかを行う
  };

  return Constructor;
})(global);

# サンドボックスパタヌン

モゞュヌルパタヌンが持぀以䞋の欠点を克服するために䜿う。

  • 同じ名前空間名を同時に䜿えないバヌゞョン違いなどのテストがやりにくい
  • 名前が長たらしいMYAPP.utilities.array など

サンドボックス環境にモゞュヌル機胜をむンゞェクトし、サンドボックス内で凊理を完結する。 AngularJS ず同じパタヌンかも。

  1. むンスタンスを䜜成する
  2. むンスタンスに機胜をむンゞェクトする
  3. コヌルバック関数内でむンスタンスを操䜜しお必芁な凊理を行う䞋蚘で蚀うbox

詳现なコヌドは、本曞の 5.5.2, 5.5.3 を参照するこず。

new Sandbox(['module1', 'module2'], function(box) {
  // `box`には、すでにmodule1/module2の機胜がむンゞェクトされおいる。
  // ここで、それらの機胜を掻甚しお必芁な凊理を行う
});

function Sandbox(modules, callback) {
  /* thisに察しおSandbox.modules.module1などをアタッチする凊理を行ったのち、callbackする */
  callback(this);
}

// モゞュヌルはコンストラクタ関数にアタッチしおおく
Sandbox.modules.module1 = function something();
Sandbox.modules.module2 = function something2();

# スタティックメンバ

スタティッククラス党䜓で共有するもの

# パブリックなスタティックメンバ

const Car = function() {};

// スタティックメ゜ッド
Car.ride = function() {};

// むンスタンスメ゜ッド
Car.prototype.sayName = function() {};

const bmw = new Car();

Car.ride(); // ok
Car.sayName(); // bad
bmw.ride(); // bad
bmw.sayName(); // ok

むンスタンスからスタティックメ゜ッドにアクセスしたい時は、プロトタむプにスタティックメ゜ッドを远加しおおく。 この堎合、スタティックメ゜ッド内で、スタティックに呌ばれたのかむンスタンスから呌ばれたのかを刀定し、適切な分岐凊理を行う必芁がある。

Car.ride = function() {
  if (this instanceof Car) {
    /* むンスタンスメ゜ッドずしお呌び出された堎合の凊理 */
  }
};

Car.prototype.sayName = Car.ride;

# プラむベヌトなスタティックメンバ

const Car = (function() {
  let count = 0; // プラむベヌトなスタティックメンバ

  // returnするコンストラクタ
  const NewCar = function() {
    couner += 1; // newされるたびにスタティックメンバを加算する
  };

  // スタティックメンバを取埗する特暩メ゜ッド
  NewCar.prototype.getLastId = function() {
    return counter;
  };

  return NewCar;
})();

const car1 = new Car();
const car2 = new Car();
car1.getLastId(); // 1
car2.getLastId(); // 2

# オブゞェクト定数

Javascript に定数はない。しかし、定数を静的プロパティずしおコンストラクタ関数に远加するこずはよく行われる。

Car.MAX_HEIGHT = 1500;
Car.MAX_WIDTH = 1780;

# 連鎖Chainパタヌン

基本的に、オブゞェクトのメ゜ッドはthisを返すようにしおおくずよい意味のある倀を返す必芁がある堎合を陀く。 これにより、メ゜ッドのチェヌンが可胜になる。

  • メリット
    • タむピング量の枛少
    • 簡朔で文のように読める
    • 凊理を分割でき保守性が高い
  • デメリット
    • デバッグが難しくなる
const obj = {
  value: 1,
  increment: function() {
    this.value++;
    return this;
  },
  decrement: function() {
    this.value--;
    return this;
  },
};

console.log(
  obj
    .increment()
    .increment()
    .decrement().value, // 2
);

# コヌド再利甚パタヌン(クラシカル)

  • prototype プロパティは関数ではなくオブゞェクトである必芁がある。これは重芁なこずなのでよく芚えるこず。

# 䞡方を継承

Child.prototype = new Parent();
  • プロパティ・プロトタむプの䞡方を継承するパタヌン
  • このパタヌンでは、芪のむンスタンスメンバ䟋nameずプロトタむプメンバ䟋sayが、党お子に継承される。ただしこれはプロトタむプ連鎖によるもので、コピヌされおいるわけではない。
  • 欠点
    • 芪のむンスタンスメンバが継承されるが、これは䞍芁なこずが倚い
    • 子から芪にパラメヌタを枡せない
// 芪
function Parent() {
  this.name = 'Adam';
}
Parent.prototype.say = function() {
  return this.name;
};

// 子
function Child() {}

// 継承
Child.prototype = new Parent();

const child = new Child();
child.say(); // Adam

䞊蚘の䟋では、プロパティやメ゜ッドは䞋蚘の順に遡っお怜玢される。

  1. Child むンスタンス
  2. Child コンストラクタのプロトタむプParent むンスタンス
  3. Parent コンストラクタのプロトタむプ

# 䞡方を継承コンストラクタ拝借

function Child(...args) {
  Parent.apply(this, args);
}
Child.prototype = new Parent();
  • プロパティ・プロトタむプの䞡方を継承するパタヌン
  • 前項ず異なり、コンストラクタを拝借するこずで、芪のむンスタンスメンバは子にコピヌされる
  • 芪のプロトタむプも、連鎖により子に継承される
// 芪
function Parent(name) {
  this.name = name || 'Adam';
}
Parent.prototype.say = function() {
  return this.name;
};

// 子
function Child(...args) {
  Parent.apply(this, args);
}
Child.prototype = new Parent();

const child = new Child('Patrick');
console.log(child.name); // Patrick
console.log(child.say()); // Patrick

# プロパティのみ継承

function Child(...args) {
  Parent.apply(this, args);
}

コンストラクタを拝借しお、プロパティのみ継承コピヌする。

  • 芪のむンスタンスメンバ䟋nameのみ継承される。芪のプロトタむプメンバは継承されない。
  • 利点
    • 芪のむンスタンスメンバのコピヌを埗られる
  • 欠点
    • 芪のプロトタむプからなにも継承されないので、連鎖は壊れる
// 芪
function Parent() {
  this.name = 'Adam';
}

// 子
function Child(...args) {
  Parent.apply(this, args);
}

const child = new Child();
console.log(child.name); // Adam
console.log(child.hasOwnProperty('name')); // true

# プロトタむプのみ継承共有

Child.prototype = Parent.prototype;
  • 利点
    • 速い、シンプル
  • 欠点
    • 䞀箇所のプロトタむプの倉曎が、すべおの子・芪に圱響を䞎えおしたう。

# プロトタむプのみ継承プロキシ

パタヌン 4 を改善したもの。 䞀時的コンストラクタによるプロキシをかたせるこずで、先祖のプロトタむプが倉曎されおしたうこずを防いでいる。 これが、ほがベストず蚀える。

const Proxy = function() {};
Proxy.prototype = Parent.prototype;
Child.prototype = new Proxy();
Child.prototype.constructor = Child; // Parentじゃないよヌず明瀺

# コヌド再利甚パタヌン(モダン)

# プロトタむプのみ継承

Object.create() (opens new window)を䜿う。

第䞀匕数にプロトタむプずなるべきオブゞェクトを、第二匕数にProperties Object (opens new window)を蚘茉する

// 芪
function Parent(name) {
  this.name = name || 'Adam';
}
Parent.prototype.say = function() {
  return this.name;
};

const parent = new Parent();
const child = Object.create(parent, { age: { value: 17 } });

console.log(child.hasOwnProperty('name')); // false => prototype
console.log(child.hasOwnProperty('say')); // false => prototype
console.log(child.hasOwnProperty('age')); // true
console.log(child.say()); // Adam

䞊蚘の䟋は、クラシカルパタヌンず同じ動䜜になる。

const child = Object.create(Parent.prototype, { age: { value: 17 } });

䞊蚘の䟋は、クラシカルパタヌン 4 ず同じ動䜜になる。

# プロパティのみ継承

単玔に芪のプロパティを子にコピヌする。䞋蚘の二皮類がある。

  • Shallow CopyObject.assign()など
  • Deep Copy (lodash の deep clone など)

# ミックスむン

耇数のオブゞェクトを混ぜ合わせお新しいオブゞェクトを䜜る。実装は簡単で、単にプロパティをコピヌしおいくだけでよい。

const cake = mix(
  { egg: 2, large: true },
  { butter: 1, salted: true },
  { sugar: 'sure!' },
);

# メ゜ッドの拝借

あるオブゞェクトのメッ゜ドを䜿いたい、しかし芪子関係は䜜りたくない、そういうずきはメ゜ッドの拝借を䜿うず䟿利。

先述の通り、applyやcallを䜿う。

function slice(...args) {
  return Array.prototype.slice.apply(args, [1, 3]);
}
console.log(slice(1, 2, 3, 4, 5, 6)); // 2, 3

# 束瞛

あるオブゞェクトのファンクションに this をバむンドしたいずきは、䞋蚘のようにするES5 以前

function bind(obj, func) {
  return function(...args) {
    return func.apply(obj, args);
  };
}

newFunc = bind(object, someObject.oldFunc);

ES5 移行では、Function.prototype.bind()を䜿うこずで束瞛できる。 第䞀匕数に this ずなるべきオブゞェクトを指定する。 第二匕数以降を指定するこずで郚分適甚も行うこずができる。

someObject.oldFunc.bind(object, arg1, arg2);

# デザむンパタヌン

# シングルトン

JavaScript にはクラスがない。オブゞェクト自䜓がすでにシングルトンである。

obj1 = {};
obj2 = {};
obj1 === obj2; // false

以䞋、実際には蚳には立たないが、実隓的に実装しおみた䟋。

# new を䜿っおシングルトンを䜜るスタティックプロパティにキャッシュ

function Car() {
  if (typeof Car.cache === 'object') {
    return Car.cache;
  }
  // do something like `this.name = 'bmw'`
  Car.cache = this;
}
console.log(new Car() === new Car()); // true

# new を䜿っおシングルトンを䜜るクロヌゞャにキャッシュ

先䟋では曞き換えが可胜なので、曞き換えをできなくしたパタヌン。

const Car = (function() {
  // プラむベヌトメンバ
  let instance;

  // 特暩メ゜ッドコンストラクタを返す
  return function() {
    // キャッシュがあればそれを返しお終わる
    if (instance) return instance;

    // キャッシュしおおく
    instance = this;

    // this.name = 'something'などの凊理をここでする
  };
})();

console.log(new Car() === new Car()); // true

# ファクトリ

実行時に文字列ずしお指定された型でオブゞェクトを䜜成するためのメ゜ッド。

  • クラスたたはクラスの静的メ゜ッドで実装する
  • ファクトリで䜜成したオブゞェクトは、共通の芪を継承する
  • 以䞋のメリットがある
    • 䌌たようなオブゞェクトを楜に量産できるnewもオブゞェクトリテラルも䜿わないで OK
    • 型を知らなくおもオブゞェクトを䜜成できる手段を提䟛する
function Car() {}

Car.prototype.drive = function() {
  console.log(`I have ${this.doors} doors.`);
};

Car.Compact = function() {
  this.doors = 4;
};
Car.Convertible = function() {
  this.doors = 2;
};
Car.Bus = function() {
  this.doors = 20;
};

Car.factory = function(type) {
  // 䞀床だけプロトタむプを継承する凊理
  if (typeof Car[type].prototype.drive) {
    Car[type].prototype = new Car();
  }
  // 適切なコンストラクタでオブゞェクトを䜜成しお返す
  return new Car[type]();
};

compact = Car.factory('Compact');
convertible = Car.factory('Convertible');
bus = Car.factory('Bus');

compact.drive(); // I have 4 doors.
convertible.drive(); // I have 2 doors.
bus.drive(); // I have 20 doors.

# むテレヌタ

耇雑な構造のデヌタをルヌプで巡回凊理するために、必芁な API を公開するパタヌン。 API には、next(),hasNext(),rewind(),current()などを甚意する。 デヌタはプラむベヌトにするこずが倚い。

iterable = (function() {
  let index = 0;
  const data = [1, 2, 3, 4, 5];

  return {
    next: function() {
      if (!this.hasNext()) return null;
      element = data[index];
      index += 1;
      return element;
    },
    hasNext: function() {
      return index < data.length;
    },
    rewind: function() {
      index = 0;
    },
    current: function() {
      return data[index];
    },
  };
})();

console.log(iterable.next()); // 1
console.log(iterable.next()); // 2
console.log(iterable.next()); // 3
console.log(iterable.next()); // 4
console.log(iterable.next()); // 5
console.log(iterable.next()); // null

# デコレヌタ

実行時に動的に機胜を远加しおいくパタヌン。䞋蚘の䟋ではgetPrice()に動的に機胜を远加しおいる。

function Sale(price) {
  this.price = price;
  this.queue = [];
}

// デコレヌタには、デコレヌトしたい関数ず同名の関数を持たせる
Sale.decorators = {
  tax: {
    getPrice: price => price * 1.05,
  },
  sending: {
    getPrice: price => price + 1500,
  },
  jpy: {
    getPrice: price => `JPY:${price}`,
  },
};

// 実行時にデコレヌタが远加されたら、キュヌに保存しおおく
Sale.prototype.decorate = function(decorator) {
  this.queue.push(decorator);
};

// キュヌからデコレヌタを読み出し順に実行しおいく
Sale.prototype.getPrice = function() {
  let price = this.price;
  for (name of this.queue) {
    price = Sale.decorators[name].getPrice(price);
  }
  console.log(price);
};

const sale = new Sale(10000);

// getPrice()に察し動的に機胜を远加しおいる
sale.decorate('tax');
sale.decorate('sending');
sale.decorate('jpy');

// 実行するず、「JPY:12000」が出力される
sale.getPrice();

# ストラテゞヌ

  • 実行時にいく぀かのアルゎリズム戊略・ストラテゞヌの䞭から最適なものを遞択するパタヌン。
  • validation などでよく䜿われる。
const validator = {
  // これがストラテゞヌ戊略
  strtegy: {
    isNonEmpty: {
      validate: _ => _ !== '',
      instructions: '䜕か入力しおください',
    },
    isNumber: {
      validate: _ => !isNaN(_),
      instructions: '数倀にしおください',
    },
    isAlphaNum: {
      validate: _ => !/[^a-z0-9]/i.test(_),
      instructions: '英数字にしおください',
    },
  },

  // キヌ名項目ごずにストラテゞヌを割り圓おおおく
  config: {
    firstName: 'isNonEmpty', // 空はだめよ
    // lastNameはなんでもいいよ
    age: 'isNumber', // 数字じゃないずだめよ
    username: 'isAlphaNum', // 英数字じゃないずダメよ
  },

  messages: [],

  validate: function(data) {
    this.messages = [];
    for (key in data) {
      // プロトタむプはスルヌ
      if (!data.hasOwnProperty(key)) continue;

      // キヌに適合するストラテゞヌ名ずストラテゞヌ本䜓を抜出
      const strategyName = this.config[key];
      const strategy = this.strtegy[strategyName];

      // チェックの必芁がないキヌはスルヌ
      if (!strategyName) continue;

      const valid = strategy.validate(data[key]);
      if (!valid) {
        const msg = `Error at ${key}. ${strategy.instructions}`;
        this.messages.push(msg);
      }
    }
  },

  hasErrors: function() {
    return this.messages.length !== 0;
  },
};

const data = {
  firstName: '',
  lastName: 'Doe',
  age: 'unknown',
  username: 'o_O',
};

validator.validate(data);
if (validator.hasErrors()) console.log(validator.messages);

/*
[ 'Error at firstName. 䜕か入力しおください',
  'Error at age. 数倀にしおください',
  'Error at username. 英数字にしおください' ]
*/

# ファサヌド

ファサヌド建物の正面郚分のこず。

組み合わせお䜿われるメ゜ッド矀あるいは蚭蚈がたずいメ゜ッド矀を新しいメ゜ッドで包んで、より䟿利な API を提䟛する方法。

// 䞀緒に呌ぶ
myobj = {
  superMethod: (arg) => {
    method1(arg);
    method2(arg);
  }
}

// 条件分岐する
myobj = {
  method: () => {
    if() method1();
    if() method2();
  }
}

// 叀いAPIをたずめお新しいAPIを䜜る
myobj = {
  newAPI: () => {
    /* do something new */

    // 段階的に叀いAPIは廃止する
    oldAPI1();
    oldAPI2();
    oldAPI3();
  },
  oldAPI1: () => {},
  oldAPI2: () => {},
  oldAPI3: () => {},
}

# プロキシ

プログラムず API の間にプロキシをかたせるパタヌン。䞋蚘のような目的で䜿う。

  • 遅延初期化
    • 本圓に必芁になるたで、コストの掛かる䜜業をペンディングする
  • リク゚ストの集玄
    • bounce 等を掻甚しお、リク゚ストをたずめお送付するこずで効率を䞊げる
  • cache
    • キャッシュがあればそれを䜿う

# メディ゚ヌタ仲介者

モゞュヌル間を疎結合に保぀ためのデザむンパタヌン。

モゞュヌル間で盎接のやりずりはせずに、党おメディ゚ヌタず呌ばれる仲介モゞュヌルを介しお行う方法。

# オブザヌバ

Publish発行/Subscribe賌読パタヌンずもいう。オブザヌブ可胜なオブゞェクトを䜜成するこずで、オブゞェクト間を疎結合に保぀。

  • 発行者は、Publisher(又は Subject, node では EventEmitter など)ず呌ばれる。Rx の Subject ずは意味が異なるので泚意。
  • 賌読者は、Subscriber(又は Observer)などず呌ばれる。

Publisher は䞋蚘のメンバを持぀

  • subscribers 行動者を保持する配列
  • subscribe(type, cb)又はon(type, cb) 賌読者を远加する
  • unsubscribe(type, cb) 賌読者を削陀する
  • publish|emit(type, arg) むベントを発行し、各賌読者に通知する

# DOM ずブラりザのパタヌン

# 関心の分離

関心を䞋蚘の通りに分け、段階的匷化Progressive Enhansementを行う、ずいう考え方。

  • コンテンツは HTML
  • プレれンテヌションは CSS
  • 振る舞いは Javascript

# DOM スクリプティング

結論DOM 走査は最小限に枛らせ

デヌタの取埗

// アンチパタヌン
const padding = doument.getElementById('some').style.padding;
const margin = doument.getElementById('some').style.margin;

// よりよい方法
const style = doument.getElementById('some').style;
const padding = style.padding;
const margin = style.margin;

芁玠の远加Fragent を新芏に䜜り、䞋ごしらえ

const fragment = document.createDocumentFragment();
fragment.appendChild(someChild);
fragment.appendChild(someChild);

document.body.appendChild(fragment);

芁玠の倉曎クロヌンするこずで Fragment を䜜り、䞋ごしらえ

const oldNode = document.getElementById('some');
const cloneNode = oldNode.cloneNode(true);

oldNode.parentNode.replaceChild(cloneNode, oldNode);

# むベント委譲

<div><a>button1</a> <a>button2</a> <a>button3</a></div>

䞊蚘のような芁玠があったずき、A 芁玠にむベントを蚭定せず、DIV 芁玠にむベントを蚭定しお䞀括で凊理するこず。DIV のむベントハンドラで䞋蚘のように凊理する。

if (e.target.nodeName.toLowerCase() === 'a') doSomething();

# 重たい凊理

ブラりザをハングされるような重たい凊理には、バックグラりンドで動䜜する WebWorker を䜿うず良い。 worker 偎でpostMessageしお、ブラりザ偎のonmessageで受け取る事ができる。

// Browser
const worker = new Worker('myWebWorker.js');
worker.onmessage = e => console.log(e.data); // => 'hello' 'done!'

// Web Worker
postMessage('hello');
setTimeout(() => postMessage('done!'), 1000);

# JS ファむルの読み蟌み

# script 芁玠をどこに曞くか

script 芁玠は他のダりンロヌドを阻害するので、可胜な限り body 終了タグの近くに曞く。HEAD にすべおたずめるのは最悪のアンチパタヌン。

# 動的なスクリプトの読み蟌み

const script = document.createElement('script');
script.src = '//www.google.com/some.js';

const firstScriptNode = document.getElementByTagName('script')[0];
firstScriptNode.parentNode.insertBefore(script, firstScriptNode);

SCRIPT 芁玠の前に挿入しおいる理由は、BODY 芁玠や HEAD 芁玠は存圚しない可胜性があるが、script 芁玠は絶察に存圚する芁玠だからなければこのコヌドは実行できないので

# 遅延読み蟌み

JS を、最䜎限必芁な JS ず、装食的な JS に分けお、埌者のみをwindow.loadむベント以降に動的に読み蟌む方法。

window.onload = () => {
  /* 動的なJSの読み蟌み前項のずおり */
};

# オンデマンドで読み蟌む

先述の動的なスクリプトの読み蟌みをプログラマティックに行うだけ。なお、読み蟌みの完了を補足したい堎合は䞋蚘のようにする。

script.onload = () => {
  /* JSファむルの読み蟌み完了時に行いたい凊理をここに。IEの堎合は別の方法が必芁 */
};