灏天阁

编程模式

· Yin灏

命名空间

  1. 将对象用作命名空间
var MYAPP = MYAPP || {};

MYAPP.event = {
    addListener: function(el, type, fn) {
        //...
    },
    removeListener: function(el, type, fn) {
        //...
    },
    addListener: function(e) {
        //...
    }
}
  1. 命名空间中构造器的应用

本例中,DOM 工具本身就定义了一个 Element 构造器,通过它我们可以很方便的创建 DOM 元素。

var MYAPP = MYAPP || {};

MYAPP.dom = {};

MYAPP.dom.Element = function(type, properties) {
    var tmp = document.createElement(type);
    for (var i in properties) {
        if (properties.hasOwnProperty(i)) {
            tmp.setAttribute(i, properties[i]);
        }
    }
    return tmp;
}

MYAPP.dom.Text = function(txt) {
    return document.createTextNode(txt)
}

使用构造器

var link = new MYAPP.dom.Element('a', { href: 'http://baidu.com', target: '_blank' })
var text = new MYAPP.dom.Text('click_me');
link.appendChild(text);
document.body.appendChild(link);
  1. namespace() 方法

可以实现一个名为 namespace 的工具方法,来简化我们的工作,

var MYAPP = {};

MYAPP.namespace = function(name) {
    var parts = name.split('.');
    var current = MYAPP;
    for (var i = 0; i < parts.length; i++) {
        if (!current[parts[i]]) {
            current[parts[i]] = {};
        }
        current = current[parts[i]];
    }
}

MYAPP.namespace('event')
MYAPP.namespace('dom.style')

console.log(MYAPP);
/*
 var MYAPP = {
    dom: {
      style: {}
    },
    event: {}
  }
*/

初始化分支

不同浏览器对于相同或相似的方法可能有不同的实现,这时,需要依据当前的浏览器的支持方式来选择响应的执行分支,这类分支可能有很多,会拖慢脚本的执行速度。

非要等到运行时才执行分支吗?完全可以在加载脚本时,在模块初始化的过程中就将部分代码进行分支处理,这样显然更高效。

var MYAPP = {}

MYAPP.event = {
    addListener: null,
    removeListener: null
}

if (window.addEventListener) {
    MYAPP.event.addListener = function(el, type, fn) {
        el.addEventListener(type, fn, false);
    }
    MYAPP.event.removeListener = function(el, type, fn) {
        el.removeEventListener(type, fn, false);
    }
} else if (document.attachEvent) {
    MYAPP.event.addListener = function(el, type, fn) {
        el.attachEvent('on' + type, fn);
    }
    MYAPP.event.removeListener = function(el, type, fn) {
        el.detachEvent('on' + type, fn);
    }
} else {
    MYAPP.event.addListener = function(el, type, fn) {
        el['on' + type] = fn;
    }
    MYAPP.event.removeListener = function(el, type, fn) {
        el['on' + type] = null;
    }
}

console.log(MYAPP);

/*
  脚本一旦执行,我们就定义了与浏览器特性相关的 addListener 和 removeListener 方法,如此,当它们再次被调用时,就不需要在探测浏览器特性了,脚本会执行的更快。
*/

惰性初始

该分支在相关函数第一次调用时才会发生,会使得初始化过程更为轻量,因为不需要做分支检测。

var MYAPP = {};

MYAPP.myevent = {
    addListener: function(el, type, fn) {
        if (el.addEventListener) {
            MYAPP.myevent.addListener = function(el, type, fn) {
                el.addEventListener(type, fn, false);
            }
        } else if (el.attachEvent) {
            MYAPP.myevent.addListener = function(el, type, fn) {
                el.attachEvent('on' + type, fn);
            }
        } else {
            MYAPP.myevent.addListener = function(el, type, fn) {
                el['on' + type] = fn;
            }
        }
        // 执行调用
        MYAPP.myevent.addListener(el, type, fn)
    }
}

配置对象

该模式往往适用于有多个参数的函数或方法,

将多个参数处理成一个对象,不同考虑参数的顺序,也可以跳过某些参数的位置。

var MYAPP = {
    dom: {}
};

MYAPP.dom.FancyButton = function(text, conf) {
    var type = conf.type || 'submit';
    var font = conf.font || 'Verdana';
    //...
}

var config = {
    color: 'white',
    font: 'Arial, Verdana, sans-serif'
}

new MYAPP.dom.FancyButton('puuush', config);

私有属性和方法

var MYAPP = {
    dom: {}
};

MYAPP.dom.FancyButton = function(text, conf) {
    // 私有属性
    var styles = {
        font: 'Verdana',
        border: '1px solid black',
        color: 'black',
        backgroundColor: 'grey'
    };
    // 私有方法
    function setStyle(b) {
        var i;
        for (i in styles) {
            if (styles.hasOwnProperty(i)) {
                b.style[i] = conf[i] || styles[i];
            }
        }
    }

    conf = conf || {}

    var b = document.createElement('input');
    b.type = conf.type || 'submit';
    b.value = text;
    setStyle(b);
    return b;
}

私有函数公有化

var MYAPP = {};

MYAPP.dom = (function() {
    var _setStyle = function(el, prop, value) {
        console.log('setStyle');
    }
    var _getStyle = function(el, prop) {
        console.log('getStyle');
    }

    return {
        setStyle: _setStyle,
        getStyle: _getStyle,
        yetAnother: _setStyle
    }
}())

即时函数

为了保证全局命名空间不被污染,吧代码封装在一个匿名函数中并立即调用,如此,该函数的所有的变量都是局部的,并在函数返回时被销毁。

(function() {
    //...
}())

即时函数也可用于创建和返回对象,

var MYAPP = {};

MYAPP.dom = (function() {
    function _private() {
        //...
    }
    return {
        getStyle: function(el, prop) {
            console.log('getStyle');
            _private();
        },
        setStyle: function(el, prop, value) {
            console.log('setStyle');
        }
    }
}());

模块

模块模式包括一下几个部分:

  • 命名空间:用于减少模块之间的命名冲突
  • 即时函数:用于提供私有作用域以及初始化操作
  • 私有属性与方法
  • 作为返回值的对象:该函数作为模块提供公开 API
namespace('MYAPP.module.amazing');
MYAPP.module.amazing = (function(){
    var another = MYAPP.module.another;
    var i,j;
    function hidden() {}
    return {
        hi: function() {
            return 'hello'
        }
    }
}())

// 使用方式
MYAPP.module.amazing.hi(); // 'hello'

链式调用

返回 this 即可,但是中间出了问题,难以调试。

- Book Lists -