javascript的对象建立模式

在javascript中,咱们知道可使用对象字面量或者构造函数建立对象,可是如何优雅地建立一个对象你却不必定了解。javascript

前人在踩过无数坑又填过无数坑以后,给咱们总结了不一样场景下的几种对象建立模式:java

  • 命名空间模
  • 模块模式
  • 沙箱模式
  • 链模式

命名模式

有这样一种场景,假如你正在写一个插件,这个插件内部会用到不少的全局变量,这时候你要怎么保证你的变量不会与其余插件的变量产生命名冲突呢?咱们知道,在javascript中并无内置命名空间,后面的变量会覆盖掉前面的变量,你要如何避免这种问题的发生?ajax

可能你已经想到,只要足够独特的命名就能够了吧,好比lynneShowVar,这样确实是能够的,可是若是变量不少,难道要为每一个都起一个很独特的命名?是否是很累?并且维护也是很麻烦的。设计模式

命名模式提出,为应用程序建立一个全局对象,好比MYAPP,而后将全部的变量和函数挂到这个全局对象(MYAPP)的属性上。数组

//建立全局对象
var MYAPP = {};

//构造函数
MYAPP.parent = function () {};
MYAPP.child = function () {};

//一个变量
MYAPP.some_var = 1;

//一个对象容器
MYAPP.modules = {}

//嵌套对象
MYAPP.modules.module1 = {};
MYAPP.modules.module2 = {};

命名约定:一般以所有大写的方式来命名这个全局对象。闭包

优势:app

  1. 避免代码中的命名冲突
  2. 避免代码与第三方的命名冲突

缺点dom

  1. 须要输入更多字符
  2. 任何部分的代码均可以修改全局实例
  3. 嵌套的额数字意味着更长的属性查询解析。

那么,有什么办法能够避免这些缺陷么?声明代码依赖关系应该是一个不错的主意了。模块化

声明依赖关系函数

var myFunction = function () {
    //依赖
    var event = MYAPP.util.event,
        dom  = MYAPP.util.dom;
    
    //...
}

优势:

  1. 可读性强
  2. 解析局部变量的速度老是比解析全局变量的速度快
  3. 减少代码量

通用命名空间

随着程序的复杂度的增长,如何保证全局对象上新添属性不会覆盖原有的属性呢。这就须要每次在添加新属性以前先检查它是否已经存在了。

var MYAPP = MYAPP || {};

MYAPP.name = function (ns_string) {
    var parts = ns_string.split('.'),
        parent = MYAPP,
        i;
    
    //剥离最前面的冗余全局变量
    if (parts[0] === 'MYAPP') {
        parts = parts.slice(1);
    }
    
    for(i = 0; i < parts.length; i++) {
        //若是不存在就建立一个属性
        if (typeof parent[parts[i]] === 'undefined') {
            parent[parts[i]] = {};
        }
        parent = parent[parts[i]];
    }
    return parent;
    
}

命名模式虽然好用,但仍是有个问题,那就是任何部分的代码均可以修改全局实例,以及里面的属性,怎么避免?咱们知道es5并无类的概念,若是要实现私有成员,可使用闭包来 模拟这种特性。

模块模式

其实模块模式是多种模式的组合

  • 命名空间
  • 即时函数
  • 私有和特权函数
  • 声明依赖
MYAPP.ntilities.array = (function () {
    //依赖
    var uobj = MYAPP.ntilities.object,
        ulang = MYAPP.ntilities.lang;
    //私有属性
    var array_string = '[object array]',
        ops = Object.prototype.toString;
    //私有方法
    //...
    
    //其它
    
    //共有API
    return {
        inArray: function (needle, haystack) { ... },
        isArray: function (str) { ... }
        //更多...
    }
})()

将全局变量导入到模块中

能够将参数传递到包装了模块的即时函数中,有助于加速即时函数中全局符号的解析。

MYAPP.ntilities.array = (function (app, global) {
}(MYAPP, this);

沙箱模式

假设须要在同一个页面运行同一个库的两个版本,很遗憾,前面两种建立模式都是不支持的。这时候就须要模块化。

在命名模式中,有一个全局对象 ,在沙箱模式中,有一个全局构造函数,咱们这里命名为Sandbox()。这个构造函数能够接收一个或多个参数,这些参数指定对象实例须要的模块名,以及一个回调函数。如:

//模块名可使用数组的形式传参
Sandbox(['ajax', 'dom'], function () { //这里是回调函数 });

//模块名也可使用单个参数的形式传参,参数之间使用,号隔开
Sandbox('ajax', 'dom', function () { //这里是回调函数 });

//不指定模块名称或指定'*',表示须要依赖全部模块
Sandbox(function () { //这里是回调函数 });
Sandbox('*', function () { //这里是回调函数 });

在实现实际的构造函数以前,先为Sandbox添加一个名为modules的静态属性,这须要理解的是Sandbox同时也是一个对象。

Sandbox.modules = {}

把须要的模块添加到Sandbox.modules对象中。假设咱们一共须要dom、ajax、event模块

Sandbox.modules.dom = function(box) {
    box.getElement = function () {};
};

Sandbox.modules.ajax = function(box) {
    box.getResponse = function () {};
};

Sandbox.modules.event = function(box) {
    box.attachEvent = function () {};
};

接下来实现构造函数

function Sandbox = function () {
    //将参数转换成一个数组
    var args = Array.prototype.slice.call(arguments),
        //最后一个参数是回调函数
        callback = args.pop(),
        //args[0]若是是String类型,说明模块做为单独参数传递,不然模块做为数组形式传递
        modules = (args[0] && typeof args[0] === 'String') ? args : arg[0],
        i;
        
    //确保该函数做为构造函数调用
    if(!(this instanceof Sandbox)) {
        return new Sandbox (modules, callback);
    }
    
    //须要向this添加的属性,也可能没有,这里看实际项目需求
    this.a = 1;
    this.b = 2;   
    
    //如今向该核心'this'对象添加模块
    //不指定模块名称或指定'*',都表示制定全部模块
    if (!modules || modules === '*') {
        modules = [];
        for (i in Sandbox.modules) {
            modules.push[i];
        }
    }
    
    //初始化所需的模块
    for(i = 0; i < modules.lenght; i++) {
        Sandbox.modules[modules[i]](this)
    }
    
    //依赖模块已所有初始化,能够执行回调函数
    callback();
}

//获取你还须要添加一些原型属性,看需求
Sanndbox.prototype = {
    name: 'MY Application',
    version: '1.0',
    getName: function () {
        return this.name;
    }
};

优势

  1. 实现模块化,解决 同一个页面不能 使用同一个库的不一样版本的问题
  2. 优化了以点分割的名字的解析时间,如:MYAPP.untilities.array

链式模式

链式模式可使您可以一个接一个地调用对象的方法,而无需将前一个操做返回的值付给变量,且无需分割成多行。如:

myobj.method1('hello').method2().method3('str');

这个很好理解,咱们直接来看一看代码

var obj = {
    al: 1,
    add: function (v) {
        this.val += v;
        return this;
    },
    set: function (v) {
        this.val = v;
        return this;
    },
    shout: function (v) {
        console.log(this.val);
    }
};


//链方法调用
obj.add(1).set(2).shout();

能够看到,obj的每一个方法都返回this对象,这就是实现链式的原理。

优势

  1. 节省输入的字符
  2. 有助于分割函数,提升代码的维护性

缺点

  1. 难以调试,一旦出现问题,难以定位到具体步骤

每种设计模式都各有优缺点,具体使用哪一种模式还得看项目需求,你们一块儿学习吧。

相关文章
相关标签/搜索