JavaScript 设计模式 安全沙箱模式


Posted in Javascript onSeptember 24, 2010

命名空间

JavaScript本身中没有提供命名空间机制,所以为了避免不同函数、对象以及变量名对全局空间的污染,通常的做法是为你的应用程序或者库创建一个唯一的全局对象,然后将所有方法与属性添加到这个对象上。

/* BEFORE: 5 globals */ 
// constructors 
function Parent() {} 
function Child() {} 
// a variable 
var some_var = 1; 
// some objects 
var module1 = {}; 
module1.data = {a: 1, b: 2}; 
var module2 = {}; 
/* AFTER: 1 global */ 
// global object 
var MYAPP = {}; 
// constructors 
MYAPP.Parent = function() {}; 
MYAPP.Child = function() {}; 
// a variable 
MYAPP.some_var = 1; 
// an object 
MYAPP.modules = {}; 
// nested objects 
MYAPP.modules.module1 = {}; 
MYAPP.modules.module1.data = {a: 1, b: 2}; 
MYAPP.modules.module2 = {};

代码清单1 : 传统命名空间模式

在这段代码中,你创建了一个全局对象MYAPP,并将其他所有对象、函数作为属性附加到MYAPP上.

通常这是一种较好的避免命名冲突的方法,它被应用在很多项目中,但这种方法有一些缺点。

1.需要给所有需要添加的函数、变量加上前缀。

2.因为只有一个全局对象,这意味着一部分代码可以肆意地修改全局对象而导致其余代码的被动更新。

全局构造器

你可以用一个全局构造器,而不是一个全局对象,我们给这个构造器起名为Sandbox(),你可以用这个构造器创建对象,你还可以为构造器传递一个回调函数作为参数,这个回调函数就是你存放代码的独立沙箱环境。

new Sandbox(function(box){ 
 // your code here... 
});

代码清单2:沙箱的使用

让我们给沙箱添加点别的特性

1.创建沙箱时可以不使用'new'操作符

2.Sandbox()构造器接受一些额外的配置参数,这些参数定义了生成对象所需模块的名称,我们希望代码更加模块化。

拥有了以上特性后,让我们看看怎样初始化一个对象。

代码清单3显示了你可以在不需要‘new'操作符的情况下,创建一个调用了'ajax'和'event'模块的对象.

Sandbox(['ajax', 'event'], function(box){ 
 // console.log(box); 
});

代码清单3:以数组的形式传递模块名
Sandbox('ajax', 'dom', function(box){ 
 // console.log(box); 
});

代码清单4:以独立的参数形式传递模块名

代码清单5显示了你可以把通配符'*'作为参数传递给构造器,这意味着调用所有可用的模块,为了方便起见,如果没有向构造器传递任何模块名作为参数,构造器会把'*'作为缺省参数传入.

Sandbox('*', function(box){ 
 // console.log(box); 
}); 
Sandbox(function(box){ 

 // console.log(box); 
});

代码清单5:调用所用可用模块

代码清单6显示你可以初始化沙箱对象多次,甚至你可以嵌套它们,而不用担心彼此间会产生任何冲突.

Sandbox('dom', 'event', function(box){ 
// work with dom and event 
 Sandbox('ajax', function(box) { 

 // another sandboxed "box" object 

// this "box" is not the same as 

// the "box" outside this function 

//... 

// done with Ajax 

 }); 
// no trace of Ajax module here 
});

代码清单6:嵌套的沙箱实例

从上面这些示例可以看出,使用沙箱模式,通过把所有代码逻辑包裹在一个回调函数中,你根据所需模块的不同生成不同的实例,而这些实例彼此互不干扰独立的工作着,从而保护了全局命名空间。

现在让我们看看怎样实现这个Sandbox()构造器.

添加模块

在实现主构造器之前,让我们看看如何向Sandbox()构造器中添加模块。

因为Sandbox()构造器函数也是对象,所以你可以给它添加一个名为'modules'的属性,这个属性将是一个包含一组键值对的对象,其中每对键值对中Key是需要注册的模块名,而Value则是该模块的入口函数,当构造器初始化时当前实例会作为第一个参数传递给入口函数,这样入口函数就能为该实例添加额外的属性与方法。

在代码清单7中,我们添加了'dom','event','ajax'模块。

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

box.getStyle = function() {}; 

box.foo = "bar"; 
}; 
Sandbox.modules.event = function(box) { 
// access to the Sandbox prototype if needed: 
// box.constructor.prototype.m = "mmm"; 

 box.attachEvent = function(){}; 

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

box.makeRequest = function() {}; 

box.getResponse = function() {}; 
};

代码清单7:注册模块

实现构造器

代码清单8描述了实现构造器的方法,其中关键的几个要点:

1.我们检查this是否为Sandbox的实例,若不是,证明Sandbox没有被new操作符调用,我们将以构造器方式重新调用它。

2.你可以在构造器内部为this添加属性,同样你也可以为构造器的原型添加属性。

3.模块名称会以数组、独立参数、通配符‘*'等多种形式传递给构造器。

4.请注意在这个例子中我们不需要从外部文件中加载模块,但在诸如YUI3中,你可以仅仅加载基础模块(通常被称作种子(seed)),而其他的所有模块则会从外部文件中加载。

5.一旦我们知道了所需的模块,并初始化他们,这意味着调用了每个模块的入口函数。

6.回调函数作为参数被最后传入构造器,它将使用最新生成的实例并在最后执行。

function Sandbox() { 
 // turning arguments into an array 

 var args = Array.prototype.slice.call(arguments), 

 // the last argument is the callback 


 callback = args.pop(), 

 // modules can be passed as an array or as individual parameters 


 modules = (args[0] && typeof args[0] === "string") ? 


args : args[0], 


i; 


 // make sure the function is called 


// as a constructor 

 if (!(this instanceof Sandbox)) { 


 return new Sandbox(modules, callback); 

} 

// add properties to 'this' as needed: 

this.a = 1; 

this.b = 2; 

// now add modules to the core 'this' object 

// no modules or "*" both mean "use all modules" 

if (!modules || modules === '*') { 


modules = []; 


for (i in Sandbox.modules) { 



if (Sandbox.modules.hasOwnProperty(i)) { 




modules.push(i); 



} 


 } 

 } 

// init the required modules 

for (i = 0; i < modules.length; i++) { 


Sandbox.modules[modules[i]](this); 

} 

// call the callback 

callback(this); 
} 
// any prototype properties as needed 
Sandbox.prototype = { 

name: "My Application", 

version: "1.0", 

getName: function() { 


return this.name; 

} 
};

代码清单8:实现Sandbox构造器
原文来自:Stoyan Stefanov - JavaScript Patterns Part 7:The Sandbox Pattern
Javascript 相关文章推荐
getElementById在任意一款浏览器中都可以用吗的疑问回复
May 13 Javascript
关于JavaScript中string 的replace
Apr 12 Javascript
jquery获得keycode的示例代码
Dec 30 Javascript
js获取url中&quot;?&quot;后面的字串方法
May 15 Javascript
Javascript基础教程之break和continue语句
Jan 18 Javascript
完美JQuery图片切换效果的简单实现
Jul 21 Javascript
js原生跨域_用script标签的简单实现
Sep 24 Javascript
详谈jQuery Ajax(load,post,get,ajax)的用法
Mar 02 Javascript
JS组件系列之JS组件封装过程详解
Apr 28 Javascript
ztree实现权限横向显示功能
May 20 Javascript
解决Jquery下拉框数据动态获取的问题
Jan 25 jQuery
解决vue-router路由拦截造成死循环问题
Aug 05 Javascript
IE无法设置短域名下Cookie
Sep 23 #Javascript
Javascript中获取出错代码所在文件及行数的代码
Sep 23 #Javascript
基于JQuery的一个简单的鼠标跟随提示效果
Sep 23 #Javascript
用js模拟JQuery的show与hide动画函数代码
Sep 20 #Javascript
通过DOM脚本去设置样式信息
Sep 19 #Javascript
javscript对象原型的一些看法
Sep 19 #Javascript
Ext 今日学习总结
Sep 19 #Javascript
You might like
深入理解PHP原理之Session Gc的一个小概率Notice
2011/04/12 PHP
php 字符串中的\n换行符无效、不能换行的解决方法
2014/04/02 PHP
php更新mysql后获取改变行数的方法
2014/12/25 PHP
总结PHP如何获取当前主机、域名、网址、路径、端口和参数等
2016/09/09 PHP
总结一些PHP中好用但又容易忽略的小知识
2017/06/02 PHP
在laravel中使用Symfony的Crawler组件分析HTML
2017/06/19 PHP
Laravel框架实现model层的增删改查(CURD)操作示例
2018/05/12 PHP
laravel框架创建授权策略实例分析
2019/11/22 PHP
flexigrid 类似ext grid的JS表格代码
2010/07/17 Javascript
增强用户体验友好性之jquery easyui window 窗口关闭时的提示
2012/06/22 Javascript
js写一个字符串转成驼峰的实例
2013/06/21 Javascript
Javascript玩转继承(二)
2014/05/08 Javascript
Struts2+jquery.form.js实现图片与文件上传的方法
2016/05/05 Javascript
1秒50万字!js实现关键词匹配
2016/08/01 Javascript
关于动态执行代码(js的Eval)实例详解
2016/08/15 Javascript
jQuery判断邮箱格式对错实例代码讲解
2017/04/12 jQuery
微信小程序拖拽排序列表的示例代码
2020/07/08 Javascript
python字符串过滤性能比较5种方法
2017/06/22 Python
python学习之matplotlib绘制散点图实例
2017/12/09 Python
对Tensorflow中的变量初始化函数详解
2018/07/27 Python
python将txt文件读入为np.array的方法
2018/10/30 Python
python通过TimedRotatingFileHandler按时间切割日志
2019/07/17 Python
python IDLE添加行号显示教程
2020/04/25 Python
django 数据库返回queryset实现封装为字典
2020/05/19 Python
Guess欧洲官网:美国服饰品牌
2019/08/06 全球购物
联想阿根廷官方网站:Lenovo Argentina
2019/10/14 全球购物
马来西亚网上花店:FlowerAdvisor马来西亚
2020/01/03 全球购物
泰国Robinson百货官网:购买知名品牌的商品
2020/02/08 全球购物
Abbott Lyon官网:女士手表、珠宝及配件
2020/12/26 全球购物
女娲补天教学反思
2014/02/05 职场文书
大堂副理的岗位职责范文
2014/02/17 职场文书
超市促销活动方案
2014/03/05 职场文书
护理目标管理责任书
2014/07/25 职场文书
广播体操比赛主持词
2015/06/29 职场文书
为什么node.js不适合大型项目
2021/04/28 Javascript
Go 在 MongoDB 中常用查询与修改的操作
2021/05/07 Golang