摘要:创建全局对象构造函数一个变量一个对象容器嵌套对象命名约定通常以全部大写的方式来命名这个全局对象。在命名模式中,有一个全局对象,在沙箱模式中,有一个全局构造函数,我们这里命名为。
在javascript中,我们知道可以使用对象字面量或者构造函数创建对象,但是如何优雅地创建一个对象你却不一定了解。
前人在踩过无数坑又填过无数坑之后,给我们总结了不同场景下的几种对象创建模式:
命名空间模
模块模式
沙箱模式
链模式
命名模式有这样一种场景,假如你正在写一个插件,这个插件内部会用到很多的全局变量,这时候你要怎么保证你的变量不会与其他插件的变量产生命名冲突呢?我们知道,在javascript中并没有内置命名空间,后面的变量会覆盖掉前面的变量,你要如何避免这种问题的发生?
可能你已经想到,只要足够独特的命名就可以了吧,比如lynneShowVar,这样确实是可以的,但是如果变量很多,难道要为每个都起一个很独特的命名?是不是很累?而且维护也是很麻烦的。
命名模式提出,为应用程序创建一个全局对象,比如MYAPP,然后将所有的变量和函数挂到这个全局对象(MYAPP)的属性上。
//创建全局对象 var MYAPP = {}; //构造函数 MYAPP.parent = function () {}; MYAPP.child = function () {}; //一个变量 MYAPP.some_var = 1; //一个对象容器 MYAPP.modules = {} //嵌套对象 MYAPP.modules.module1 = {}; MYAPP.modules.module2 = {};
命名约定:通常以全部大写的方式来命名这个全局对象。
优点:
避免代码中的命名冲突
避免代码与第三方的命名冲突
缺点
需要输入更多字符
任何部分的代码都可以修改全局实例
嵌套的额数字意味着更长的属性查询解析。
那么,有什么办法可以避免这些缺陷么?声明代码依赖关系应该是一个不错的主意了。
声明依赖关系
var myFunction = function () { //依赖 var event = MYAPP.util.event, dom = MYAPP.util.dom; //... }
优点:
可读性强
解析局部变量的速度总是比解析全局变量的速度快
减小代码量
通用命名空间
随着程序的复杂度的增加,如何保证全局对象上新添属性不会覆盖原有的属性呢。这就需要每次在添加新属性之前先检查它是否已经存在了。
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; } };
优点
实现模块化,解决 同一个页面不能 使用同一个库的不同版本的问题
优化了以点分割的名字的解析时间,如: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对象,这就是实现链式的原理。
优点
节省输入的字符
有助于分割函数,提高代码的维护性
缺点
难以调试,一旦出现问题,难以定位到具体步骤
每种设计模式都各有优缺点,具体使用哪种模式还得看项目需求,大家一起学习吧。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/85191.html
摘要:都是构造函数模式创建的原生构造函数。使用构造函数创建对象经历了以下四个过程创建一个新对象构造函数的作用域交给新对象。 在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式。但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界上(程序中)的所有使用到的对象都使用一遍 var xxx = {} ,...
摘要:都是构造函数模式创建的原生构造函数。使用构造函数创建对象经历了以下四个过程创建一个新对象构造函数的作用域交给新对象。 在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式。但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界上(程序中)的所有使用到的对象都使用一遍 var xxx = {} ,...
摘要:停更许久,近期计划更新设计模式系列。单例模式是创建型设计模式的一种。虽然它不是正规的单例模式,但不可否认确实具备类单例模式的特点。适用场景单例模式的特点,意图解决维护一个全局实例对象。 停更许久,近期计划更新:设计模式系列。 showImg(https://segmentfault.com/img/bVbt7uw?w=800&h=600); 单例模式:限制类实例化次数只能一次,一个类只...
摘要:但是,这并不是采用单例的唯一原因。使用命名空间单例模式也被称为模块设计模式。函数内部声明了一些局部函数和或变量。紧随函数声明放置即可立即执行外部函数,并将所得的对象文字费赔给变量。 JavaScript设计模式-第一部分:单例模式、组合模式和外观模式 设计模式是一些可靠的编程方式,有助于保证代码更加易于维护、扩展及分离,所有设计模式在创建大型JavaScript应用程序时均不可或缺 单...
摘要:此时我们创建的对象内保存静态变量通过取值器访问,最后将这个对象作为一个单例放在全局空间里面作为静态变量单例对象供他人使用。 单例模式 又被称为单体模式,是只允许实例化一次的对象类。有时我们也用一个对象来规划一个命名空间,井井有条的管理对象上面的属性和方法。 传统的面向对象语言中单例模式的实现,均是单例对象从类中创建而来,在以类为中心的语言中,这是很常见的做法。如果需要某个对象,就必须先...
摘要:三种使用构造函数创建对象的方法和的作用都是在某个特殊对象的作用域中调用函数。这种方式还支持向构造函数传递参数。叫法上把函数叫做构造函数,其他无区别适用情境可以在特殊的情况下用来为对象创建构造函数。 一、工厂模式 工厂模式:使用字面量和object构造函数会有很多重复代码,在此基础上改进showImg(https://segmentfault.com/img/bVbmKxb?w=456&...
阅读 2872·2021-10-19 10:09
阅读 3084·2021-10-09 09:41
阅读 3322·2021-09-26 09:47
阅读 2580·2019-08-30 15:56
阅读 562·2019-08-29 17:04
阅读 949·2019-08-26 11:58
阅读 2467·2019-08-26 11:51
阅读 3282·2019-08-26 11:29