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 相关文章推荐
javascript 程序库的比较(一)之DOM功能
Apr 07 Javascript
JQuery操作元素的css样式
Mar 09 Javascript
JavaScript中Number.MAX_VALUE属性的使用方法
Jun 04 Javascript
javascript图片延迟加载实现方法及思路
Dec 31 Javascript
JavaScript数据推送Comet技术详解
Apr 07 Javascript
AngularJS控制器之间的通信方式详解
Nov 03 Javascript
Vue.js组件tab实现选项卡切换
Mar 23 Javascript
Bootstrap模态对话框用法简单示例
Aug 31 Javascript
基于Vue2-Calendar改进的日历组件(含中文使用说明)
Apr 14 Javascript
微信小程序中使用echarts的实现方法
Apr 24 Javascript
微信小程序JS加载esmap地图的实例详解
Sep 04 Javascript
layui实现把数据表格时间戳转换为时间格式的例子
Sep 12 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 strtr() 函数使用说明
2008/11/21 PHP
php源码加密 仿微盾PHP加密专家(PHPCodeLock)
2010/05/06 PHP
轻松掌握php设计模式之访问者模式
2016/09/23 PHP
php原生导出excel文件的两种方法(推荐)
2016/11/19 PHP
PHP实现git部署的方法教程
2017/12/19 PHP
javascript options属性集合操作代码
2009/12/28 Javascript
jQuery EasyUI API 中文文档 可调整尺寸
2011/09/29 Javascript
JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性
2012/10/11 Javascript
JavaScript异步编程:异步数据收集的具体方法
2013/08/19 Javascript
一个简单的JS时间控件示例代码(JS时分秒时间控件)
2013/11/22 Javascript
Array 重排序方法和操作方法的简单实例
2014/01/24 Javascript
基于jQuery实现的文字按钮表单特效整理
2014/12/07 Javascript
前端jquery部分很精彩
2016/05/03 Javascript
详解javascript事件绑定使用方法
2016/10/20 Javascript
完美解决jQuery 鼠标快速滑过后,会执行多次滑出的问题
2016/12/08 Javascript
详解js树形控件—zTree使用总结
2016/12/28 Javascript
JavaScript验证知识整理
2017/03/24 Javascript
纯js实现的积木(div层)拖动功能示例
2017/07/19 Javascript
高性能的javascript之加载顺序与执行原理篇
2018/01/14 Javascript
详解ES6 系列之异步处理实战
2018/10/26 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
python实现在每个独立进程中运行一个函数的方法
2015/04/23 Python
Python中处理字符串之endswith()方法的使用简介
2015/05/18 Python
python 接口测试response返回数据对比的方法
2018/02/11 Python
创建Django项目图文实例详解
2019/06/06 Python
python中的函数递归和迭代原理解析
2019/11/14 Python
Python变量、数据类型、数据类型转换相关函数用法实例详解
2020/01/09 Python
Centos7下源码安装Python3 及shell 脚本自动安装Python3的教程
2020/03/07 Python
澳洲Chemist Direct药房中文网:澳洲大型线上直邮药房
2019/11/04 全球购物
Tuckernuck官网:经典的美国品质服装、鞋子和配饰
2021/01/11 全球购物
介绍一下结构化程序设计方法和面向对象程序设计方法的区别
2012/06/27 面试题
小学校园活动策划
2014/01/30 职场文书
奥林匹克的口号
2014/06/13 职场文书
2014幼儿园家长工作总结
2014/11/10 职场文书
入党转正介绍人意见
2015/06/03 职场文书
2019年鼓励无偿献血倡议书
2019/09/17 职场文书