码迷,mamicode.com
首页 > 编程语言 > 详细

谈JavaScript代码封装

时间:2016-04-08 19:47:41      阅读:287      评论:0      收藏:0      [点我收藏+]

标签:

也算老生常谈的问题了,再深入搞一搞怎么玩儿封装,如果看到这篇文章的你,正好你也是追求完美的代码洁癖狂者,那么这篇文章相信非常适合你。

举一个例子,编写一个Person类,具有name和birthday(时间戳)两个属性及对应的getter和setter方法,注意,setBirthday输入的参数是日期字符串,如"2016-04-08"。getBirthday同样得到的也是日期字符串。那么这个类是这样的——

var Person = function(name, birthday) {
    this.name = name;
    this.birthday = birthday;   // timestamp
};

function getTimestampOfInput(dateString) {
    return new Date(dateString).getTime();
}

function getFormattedDay(timestamp) {
    var datetime = new Date(timestamp);
    var year = datetime.getFullYear();
    var month = datetime.getMonth() + 1;
    var date = datetime.getDate();
    return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
            + (String(date).length < 2 ? "0" + date : date);
}

Person.prototype = {
    setName: function(name) {
        this.name = name;
    },
    getName: function() {
        return this.name;
    },
    /**
     * 设置生日
     * @param dateString
     */
    setBirthday: function(dateString) {
        this.birthday = getTimestampOfInput(dateString);
    },
    /**
     * 获取生日
     * @returns {*}
     */
    getBirthday: function() {
        return getFormattedDay(this.birthday);
    }
};

如果采用面向过程的方式去写,我们需要借助自执行匿名函数闭包的方式,如——

// 常用模式一:单例/静态 - 私有变量&共有方法
// 生成一个人
var person = (function() {
    // 私有变量
    var name = ‘‘;
    var birthday = new Date().getTime();    // 默认是时间戳方式
    // 共有方法
    return {
        setName: function(newName) {
            name = newName;
        },
        getName: function() {
            return name;
        },
        setBirthday: function(dateString) {
            // 私有函数
            function getTimestampOfInput() {
                return new Date(dateString).getTime();
            }

            birthday = getTimestampOfInput();
        },
        getBirthday: function() {
            return getFormattedDay(birthday);

            // 函数式 - 不访问外界变量,没有闭包的呈现
            // 有了输入,便有了预想中的输出,不保存状态
            // 私有函数 - 已工具方法存在
            function getFormattedDay(timestamp) {
                var datetime = new Date(timestamp);
                var year = datetime.getFullYear();
                var month = datetime.getMonth() + 1;
                var date = datetime.getDate();
                return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                        + (String(date).length < 2 ? "0" + date : date);
            }
        }
    };
})();

person.setName(‘king‘);
console.log(person.getName());

person.setBirthday(‘2016-4-8‘);
console.log(person.getBirthday());

 

一、精分面向过程的写法

要知道,上面的面向过程person是一个单例,这种写法更像是一种命名空间提供工具函数的方式,如——

技术分享
  1 /**
  2  * @file cookie
  3  * @author
  4  */
  5 define(function (require, exports, module) {
  6 
  7     /**
  8      * 操作 cookie
  9      *
 10      * 对外暴露三个方法:
 11      *
 12      * get()
 13      * set()
 14      * remove()
 15      *
 16      * 使用 cookie 必须了解的知识:
 17      *
 18      * 一枚 cookie 有如下属性:
 19      *
 20      *    key value domain path expires secure
 21      *
 22      *    domain: 浏览器只向指定域的服务器发送 cookie,默认是产生 Set-Cookie 响应的服务器的主机名
 23      *    path: 为特定页面指定 cookie,默认是产生 Set-Cookie 响应的 URL 的路径
 24      *    expires: 日期格式为(Weekday, DD-MON-YY HH:MM:SS GMT)唯一合法的时区是 GMT,默认是会话结束时过期
 25      *    secure: 使用 ssl 安全连接时才会发送 cookie
 26      *
 27      * 有点类似命名空间的意思
 28      *
 29      */
 30 
 31     ‘use strict‘;
 32 
 33     /**
 34      * 一小时的毫秒数
 35      *
 36      * @inner
 37      * @const
 38      * @type {number}
 39      */
 40     var HOUR_TIME = 60 * 60 * 1000;
 41 
 42     /**
 43      * 把 cookie 字符串解析成对象
 44      *
 45      * @inner
 46      * @param {string} cookieStr 格式为 key1=value1;key2=value2;
 47      * @return {Object}
 48      */
 49     function parse(cookieStr) {
 50 
 51         if (cookieStr.indexOf(‘"‘) === 0) {
 52             // 如果 cookie 按照 RFC2068 规范进行了转义,要转成原始格式
 53             cookieStr = cookieStr.slice(1, -1)
 54                                  .replace(/\\"/g, ‘"‘)
 55                                  .replace(/\\\\/g, ‘\\‘);
 56         }
 57 
 58         var result = { };
 59 
 60         try {
 61             // Replace server-side written pluses with spaces.
 62             // If we can‘t decode the cookie, ignore it, it‘s unusable.
 63             // If we can‘t parse the cookie, ignore it, it‘s unusable.
 64             cookieStr = decodeURIComponent(cookieStr.replace(/\+/g, ‘ ‘));
 65 
 66             $.each(
 67                 cookieStr.split(‘;‘),
 68                 function (index, part) {
 69                     var pair = part.split(‘=‘);
 70                     var key = $.trim(pair[0]);
 71                     var value = $.trim(pair[1]);
 72 
 73                     if (key) {
 74                         result[key] = value;
 75                     }
 76                 }
 77             );
 78         }
 79         catch (e) { }
 80 
 81         return result;
 82     }
 83 
 84     /**
 85      * 设置一枚 cookie
 86      *
 87      * @param {string} key
 88      * @param {string} value
 89      * @param {Object} options
 90      */
 91     function setCookie(key, value, options) {
 92 
 93         var expires = options.expires;
 94 
 95         if ($.isNumeric(expires)) {
 96             var hours = expires;
 97             expires = new Date();
 98             expires.setTime(expires.getTime() + hours * HOUR_TIME);
 99         }
100 
101         document.cookie = [
102             encodeURIComponent(key), ‘=‘, encodeURIComponent(value),
103             expires ? ‘;expires=‘ + expires.toUTCString() : ‘‘,
104             options.path ? ‘;path=‘ + options.path : ‘‘,
105             options.domain ? ‘;domain=‘ + options.domain : ‘‘,
106             options.secure ? ‘;secure‘ : ‘‘
107         ].join(‘‘);
108     }
109 
110     /**
111      * 读取 cookie 的键值
112      *
113      * 如果不传 key,则返回完整的 cookie 键值对象
114      *
115      * @param {string=} key
116      * @return {string|Object|undefined}
117      */
118     exports.get = function (key) {
119         var result = parse(document.cookie);
120         return $.type(key) === ‘string‘ ? result[key] : result;
121     };
122 
123     /**
124      * 写入 cookie
125      *
126      * @param {string|Object} key 如果 key 是 string,则必须传 value
127      *                            如果 key 是 Object,可批量写入
128      * @param {*=} value
129      * @param {Object=} options
130      * @property {number=} options.expires 过期小时数,如 1 表示 1 小时后过期
131      * @property {string=} options.path 路径,默认是 /
132      * @property {string=} options.domain 域名
133      * @property {boolean=} options.secure 是否加密传输
134      */
135     exports.set = function (key, value, options) {
136 
137         if ($.isPlainObject(key)) {
138             options = value;
139             value = null;
140         }
141 
142         options = $.extend({ }, exports.defaultOptions, options);
143 
144         if (value === null) {
145             $.each(
146                 key,
147                 function (key, value) {
148                     setCookie(key, value, options);
149                 }
150             );
151         }
152         else {
153             setCookie(key, value, options);
154         }
155     };
156 
157     /**
158      * 删除某个 cookie
159      *
160      * @param {string} key
161      * @param {Object=} options
162      * @property {string=} options.path cookie 的路径
163      * @property {string=} options.domain 域名
164      * @property {boolean=} options.secure 是否加密传输
165      */
166     exports.remove = function (key, options) {
167 
168         if (key == null) {
169             return;
170         }
171 
172         options = options || { };
173         options.expires = -1;
174 
175         setCookie(
176             key,
177             ‘‘,
178             $.extend({ }, exports.defaultOptions, options)
179         );
180     };
181 
182     /**
183      * 默认属性,暴露给外部修改
184      *
185      * @type {Object}
186      */
187     exports.defaultOptions = {
188         path: ‘/‘
189     };
190 
191 });
View Code

对于这个person单例或者理解为一个普通的(命名空间)对象,我们会发现两个工具函数(用于birthday的格式化)——

getTimestampOfInput:服务于setBirthday这个方法
getFormattedDay:服务于getBirthday这个方法

会发现,每一次执行setBirthday,都会创建getTimestampOfInput这个函数,执行完setBirthday之后,getTimestampOfInput又会被销毁;同理getFormattedDay方法。私有性,我们做到了,但是每一次都需要去创建工具函数(getTimestampOfInput和getFormattedDay)。如果我们想把工具函数仅仅执行一次,可以这样写——

// 常用模式一:单例/静态 - 私有变量&共有方法
// 生成一个人
var person = (function() {
    // 私有变量
    var name = ‘‘;
    var birthday = new Date().getTime();    // 默认是时间戳方式
    // 共有方法
    return {
        setName: function(newName) {
            name = newName;
        },
        getName: function() {
            return name;
        },
        setBirthday: (function() {
            // 私有函数
            function getTimestampOfInput(dateString) {
                return new Date(dateString).getTime();
            }
            return function(dateString) {
                getTimestampOfInput(dateString);
            };
        })(),
        getBirthday: (function() {
            // 函数式 - 不访问外界变量,没有闭包的呈现
            // 有了输入,便有了预想中的输出,不保存状态
            // 私有函数 - 已工具方法存在
            function getFormattedDay(timestamp) {
                var datetime = new Date(timestamp);
                var year = datetime.getFullYear();
                var month = datetime.getMonth() + 1;
                var date = datetime.getDate();
                return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                        + (String(date).length < 2 ? "0" + date : date);
            }
            return function() {
                return getFormattedDay(birthday);
            };
        })()
    };
})();

要看见里面用了一层闭包哦,也就是多需要耗损内存,但换来了性能上的优化。

我们继续变态的走下去,把这两个工具函数抽取出来,如——

// 常用模式一:单例/静态 - 私有变量&共有方法
// 生成一个人
var person = (function() {
    // 私有变量
    var name = ‘‘;
    var birthday = new Date().getTime();    // 默认是时间戳方式
    // 函数式 - 不访问外界变量,没有闭包的呈现
    // 有了输入,便有了预想中的输出,不保存状态
    // 私有函数 - 已工具方法存在
    function getFormattedDay(timestamp) {
        var datetime = new Date(timestamp);
        var year = datetime.getFullYear();
        var month = datetime.getMonth() + 1;
        var date = datetime.getDate();
        return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                + (String(date).length < 2 ? "0" + date : date);
    }
    // 函数式 - 不访问外界变量,没有闭包的呈现
    // 有了输入,便有了预想中的输出,不保存状态
    // 私有函数 - 已工具方法存在
    function getTimestampOfInput(dateString) {
        return new Date(dateString).getTime();
    }
    // 共有方法
    return {
        setName: function(newName) {
            name = newName;
        },
        getName: function() {
            return name;
        },
        setBirthday: function(dateString) {
            birthday = getTimestampOfInput(dateString);
        },
        getBirthday: function() {
            return getFormattedDay(birthday);
        }
    };
})();

那么这两个工具方法同样具有私有性,但是它能够服务的方法就更多了,所有对外暴露的方法(如将来有个新的方法getCreateDay),都可以使用这两个工具函数。

OK,我们看到上面的例子中,name,birthday,包含两个工具方法都是私有的,我们可以使用"_"的方式来显示声明它是私有的,就可以这样去改装——

// 常用模式一:静态私有变量&共有方法
// 生成一个人
var person = {
    // 单例的私有属性 - 或者可理解为静态变量
    _name: ‘‘,
    // 单例的私有属性 - 或者可理解为静态变量
    _birthday: new Date().getTime(),    // 默认是时间戳方式
    // 工具函数
    _getTimestampOfInput: function(dateString) {
        return new Date(dateString).getTime();
    },
    // 工具函数
    _getFormattedDay: function(timestamp) {
        var datetime = new Date(timestamp);
        var year = datetime.getFullYear();
        var month = datetime.getMonth() + 1;
        var date = datetime.getDate();
        return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                + (String(date).length < 2 ? "0" + date : date);
    },
    // 共有方法
    setName: function(newName) {
        this._name = newName;
    },
    getName: function() {
        return this._name;
    },
    setBirthday: function(dateString) {
        this._birthday = this._getTimestampOfInput(dateString);
    },
    getBirthday: function() {
        return this._getFormattedDay(this._birthday);
    }
};

看起来还不错,但是私有属性还是可以被访问的,如person._birthday,那么,我们想要让私有的属性达到真正的私有,会有这个方式——

// 常用模式一:静态私有变量&共有方法
// 生成一个人
var person = (function() {
    // 该对象保存静态属性
    // 保存单例的状态
    var _private = {
        // 单例的私有属性 - 或者可理解为静态变量
        _name: ‘‘,
        // 单例的私有属性 - 或者可理解为静态变量
        _birthday: new Date().getTime(),    // 默认是时间戳方式
        // 工具函数
        getTimestampOfInput: function(dateString) {
            return new Date(dateString).getTime();
        },
        // 工具函数
        _getFormattedDay: function(timestamp) {
            var datetime = new Date(timestamp);
            var year = datetime.getFullYear();
            var month = datetime.getMonth() + 1;
            var date = datetime.getDate();
            return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                    + (String(date).length < 2 ? "0" + date : date);
        },
        getFormattedDayOfBirthday: function() {
            return this._getFormattedDay(this._birthday);
        }
    };

    // 共有对象
    var _public = {
        setName: function(newName) {
            _private._name = newName;
        },
        // 直接从_private对象中获取
        getName: function() {
            return _private._name;
        },
        /**
         * 可直接操作_private中的静态属性
         * @param dateString
         */
        setBirthday: function(dateString) {
            _private._birthday = _private.getTimestampOfInput(dateString);
        },
        getBirthday: function() {
            return _private.getFormattedDayOfBirthday();
        }
    };

    return _public;
})();

_private和_public这两个命名空间还不错。在此基础上,建议把工具函数拿出来,可以这样——

// 常用模式一:静态私有变量&共有方法
// 生成一个人
var person = (function() {

    // 工具函数
    // 可供_private和_public对象共用
    function getTimestampOfInput(dateString) {
        return new Date(dateString).getTime();
    }
    // 工具函数
    // 可供_private和_public对象共用
    function getFormattedDay(timestamp) {
        var datetime = new Date(timestamp);
        var year = datetime.getFullYear();
        var month = datetime.getMonth() + 1;
        var date = datetime.getDate();
        return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                + (String(date).length < 2 ? "0" + date : date);
    }

    // 该对象保存静态属性
    // 保存单例的状态
    var _private = {
        // 单例的私有属性 - 或者可理解为静态变量
        _name: ‘‘,
        // 单例的私有属性 - 或者可理解为静态变量
        _birthday: new Date().getTime()    // 默认是时间戳方式
    };

    // 共有对象
    var _public = {
        setName: function(newName) {
            _private._name = newName;
        },
        // 直接从_private对象中获取
        getName: function() {
            return _private._name;
        },
        /**
         * 可直接操作_private中的静态属性
         * @param dateString
         */
        setBirthday: function(dateString) {
            _private._birthday = getTimestampOfInput(dateString);
        },
        getBirthday: function() {
            return getFormattedDay(_private._birthday);
        }
    };

    return _public;
})();

有些同学非常喜欢将工具函数靠近与它的调用者,类似于这样——

// 常用模式一:静态私有变量&共有方法
// 生成一个人
var person = (function() {

    // 该对象保存静态属性
    // 保存单例的状态
    var _private = {
        // 单例的私有属性 - 或者可理解为静态变量
        _name: ‘‘,
        // 单例的私有属性 - 或者可理解为静态变量
        _birthday: new Date().getTime()    // 默认是时间戳方式
    };

    _private.name = ‘‘;
    _private.birthday = new Date().getTime();    // 默认是时间戳方式

    var _public = {};

    _public.setName = function(newName) {
        _private._name = newName;
    };

    _public.getName = function() {
        return _private._name;
    };

    // 工具函数
    // 可供_private和_public对象共用
    function getTimestampOfInput(dateString) {
        return new Date(dateString).getTime();
    }
    _public.setBirthday = function(dateString) {
        _private._birthday = getTimestampOfInput(dateString);
    };

    // 工具函数
    // 可供_private和_public对象共用
    function getFormattedDay(timestamp) {
        var datetime = new Date(timestamp);
        var year = datetime.getFullYear();
        var month = datetime.getMonth() + 1;
        var date = datetime.getDate();
        return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                + (String(date).length < 2 ? "0" + date : date);
    }
    _public.getBirthday = function() {
        return getFormattedDay(_private._birthday);
    };

    return _public;
})();

同样的,我们发现这两个工具函数具有通用性,可以放置于全局,供所有函数使用,那么就有这样的方式,如——

// 这里的工具类,可以以单独文件存在,供全局工程来使用
var util = {
    /**
     * 生日格式化显示
     * @param timestamp
     * @returns {string}
     * @private
     */
    getFormattedDay: function(timestamp) {  // 模拟实现静态方法
        var datetime = new Date(timestamp);
        var year = datetime.getFullYear();
        var month = datetime.getMonth() + 1;
        var date = datetime.getDate();
        return year + ‘-‘ + (String(month).length < 2 ? "0" + month : month) + "-"
                + (String(date).length < 2 ? "0" + date : date);
    },
    /**
     * 根据用户输入来获取时间戳,如输入‘1995-10-05‘
     * @param timestamp
     * @returns {string}
     * @private
     */
    getTimestampOfInput: function(dateString) {
        return new Date(dateString).getTime();
    }
};

var person = (function() {
    // 私有变量
    var name = ‘‘;
    var birthday = new Date().getTime();    // 默认是时间戳方式
    // 共有方法
    return {
        setName: function(newName) {
            name = newName;
        },
        getName: function() {
            return name;
        },
        setBirthday: function(dateString) {
            birthday = util.getTimestampOfInput(dateString);
        },
        getBirthday: function() {
            return util.getFormattedDay(birthday);
        }
    };
})();

OK,那么面向过程的写法方式,就算是精分完了,很变态对不对?

总之,没有严格的对错,按照你认同喜欢的模式来。

 

谈JavaScript代码封装

标签:

原文地址:http://www.cnblogs.com/jinguangguo/p/5369076.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!