パターンとは
勉強していると「パターン」という言葉が結構出てきます。パターンというのは、こう書けばこういう風に動くよ、といったような書き方のお手本みたいなもので、プログラムとして機能を持っているわけではありません。
よく出来たデザインパターンはまるでピタゴラ装置のように色々な仕組みが絡まり合って最適な処理をしてくれるものもあり、デザインパターンを理解できた時の「なるほど」は他の追随を許さないですね。(個人の感想があります)
パターンは形として出来上がっているので、それを覚えておいて使えるようになれば品質の高いプログラムが書けるようになれたり、技術者同士の打ち合わせなどの時に「ここは◎◎パターンが良いね」などという話だけで細かい所を考えなくても理解できるので、「公用語」として働く事もあります。
私もこの学習の後半に対峙するであろう「デザインパターン」などもそういう点から理解し、書けるようになれたらいいな思っています。
まだまだヒヨッコなので、スラスラ書けるようになりたいなぁ〜。
では、本題に入りましょう。
関数
即時オブジェクト初期化
こちらもパターンです。
初期化に使うオブジェクトを作成し、その場で実行させます。その時に宣言した変数などは、スコープ内で処理されるので、グローバルを汚染する事が無いというのが特徴です。
初期化に関する内容を一挙にまとめる事ができ、視認性も拡がり良いと思います。
またまたデモを作りました。自分がしっかり理解したい為に作って覚えるという手段を使っています。そのせいで進みが遅くなってきましたが…。
ソース
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>即時オブジェクト初期化</title>
<style>
div#container:after {
clear: both;
}
div#container div.box {
float: left;
}
</style>
</head>
<body>
<div id="container">
<div class="box">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
<div class="box">box4</div>
<div class="box">box5</div>
<div class="box">box6</div>
</div>
</body>
<script type="text/javascript" src="js-playground3.js">
</script>
</html>
//セレクタで対象を取得
var targets = document.querySelectorAll("div#container div.box");
//変更前の値をコンソールに出力
for (var i = 0, max = targets.length; i < max; i += 1) {
console.log("index:" + i + " minwidth:" + targets[i].style.minWidth + " minheight:" + targets[i].style.minHeight);
}
//即時オブジェクト初期化を行う
({
//初期化する値を書く
minwidth: 300,
minheight: 200,
/*初期化用関数*/
//サイズを変更する関数
setSize: function(target) {
target.style.minWidth = this.minwidth + "px";
target.style.minHeight = this.minheight + "px";
},
// 色を自動生成して設定する
colorGenerator: function(target) {
target.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
},
//初期化で呼ばれる
init: function(global) {
//対象の数だけ繰り返す
for (var i = 0, max = targets.length ;i < max; i += 1) {
this.setSize(targets[i]);
this.colorGenerator(targets[i]);
}
}
}).init(window); //←init関数を呼び出す
//変更された値をコンソールに出力
for (var i = 0, max = targets.length; i < max; i += 1) {
console.log("index:" + i + " minwidth:" + targets[i].style.minWidth + " minheight:" + targets[i].style.minHeight);
}
console.log(minwidth); //ReferenceError
解説
即時オブジェクト初期化はまず、init
関数を呼び出す事が前提となっており、宣言をし終えた時にinit
を呼び出して設定を行います。
機能を分割させるために関数を設定する事もできます。初期化された後は解放されてしまうので再利用は利きません。
上記ソースでは、試しに最後の行で即時オブジェクト内のminwidth
を取得してみようとしていますが、スコープ内の変数は取得できないのでエラーが出ています。(code penでは記述していません)
このように、スコープ内で変数を宣言し、その中で処理を終えたら宣言した全てが消えますので、グローバルを汚染する事無く設定させることができます。
初期化時分岐
ブラウザ毎に差異があり、「 IE死ね」などとは現場での挨拶みたいなものでした。
最近では、ブラウザ差異も昔ほど大差がなくなり、そしてIEがバージョン10までのサポート期間が打ち切られた事もあり、全世界のWeb開発者は救われたんじゃないかと思います。
--
2016/6/4追記
コメント欄より情報を頂きました。 IE9のみWindows Vistaのサポート期限である2017年4月11日までサポート継続中との事です。
IE10はもうサポート終了となっているというのに…。早く絶滅して欲しいですね。
--
ただ案件によってはレガシブラウザーへの対応を考慮しなければならない場合があります。社内システムがIEのバージョンが7ベースで作られてるからそれを崩せない等がよくある話で、その場合、IE7独自のコードを使わざるを得なくなります。
そういった時に使えるのが、初期化時分岐です。
例えば、イベント登録を行うaddEventListener
はChromeやFirefoxでは標準で使えますが、IEではバージョン9でサポートされ、ソレ以前はattachEvent
を使わなければならない、などと面倒な事を考えなければなりません。
そのようなブラウザ毎の差異を吸収するため、イベントの対応状態をソースコードが読み込まれる頭の方で1度行って使える関数を変数に格納しておき、呼び出す際はその変数を使って呼び出せば、何度もこのイベントがあるかどうかを調べる事もないですし、作った変数の関数を呼び出していれば、そのような煩わしい事を考えないで済むのでオススメです。
//ユーティリティー用オブジェクトの宣言
var utils = {
addListener: null,
removeListener: null
}
//大体のブラウザでサポートされているイベントリスナの関数用
if (typeof window.addEventListener === "function") {
utils.addListener = function(el, type, fn) {
el.addEventListener(type, fn, false);
};
utils.removeListener = function(el, type, fn) {
el.removeEventListener(type, fn, false);
};
//IE9未満のブラウザでサポートされているイベントリスナの関数用
} else if (typeof document.attachEvent === "function") {
utils.addListener = function(el, type, fn) {
el.attachEvent('on' + type, fn);
};
utils.removeListener = function(el, type, fn) {
el.detachEvent('on' + type, fn);
};
//上記に当てはまらない場合
} else {
utils.addListener = function(el, type, fn) {
el['on' + type] = fn;
};
utils.removeListener = function(el, type, fn) {
el['on' + type] = null;
};
};
console.log(utils.addListener);
/* 出力(Safari for mac)
function (el, type, fn) {
el.addEventListener(type, fn, false);
}
*/
console.log(utils.removeListener);
/*
function (el, type, fn) {
el.removeEventListener(type, fn, false);
}
*/
感想
関数編、もっともっと速度を上げる予定でしたが、1つ1つが物凄いタメになるので、キッチリやる所はやっていこうと思います。時間がいくらあっても足りないぞ…。
リンク
次回はこちら
オブジェクト指向初心者の私がJavaScriptにも再入門 「関数・メモ化・カリー化・bind編」 〜JavaScriptパターン 学習8日目【逃げメモ】〜
前回はこちら
オブジェクト指向初心者の私がJavaScriptにも再入門 「関数・クロージャ・即時関数編」 〜JavaScriptパターン 学習6日目【逃げメモ】〜