介紹
本篇主要是介紹建立對象方面的模式的下篇,利用各種技巧可以極大地避免了錯誤或者可以編寫出非常精簡的代碼。
模式6:函數文法糖
函數文法糖是為一個對象快速添加方法(函數)的擴充,這個主要是利用prototype的特性,代碼比較簡單,我們先來看一下實現代碼:
if (typeof Function.prototype.method !== "function") { Function.prototype.method = function (name, implementation) { this.prototype[name] = implementation; return this; }; }
擴充項物件的時候,可以這麼用:
var Person = function (name) { this.name = name; } .method('getName', function () { return this.name; }) .method('setName', function (name) { this.name = name; return this; });
這樣就給Person函數添加了getName和setName這2個方法,接下來我們來驗證一下結果:
var a = new Person('Adam'); console.log(a.getName()); // 'Adam' console.log(a.setName('Eve').getName()); // 'Eve'
模式7:對象常量
對象常量是在一個對象提供set,get,ifDefined各種方法的體現,而且對於set的方法只會保留最先設定的對象,後期再設定都是無效的,已達到別人無法重載的目的。實現代碼如下:
var constant = (function () { var constants = {}, ownProp = Object.prototype.hasOwnProperty, // 只允許設定這三種類型的值 allowed = { string: 1, number: 1, boolean: 1 }, prefix = (Math.random() + "_").slice(2); return { // 設定名稱為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; } }; } ());
驗證代碼如下:
// 檢查是否存在 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
模式8:沙箱模式
沙箱(Sandbox)模式即時為一個或多個模組提供單獨的上下文環境,而不會影響其他模組的上下文環境,比如有個Sandbox裡有3個方法 event,dom,ajax,在調用其中2個組成一個環境的話,和調用三個組成的環境完全沒有幹擾。Sandbox實現代碼如下:
function Sandbox() { // 將參數轉為數組 var args = Array.prototype.slice.call(arguments), // 最後一個參數為callback callback = args.pop(), // 除最後一個參數外,其它均為要選擇的模組 modules = (args[0] && typeof args[0] === "string") ? args : args[0], i; // 強制使用new操作符 if (!(this instanceof Sandbox)) { return new Sandbox(modules, callback); } // 添加屬性 this.a = 1; this.b = 2; // 向this對象上需想添加模組 // 如果沒有模組或傳入的參數為 "*" ,則以為著傳入所有模組 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.modules[modules[i]](this); } // 調用 callback callback(this); } // 預設添加原型對象 Sandbox.prototype = { name: "My Application", version: "1.0", getName: function () { return this.name; } };
然後我們再定義預設的初始模組:
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 () { }; };
調用方式如下:
// 調用方式 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); // 上面定義的所有方法都可訪問 });
通過三個不同的調用方式,我們可以看到,三種方式的上下文環境都是不同的,第一種裡沒有foo; 而第二種則沒有attachEvent,因為只載入了ajax和dom,而沒有載入event; 第三種則載入了全部。
模式9:靜態成員
靜態成員(Static Members)只是一個函數或對象提供的靜態屬性,可分為私人的和公有的,就像C#或Java裡的public static和private static一樣。
我們先來看一下公有成員,公有成員非常簡單,我們平時聲明的方法,函數都是公有的,比如:
// 建構函式 var Gadget = function () { }; // 公有靜態方法 Gadget.isShiny = function () { return "you bet"; }; // 原型上添加的正常方法 Gadget.prototype.setPrice = function (price) { this.price = price; }; // 調用靜態方法 console.log(Gadget.isShiny()); // "you bet" // 建立執行個體,然後調用方法 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"
而私人靜態成員,我們可以利用其閉包特性去實現,以下是兩種實現方式。
第一種實現方式:
var Gadget = (function () { // 靜態變數/屬性 var counter = 0; // 閉包返回建構函式的新實現 return function () { console.log(counter += 1); }; } ()); // 立即執行 var g1 = new Gadget(); // logs 1 var g2 = new Gadget(); // logs 2 var g3 = new Gadget(); // logs 3
可以看出,雖然每次都是new的對象,但數字依然是遞增的,達到了靜態成員的目的。
第二種方式:
var Gadget = (function () { // 靜態變數/屬性 var counter = 0, NewGadget; //新建構函式實現 NewGadget = function () { counter += 1; }; // 授權可以訪問的方法 NewGadget.prototype.getLastId = function () { return counter; }; // 覆蓋建構函式 return NewGadget; } ()); // 立即執行 var iphone = new Gadget(); iphone.getLastId(); // 1 var ipod = new Gadget(); ipod.getLastId(); // 2 var ipad = new Gadget(); ipad.getLastId(); // 3
數字也是遞增了,這是利用其內部授權方法的閉包特性實現的。
總結
這是對象建立模式的下篇,兩篇一起總共9種模式,是我們在日常JavaScript編程中經常使用的對象建立模式,不同的情境起到了不同的作用,希望大家根據各自的需求選擇適用的模式。
參考:http://shichuan.github.com/javascript-patterns/#object-creation-patterns
轉載:
湯姆大叔