深入理解JavaScript系列(48):对象创建模式(下篇)


Posted in Javascript onMarch 04, 2015

介绍

本篇主要是介绍创建对象方面的模式的下篇,利用各种技巧可以极大地避免了错误或者可以编写出非常精简的代码。

模式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编程中经常使用的对象创建模式,不同的场景起到了不同的作用,希望大家根据各自的需求选择适用的模式。

Javascript 相关文章推荐
分享一个我自己写的ToolTip提示插件(附源码)
Jan 20 Javascript
js实现鼠标拖动图片并兼容IE/FF火狐/谷歌等主流浏览器
Jun 06 Javascript
firefox浏览器不支持innerText的解决方法
Aug 07 Javascript
jQuery实现首页顶部可伸缩广告特效代码
Apr 15 Javascript
javascript中的正则表达式使用详解
Aug 30 Javascript
Javascript编程之继承实例汇总
Nov 28 Javascript
js+css3实现旋转效果
Jan 20 Javascript
基于vue2实现左滑删除功能
Nov 28 Javascript
JS处理一些简单计算题
Feb 24 Javascript
jquery中为什么能用$操作
Jun 18 jQuery
JavaScript事件循环及宏任务微任务原理解析
Sep 02 Javascript
vue实现单一筛选、删除筛选条件
Oct 26 Javascript
js计算德州扑克牌面值的方法
Mar 04 #Javascript
深入理解JavaScript系列(47):对象创建模式(上篇)
Mar 04 #Javascript
深入理解JavaScript系列(46):代码复用模式(推荐篇)详解
Mar 04 #Javascript
深入理解JavaScript系列(45):代码复用模式(避免篇)详解
Mar 04 #Javascript
深入理解JavaScript系列(44):设计模式之桥接模式详解
Mar 04 #Javascript
JS实现FLASH幻灯片图片切换效果的方法
Mar 04 #Javascript
javascript下拉框选项单击事件的例子分享
Mar 04 #Javascript
You might like
php 3行代码的分页算法(求起始页和结束页)
2009/10/21 PHP
让PHP支持断点续传的源码
2010/05/16 PHP
destoon调用企业会员公司形象图片的实现方法
2014/08/21 PHP
php中将一个对象保存到Session中的方法
2015/03/13 PHP
一个简单至极的PHP缓存类代码
2015/10/23 PHP
PHP数据库连接mysql与mysqli对比分析
2016/01/04 PHP
php中yii框架实例用法
2020/12/22 PHP
Javascript 中的 &amp;&amp; 和 || 使用小结
2010/04/25 Javascript
javascript 基础篇3 类,回调函数,内置对象,事件处理
2012/03/14 Javascript
Jquery读取URL参数小例子
2013/08/30 Javascript
js中哈希表的几种用法总结
2014/01/28 Javascript
JS实现兼容各浏览器解析XML文档数据的方法
2015/06/01 Javascript
JavaScript实现简洁的俄罗斯方块完整实例
2016/03/01 Javascript
关于JavaScript数组你所不知道的3件事
2016/08/24 Javascript
bootstrap table 多选框分页保留示例代码
2017/03/08 Javascript
layer实现关闭弹出层刷新父界面功能详解
2017/11/15 Javascript
微信小程序日期时间选择器使用方法
2018/02/01 Javascript
浅谈jquery fullpage 插件增加头部和版权的方法
2018/03/20 jQuery
微信小程序配置服务器提示验证token失败的解决方法
2019/04/03 Javascript
JavaScript中工厂函数与构造函数示例详解
2019/05/06 Javascript
javascript面向对象程序设计实践常用知识点总结
2019/07/29 Javascript
ES6使用 Array.includes 处理多重条件用法实例分析
2020/03/02 Javascript
[03:04]DOTA2英雄基础教程 影魔
2013/12/11 DOTA
Python 实现购物商城,含有用户入口和商家入口的示例
2017/09/15 Python
python数组循环处理方法
2019/08/26 Python
弄懂这56个Python使用技巧(轻松掌握Python高效开发)
2019/09/18 Python
pycharm中选中一个单词替换所有重复单词的实现方法
2020/11/17 Python
Django-silk性能测试工具安装及使用解析
2020/11/28 Python
HTML5中的Web Notification桌面通知功能的实现方法
2019/07/29 HTML / CSS
英国排名第一的在线宠物用品商店:Monster Pet Supplies
2018/05/20 全球购物
超市后勤自我鉴定
2014/01/17 职场文书
入股协议书范本
2014/11/01 职场文书
夫妻分居协议书范文
2014/11/26 职场文书
2015年党员自我剖析材料
2014/12/17 职场文书
经营目标责任书
2015/05/08 职场文书
Win11电脑显示本地时间与服务器时间不一致怎么解决?
2022/04/05 数码科技