前言

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

举一个例子,编写一个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

相关文章: