深入理解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 相关文章推荐
js判断undefined变量类型使用typeof
Jun 03 Javascript
js在指定位置增加节点函数insertBefore()用法实例
Jan 12 Javascript
JavaScript实现Flash炫光波动特效
May 14 Javascript
jquery中validate与form插件提交的方式小结
Mar 26 Javascript
jQuery操作iframe中js函数的方法小结
Jul 06 Javascript
jQuery UI插件实现百度提词器效果
Nov 21 Javascript
微信小程序 详解下拉加载与上拉刷新实现方法
Jan 13 Javascript
javascript实现多张图片左右无缝滚动效果
Mar 22 Javascript
angular-cli修改端口号【angular2】
Apr 19 Javascript
JS设置随机出现2个数字的实例代码
Jul 19 Javascript
vue.js element-ui validate中代码不执行问题解决方法
Dec 18 Javascript
angular1.x ui-route传参的三种写法小结
Aug 31 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
自制短波长线天线频率预选器 - 成功消除B2K之流的镜像
2021/03/02 无线电
PHP 魔术函数使用说明
2010/05/14 PHP
网站用php实现paypal整合方法
2010/11/28 PHP
PHP源代码数组统计count分析
2011/08/02 PHP
PHP的基本常识小结
2013/07/05 PHP
JavaScript中去掉数组中的重复值的实现方法
2011/08/03 Javascript
javascript页面加载完执行事件代码
2014/02/11 Javascript
javascript获取浏览器类型和版本的方法(js获取浏览器版本)
2014/03/13 Javascript
jQuery.lazyload+masonry改良图片瀑布流代码
2014/06/20 Javascript
JS实现动态移动层及拖动浮层关闭的方法
2015/04/30 Javascript
利用JS屏蔽页面中的Enter按键提交表单的方法
2016/11/25 Javascript
详解jQuery停止动画——stop()方法的使用
2016/12/14 Javascript
bootstrap警告框示例代码分享
2017/05/17 Javascript
vue使用$emit时,父组件无法监听到子组件的事件实例
2018/02/26 Javascript
详解NodeJs开发微信公众号
2018/05/25 NodeJs
js限制input只能输入有效的数字(第一个不能是小数点)
2018/09/28 Javascript
layui 实现自动选择radio单选框(checked)的方法
2019/09/03 Javascript
js+h5 canvas实现图片验证码
2020/10/11 Javascript
[03:27]《辉夜杯》线下训练营 导师CU和海涛指点迷津
2015/10/23 DOTA
[46:49]完美世界DOTA2联赛PWL S3 access vs Rebirth 第二场 12.19
2020/12/24 DOTA
python3.3教程之模拟百度登陆代码分享
2014/01/16 Python
Python模块文件结构代码详解
2018/02/03 Python
Windows下的Python 3.6.1的下载与安装图文详解(适合32位和64位)
2018/02/21 Python
Django Docker容器化部署之Django-Docker本地部署
2019/10/09 Python
Python greenlet和gevent使用代码示例解析
2020/04/01 Python
python 串行执行和并行执行实例
2020/04/30 Python
python如何利用paramiko执行服务器命令
2020/11/07 Python
python 操作excel表格的方法
2020/12/05 Python
用pip给python安装matplotlib库的详细教程
2021/02/24 Python
韩国三星旗下的一家超市连锁店:Home Plus
2016/07/30 全球购物
北京某公司的.net笔试题
2014/03/20 面试题
团员的自我评价
2013/12/01 职场文书
要账委托书范本
2014/09/15 职场文书
董事长助理岗位职责
2015/02/11 职场文书
无房证明样本
2015/06/17 职场文书
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python