(48)對(duì)象創(chuàng)建模式(下篇)

2018-02-24 15:26 更新

介紹

本篇主要是介紹創(chuàng)建對(duì)象方面的模式的下篇,利用各種技巧可以極大地避免了錯(cuò)誤或者可以編寫(xiě)出非常精簡(jiǎn)的代碼。

模式6:函數(shù)語(yǔ)法糖

函數(shù)語(yǔ)法糖是為一個(gè)對(duì)象快速添加方法(函數(shù))的擴(kuò)展,這個(gè)主要是利用prototype的特性,代碼比較簡(jiǎn)單,我們先來(lái)看一下實(shí)現(xiàn)代碼:

if (typeof Function.prototype.method !== "function") {
    Function.prototype.method = function (name, implementation) {
        this.prototype[name] = implementation;
        return this;
    };
}

擴(kuò)展對(duì)象的時(shí)候,可以這么用:

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個(gè)方法,接下來(lái)我們來(lái)驗(yàn)證一下結(jié)果:

var a = new Person('Adam');
console.log(a.getName()); // 'Adam'
console.log(a.setName('Eve').getName()); // 'Eve'

模式7:對(duì)象常量

對(duì)象常量是在一個(gè)對(duì)象提供set,get,ifDefined各種方法的體現(xiàn),而且對(duì)于set的方法只會(huì)保留最先設(shè)置的對(duì)象,后期再設(shè)置都是無(wú)效的,已達(dá)到別人無(wú)法重載的目的。實(shí)現(xiàn)代碼如下:

var constant = (function () {
    var constants = {},
        ownProp = Object.prototype.hasOwnProperty,
    // 只允許設(shè)置這三種類(lèi)型的值
        allowed = {
            string: 1,
            number: 1,
            boolean: 1
        },
        prefix = (Math.random() + "_").slice(2);

    return {
        // 設(shè)置名稱(chēng)為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;
        },
        // 判斷是否存在名稱(chēng)為name的屬性
        isDefined: function (name) {
            return ownProp.call(constants, prefix + name);
        },
        // 獲取名稱(chēng)為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

// 重新檢測(cè)
console.log(constant.isDefined("maxwidth")); // true

// 嘗試重新定義
console.log(constant.set("maxwidth", 320)); // false

// 判斷原先的定義是否還存在
console.log(constant.get("maxwidth")); // 480

模式8:沙盒模式

沙盒(Sandbox)模式即時(shí)為一個(gè)或多個(gè)模塊提供單獨(dú)的上下文環(huán)境,而不會(huì)影響其他模塊的上下文環(huán)境,比如有個(gè)Sandbox里有3個(gè)方法event,dom,ajax,在調(diào)用其中2個(gè)組成一個(gè)環(huán)境的話(huà),和調(diào)用三個(gè)組成的環(huán)境完全沒(méi)有干擾。Sandbox實(shí)現(xiàn)代碼如下:

function Sandbox() {
    // 將參數(shù)轉(zhuǎn)為數(shù)組
    var args = Array.prototype.slice.call(arguments),
    // 最后一個(gè)參數(shù)為callback
        callback = args.pop(),
        // 除最后一個(gè)參數(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對(duì)象上需想添加模塊
    // 如果沒(méi)有模塊或傳入的參數(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.modules[modules[i]](this);
    }

    // 調(diào)用 callback
    callback(this);
}

// 默認(rèn)添加原型對(duì)象
Sandbox.prototype = {
    name: "My Application",
    version: "1.0",
    getName: function () {
        return this.name;
    }
};

然后我們?cè)俣x默認(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));
    // 沒(méi)有選擇dom,所以box.foo不存在
});

Sandbox('ajax', 'dom', function (box) {
    console.log(typeof (box.attachEvent));
    // 沒(méi)有選擇event,所以event里定義的attachEvent也不存在
});

Sandbox('*', function (box) {
    console.log(box); // 上面定義的所有方法都可訪(fǎng)問(wèn)
});

通過(guò)三個(gè)不同的調(diào)用方式,我們可以看到,三種方式的上下文環(huán)境都是不同的,第一種里沒(méi)有foo; 而第二種則沒(méi)有attachEvent,因?yàn)橹患虞d了ajax和dom,而沒(méi)有加載event; 第三種則加載了全部。

模式9:靜態(tài)成員

靜態(tài)成員(Static Members)只是一個(gè)函數(shù)或?qū)ο筇峁┑撵o態(tài)屬性,可分為私有的和公有的,就像C#或Java里的public static和private static一樣。

我們先來(lái)看一下公有成員,公有成員非常簡(jiǎn)單,我們平時(shí)聲明的方法,函數(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的對(duì)象,但數(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)可以訪(fǎng)問(wè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)的。

總結(jié)

這是對(duì)象創(chuàng)建模式的下篇,兩篇一起總共9種模式,是我們?cè)谌粘avaScript編程中經(jīng)常使用的對(duì)象創(chuàng)建模式,不同的場(chǎng)景起到了不同的作用,希望大家根據(jù)各自的需求選擇適用的模式。

參考:http://shichuan.github.com/javascript-patterns/#object-creation-patterns

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)