JavaScript 设计模式 安全沙箱模式

命名空间

  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

(0)

相关推荐

  • JavaScript 设计模式之组合模式解析

    怎么说呢?!就像是动物(组合对象)一样,当它生下后代(叶对象)时,它的后代就有了某种功能(比如:挖洞,听力好等等):也像是一棵树,它有一个根(组合对象)然后是从这个棵树向外冒出的其他枝杆(组合对象)以及从这些枝杆又向外长的叶子(叶对象).换句话说,就是当祖先已经有了,那么只要从这个祖先衍生出来的其他孩子(包括这个祖先下的其他组合对象)已经就具备了某种功能,看上去貌似又有些像是继承."组合模式"在组合对象的层次体系中有两种类型的对象:叶对象和组合对象.组合模式擅长于对大批对象进行操作.

  • javascript设计模式之解释器模式详解

    神马是"解释器模式"? 先翻开<GOF>看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如何创建一个抽象语法树.它不涉及语法分析.抽象语法树可用一个表驱动的语法分析程序来完成,也可用手写的(通常为递归下降法)语法分析程序创建,或直接client提供. 解析器: 指的是把描述客户端调用要求的表达式,经过解析,形成一个抽象语法树的程序. 解

  • JavaScript 设计模式学习 Factory

    复制代码 代码如下: /* DisplayModule interface. */ var DisplayModule = new Interface('DisplayModule', ['append', 'remove', 'clear']); /* ListDisplay class. */ //通过接口实现工厂,这是通过List方式显示RSS var ListDisplay = function(id, parent) { // implements DisplayModule this

  • JavaScript设计模式之观察者模式(发布者-订阅者模式)

    观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式. 复制代码 代码如下: div.onclick  =  function click (){ alert ( "click' ) } 只要订阅了div的click事件. 当点击div的时候, function click就会被触发. 那么到底什么是观察者模式呢. 先看看生活中的观察者模式. 好莱坞有句名言. "不要给我

  • 学习JavaScript设计模式(链式调用)

    1.什么是链式调用 这个很容易理解,例如: $(this).setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:调用完方法后,return this返回当前调用方法的对象. function Dog(){ this.run= function(){ alert("The dog is running...."); return this;//返回当前对象 Dog }; this.eat= function(){ alert("Af

  • JavaScript 设计模式学习 Singleton

    复制代码 代码如下: /* Basic Singleton. */ var Singleton = { attribute1: true, attribute2: 10, method1: function() { }, method2: function(arg) { } }; 单件模式最主要的用途之一就是命名空间: /* GiantCorp namespace. */ var GiantCorp = {}; GiantCorp.Common = { // A singleton with c

  • JavaScript设计模式之工厂模式和构造器模式

    什么是模式 前阵子准备期末考试,劳神又伤身的,实在闲不得空来更新文章,今天和大家说说javascript中的设计模式. 首先呢,我们需要知道的是:模式是一种可复用的解决方案,而反模式呢就是针对某个问题的不良解决方案. js反模式常见例子 1.向setTimeout和setInterval传递字符串,而不是函数,这会触发eval()的内部使用. 2.在全局上下文中定义大量的变量污染全局命名空间 3.修改Object类的原型 4.以内联形式使用js,嵌入在HTML文件中的js代码是无法包含在外部单元

  • JavaScript编程设计模式之构造器模式实例分析

    本文实例讲述了JavaScript编程设计模式之构造器模式.分享给大家供大家参考,具体如下: 经典的OOP语言中,构造器(也叫构造函数)是一个用于初始化对象的特殊方法.在JS中,因为一切皆对象,对象构造器经常被提起. 对象构造器用于建立制定类型(Class)的对象,可以接受参数用于初始化对象的属性和方法. 对象建立 在JS中,有三个常用的方法用于建立对象: //1, 推荐使用 var newObject = {}; //2, var newObject = Object.create( null

  • 常用的javascript设计模式

    阅读目录 什么是设计模式 单体模式: 工厂模式: 单例模式 观察者模式(发布订阅模式) 策略模式 模板模式 代理模式 外观模式 设计模式太多了,貌似有23种,其实我们在平时的工作中没有必要特意去用什么样的设计模式,或者你在不经意间就已经用了设计模式当中的一种.本文旨在总结平时相对来说用的比较多的设计模式. 什么是设计模式 百度百科: 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用设计模式是为了可重用代码.让代码更容易被他人理解.

  • JavaScript设计模式之原型模式(Object.create与prototype)介绍

    原型模式说明 说明:使用原型实例来 拷贝 创建新的可定制的对象:新建的对象,不需要知道原对象创建的具体过程: 过程:Prototype => new ProtoExam => clone to new Object; 使用相关代码: 复制代码 代码如下: function Prototype() {     this.name = '';     this.age = '';     this.sex = ''; } Prototype.prototype.userInfo = functio

  • JavaScript设计模式之单例模式实例

    <Practical Common Lisp>的作者 Peter Seibel 曾说,如果你需要一种模式,那一定是哪里出了问题.他所说的问题是指因为语言的天生缺陷,不得不去寻求和总结一种通用的解决方案. 不管是弱类型或强类型,静态或动态语言,命令式或说明式语言.每种语言都有天生的优缺点.一个牙买加运动员, 在短跑甚至拳击方面有一些优势,在练瑜伽上就欠缺一些. 术士和暗影牧师很容易成为一个出色的辅助,而一个背着梅肯满地图飞的敌法就会略显尴尬. 换到程序中, 静态语言里可能需要花很多功夫来实现装饰

随机推荐