本篇主要是介紹創(chuàng)建對象方面的模式的下篇,利用各種技巧可以極大地避免了錯誤或者可以編寫出非常精簡的代碼。
函數(shù)語法糖是為一個對象快速添加方法(函數(shù))的擴(kuò)展,這個主要是利用prototype的特性,代碼比較簡單,我們先來看一下實(shí)現(xiàn)代碼:
if (typeof Function.prototype.method !== "function") { Function.prototype.method = function (name, implementation) { this.prototype[name] = implementation; return this; }; }
擴(kuò)展對象的時候,可以這么用:
var Person = function (name) { this.name = name; } .method('getName', function () { return this.name; }) .method('setName', function (name) { this.name = name; return this; });
這樣就給Person函數(shù)添加了getName和setName這2個方法,接下來我們來驗(yàn)證一下結(jié)果:
var a = new Person('Adam'); console.log(a.getName()); // 'Adam' console.log(a.setName('Eve').getName()); // 'Eve'
對象常量是在一個對象提供set,get,ifDefined各種方法的體現(xiàn),而且對于set的方法只會保留最先設(shè)置的對象,后期再設(shè)置都是無效的,已達(dá)到別人無法重載的目的。實(shí)現(xiàn)代碼如下:
var constant = (function () { var constants = {}, ownProp = Object.prototype.hasOwnProperty, // 只允許設(shè)置這三種類型的值 allowed = { string: 1, number: 1, boolean: 1 }, prefix = (Math.random() + "_").slice(2); return { // 設(shè)置名稱為name的屬性 set: function (name, value) { if (this.isDefined(name)) { return false; } if (!ownProp.call(allowed, typeof value)) { return false; } constants[prefix + name] = value; return true; }, // 判斷是否存在名稱為name的屬性 isDefined: function (name) { return ownProp.call(constants, prefix + name); }, // 獲取名稱為name的屬性 get: function (name) { if (this.isDefined(name)) { return constants[prefix + name]; } return null; } }; } ());
驗(yàn)證代碼如下:
// 檢查是否存在 console.log(constant.isDefined("maxwidth")); // false // 定義 console.log(constant.set("maxwidth", 480)); // true // 重新檢測 console.log(constant.isDefined("maxwidth")); // true // 嘗試重新定義 console.log(constant.set("maxwidth", 320)); // false // 判斷原先的定義是否還存在 console.log(constant.get("maxwidth")); // 480
沙盒(Sandbox)模式即時為一個或多個模塊提供單獨(dú)的上下文環(huán)境,而不會影響其他模塊的上下文環(huán)境,比如有個Sandbox里有3個方法event,dom,ajax,在調(diào)用其中2個組成一個環(huán)境的話,和調(diào)用三個組成的環(huán)境完全沒有干擾。Sandbox實(shí)現(xiàn)代碼如下:
function Sandbox() { // 將參數(shù)轉(zhuǎn)為數(shù)組 var args = Array.prototype.slice.call(arguments), // 最后一個參數(shù)為callback callback = args.pop(), // 除最后一個參數(shù)外,其它均為要選擇的模塊 modules = (args[0] && typeof args[0] === "string") ? args : args[0], i; // 強(qiáng)制使用new操作符 if (!(this instanceof Sandbox)) { return new Sandbox(modules, callback); } // 添加屬性 this.a = 1; this.b = 2; // 向this對象上需想添加模塊 // 如果沒有模塊或傳入的參數(shù)為 "" ,則以為著傳入所有模塊 if (!modules || modules == '') { modules = []; for (i in Sandbox.modules) { if (Sandbox.modules.hasOwnProperty(i)) { modules.push(i); } } } // 初始化需要的模塊 for (i = 0; i < modules.length; i += 1) { Sandbox.modulesmodules[i]; } // 調(diào)用 callback callback(this); } // 默認(rèn)添加原型對象 Sandbox.prototype = { name: "My Application", version: "1.0", getName: function () { return this.name; } };
然后我們再定義默認(rèn)的初始模塊:
Sandbox.modules = {}; Sandbox.modules.dom = function (box) { box.getElement = function () { }; box.getStyle = function () { }; box.foo = "bar"; }; Sandbox.modules.event = function (box) { // access to the Sandbox prototype if needed: // box.constructor.prototype.m = "mmm"; box.attachEvent = function () { }; box.detachEvent = function () { }; }; Sandbox.modules.ajax = function (box) { box.makeRequest = function () { }; box.getResponse = function () { }; };
調(diào)用方式如下:
// 調(diào)用方式 Sandbox(['ajax', 'event'], function (box) { console.log(typeof (box.foo)); // 沒有選擇dom,所以box.foo不存在 }); Sandbox('ajax', 'dom', function (box) { console.log(typeof (box.attachEvent)); // 沒有選擇event,所以event里定義的attachEvent也不存在 }); Sandbox('*', function (box) { console.log(box); // 上面定義的所有方法都可訪問 });
通過三個不同的調(diào)用方式,我們可以看到,三種方式的上下文環(huán)境都是不同的,第一種里沒有foo; 而第二種則沒有attachEvent,因?yàn)橹患虞d了ajax和dom,而沒有加載event; 第三種則加載了全部。
靜態(tài)成員(Static Members)只是一個函數(shù)或?qū)ο筇峁┑撵o態(tài)屬性,可分為私有的和公有的,就像C#或Java里的public static和private static一樣。
我們先來看一下公有成員,公有成員非常簡單,我們平時聲明的方法,函數(shù)都是公有的,比如:
// 構(gòu)造函數(shù) var Gadget = function () { }; // 公有靜態(tài)方法 Gadget.isShiny = function () { return "you bet"; }; // 原型上添加的正常方法 Gadget.prototype.setPrice = function (price) { this.price = price; }; // 調(diào)用靜態(tài)方法 console.log(Gadget.isShiny()); // "you bet" // 創(chuàng)建實(shí)例,然后調(diào)用方法 var iphone = new Gadget(); iphone.setPrice(500); console.log(typeof Gadget.setPrice); // "undefined" console.log(typeof iphone.isShiny); // "undefined" Gadget.prototype.isShiny = Gadget.isShiny; console.log(iphone.isShiny()); // "you bet"
而私有靜態(tài)成員,我們可以利用其閉包特性去實(shí)現(xiàn),以下是兩種實(shí)現(xiàn)方式。
第一種實(shí)現(xiàn)方式:
var Gadget = (function () { // 靜態(tài)變量/屬性 var counter = 0; // 閉包返回構(gòu)造函數(shù)的新實(shí)現(xiàn) return function () { console.log(counter += 1); }; } ()); // 立即執(zhí)行 var g1 = new Gadget(); // logs 1 var g2 = new Gadget(); // logs 2 var g3 = new Gadget(); // logs 3
可以看出,雖然每次都是new的對象,但數(shù)字依然是遞增的,達(dá)到了靜態(tài)成員的目的。
第二種方式:
var Gadget = (function () { // 靜態(tài)變量/屬性 var counter = 0, NewGadget; //新構(gòu)造函數(shù)實(shí)現(xiàn) NewGadget = function () { counter += 1; }; // 授權(quán)可以訪問的方法 NewGadget.prototype.getLastId = function () { return counter; }; // 覆蓋構(gòu)造函數(shù) return NewGadget; } ()); // 立即執(zhí)行 var iphone = new Gadget(); iphone.getLastId(); // 1 var ipod = new Gadget(); ipod.getLastId(); // 2 var ipad = new Gadget(); ipad.getLastId(); // 3
數(shù)字也是遞增了,這是利用其內(nèi)部授權(quán)方法的閉包特性實(shí)現(xiàn)的。
這是對象創(chuàng)建模式的下篇,兩篇一起總共9種模式,是我們在日常JavaScript編程中經(jīng)常使用的對象創(chuàng)建模式,不同的場景起到了不同的作用,希望大家根據(jù)各自的需求選擇適用的模式。
參考:http://shichuan.github.com/javascript-patterns/#object-creation-patterns
更多建議: