prototypeを拡張することで得られるもの。prototype拡張指向へのスイッチ
(ε・◇・)з mofmof.js や新しい uupaa.js では prototype拡張を活用しています
(ε・◇・)з 長所がわかりづらいみたいなので、言い出しっぺのボクの中の人がメリットを列挙してみよー というエントリです
(ε・◇・)з でも、長文になりそうなので、思いついたらちょっとずつ書き足していって、後で再編するよー というノリです
(ε・◇・)з タイトルも適当です
かわいい子には旅をさせるよ ( HTMLElement#cut )
子ノード(div)を親ノードからパージする処理を考えてみましょう。
---✂-------------------✂--- if (div.parentNode) { div.parentNode.removeChild(div); // div.parentNode = null } ---✂-------------------✂--- 52 bytes
prototype拡張ならシンプルに書けます。
---✂-------------------✂---
div.cut(); // div.parentNode = null
---✂-------------------✂---
10 bytes
子供ほしいよ ( HTMLElement#add, HTMLElement#top )
自分が親となり子供を追加する処理を考えてみましょう。死んだと思っていた長男がいきなり登場するケースにも備えましょう
---✂-------------------✂--- div.insertBefore(child, div.firstChild); // 先頭に追加 div.appendChild(child); // 末尾に追加 ---✂-------------------✂--- 62 bytes
prototype拡張ならシンプルに書けます。
---✂-------------------✂---
div.top(child);
div.add(child);
---✂-------------------✂---
30 bytes
HTMLElement.prototype.cut, add, top の実装です。
IE8 には HTMLElement は存在しませんが、Element を使うとモダンブラウザと同様に実装できます。
HTMLElement.prototype.cut = HTMLElement_cut; HTMLElement.prototype.add = HTMLElement_add; HTMLElement.prototype.top = HTMLElement_top; // HTMLElement.prototype.cut function HTMLElement_cut() { // @ret this: this node // @desc: to cut a parent-child connection if (this.parentNode) { this.parentNode.removeChild(this); } return this; } // HTMLElement.prototype.add function HTMLElement_add(node) { // @arg Node: // @return this: // @desc: appendChild node && this.appendChild(node); return this; } // HTMLElement.prototype.top function HTMLElement_top(node) { // @return this: // @desc: insert to top node && this.insertBefore(node, this.firstChild); return this; }
(ε・◇・)з DOM はフルスペルの長い名前を使う傾向があるので、短い名前は割とセーフな気がするー って言ってました(中の人が)
数値の配列が欲しいよ (Number#to)
1から100までの数値の配列を作成する処理を考えてみましょう。
function createNumberArray(begin, end) { var rv = [], i = begin; for (; i <= end; ++i) { rv.push(i); } return rv; } createNumberArray(1, 100); // -> [1, .. 100]
JavaScript中級者の方は、以下のようなテクニカルなコードを好むかもしれませんね。
function createNumberArray(begin, end) { return Array(end - begin + 1).join(",").split(",").map(function(value, index) { return begin + index; }); } createNumberArray(1, 100); // -> [1, .. 100]
では、100から1までの数値の配列や、1つ飛ばしの数値の配列、フィルター関数を適用した配列を考えてみましょう。毎回このような配列を生成するコードを書くのは面倒ですし、createNumberArray という関数名も、もうちょっとこう… ってなりますよね。
そこで、Number.prototype.to を拡張し、1..to(100) とするだけで、簡単に 1から100までの数値の配列を作れるようにしました。
function isX5(n) { // Multiples of 5 filter return n % 5 === 0; } 1..to(100); // -> [1, 2 .. 99, 100] 100..to(1); // -> [100, 99 .. 2, 1] 1..to(100, 2); // -> [1, 3 .. 97, 99] (skip 2) 1..to(100, isX5); // -> [5, 10, .. 95, 100] (filter)
Number#to の実装です。
Number.prototype.to = Number_to; // Number#to function Number_to(end, // @arg Number: end number // `` 終了番号を数値で指定します。filterが1ならendで指定した終了番号を含みます filter) { // @arg Function/Number(= 1): filter or skip number // `` フィルター関数またはスキップ数を指定します。省略可能です // @ret Array: [begne, ... end] // @this: begin number`` 開始番号です // @raise: Error("BAD_ARG") `` 引数が不正です // @see: Array.range, Number#to // @help: Number#Number.prototype.to // @desc: create number array `` 数値の配列を作成します //{@assert mm.allow(end, "Number"); mm.allow(filter, "Function/Number/undefined"); //}@assert var rv = [], ri = 0, begin = +this, i = begin, iz = end, skip = 1, type = typeof filter, reverse = false; if (begin > end) { // 100..to(0) -> [100, ... 0] i = end; iz = begin; reverse = true; } if (type === "function") { for (; i <= iz; ++i) { if (filter(i) === true) { // `` filter が true が返すと index を採用します rv[ri++] = i; } } return reverse ? rv.reverse() : rv; } if (type === "number") { skip = filter; } if (skip <= 0 || end >= 0x7FFFFFFF) { // [!] array index over flow // new Array(2147483647) in [IE6][IE7][IE8] problem // http://msdn.microsoft.com/en-us/library/gg622936(v=VS.85).aspx // - Internet Explorer 9 Handles Array Elements // with a Large Index Differently // throw new Error("BAD_ARG"); } for (; i <= iz; i += skip) { rv[ri++] = i; } return reverse ? rv.reverse() : rv; }
for in ガード
なお、mofmof.js や uupaa.js の中ではprototype拡張部分が for in で列挙されないように mm.wiz 関数を使って拡張しています。
mm.wiz(Number.prototype, { to: Number_to }); // mm.wiz function mm_wiz(base, // @arg Object/Function/ObjectOrFunctionArray: base object extend, // @arg Object: extends object. { method: function, ... } override) { // @arg Boolean(= false): override // @ret Object/Function: base // @help: mm#mm.wiz // @desc: prototype extend without enumerability, mixin with "invisible" magic // [IE8] Object.defineProperty: Supports DOM Node but not user-defined objects. // [IE8] Object.defineProperties: Not supported var defineProperty = Object.defineProperty && Object.defineProperties, keys = (Object.keys || Object_keys)(extend), obj, ary = (Array_isArray(base) && base[0]) ? base : [base], key, method, i = 0, iz = keys.length; while (obj = ary.shift()) { for (i = 0; i < iz; ++i) { key = keys[i]; method = extend[key]; if (override || !(key in obj)) { if (defineProperty) { Object.defineProperty(obj, key, { // DataDescriptor configurable: true, enumerable: false, // invisibility writable: true, value: method }); } else { obj[key] = extend[key]; } } } } return base; }