沙箱模式常见于YUI3 core,它是一种采用同一构造器(Constructor)生成彼此独立且互不干扰(self-contained)的实例对象,而从避免污染全局对象的方法。

命名空间

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

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

01 /* BEFORE: 5 globals */
02 // constructors
03 function Parent() {}
04 function Child() {}
05 // a variable
06 var some_var = 1;
07 // some objects
08 var module1 = {};
09 module1.data = {a: 1, b: 2};
10 var module2 = {};
11 /* AFTER: 1 global */
12 // global object
13 var MYAPP = {};
14 // constructors
15 MYAPP.Parent = function() {};
16 MYAPP.Child = function() {};
17 // a variable
18 MYAPP.some_var = 1;
19 // an object
20 MYAPP.modules = {};
21 // nested objects
22 MYAPP.modules.module1 = {};
23 MYAPP.modules.module1.data = {a: 1, b: 2};
24 MYAPP.modules.module2 = {};

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

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

  • 需要给所有需要添加的函数、变量加上前缀。
  • 因为只有一个全局对象,这意味着一部分代码可以肆意地修改全局对象而导致其余代码的被动更新。

全局构造器

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

代码清单2:沙箱的使用

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

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

  1. 创建沙箱时可以不使用'new'操作符。
  2. Sandbox()构造器接受一些额外的配置参数,这些参数定义了生成对象所需模块的名称,我们希望代码更加模块化。

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

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

代码清单3:以数组的形式传递模块名

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

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

1 Sandbox('ajax''dom'function(box){
2     // console.log(box);
3 });

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

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

1 Sandbox('*'function(box){
2     // console.log(box);
3 });
4 Sandbox(function(box){
5     // console.log(box);
6 });

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

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

01 Sandbox('dom''event'function(box){
02     // work with dom and event
03     Sandbox('ajax'function(box) {
04     // another sandboxed "box" object
05     // this "box" is not the same as
06     // the "box" outside this function
07     //...
08     // done with Ajax
09     });
10     // no trace of Ajax module here
11 });

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

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

添加模块

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

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

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

代码清单7:注册模块

01 Sandbox.modules = {};
02 Sandbox.modules.dom = function(box) {
03     box.getElement = function() {};
04     box.getStyle = function() {};
05     box.foo = "bar";
06 };
07 Sandbox.modules.event = function(box) {
08     // access to the Sandbox prototype if needed:
09     // box.constructor.prototype.m = "mmm";
10     box.attachEvent = function(){};
11     box.dettachEvent = function(){};
12 };
13 Sandbox.modules.ajax = function(box) {
14     box.makeRequest = function() {};
15     box.getResponse = function() {};
16 };

实现构造器

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

  1. 我们检查this是否为Sandbox的实例,若不是,证明Sandbox没有被new操作符调用,我们将以构造器方式重新调用它。
  2. 你可以在构造器内部为this添加属性,同样你也可以为构造器的原型添加属性。
  3. 模块名称会以数组、独立参数、通配符‘*’等多种形式传递给构造器。
  4. 请注意在这个例子中我们不需要从外部文件中加载模块,但在诸如YUI3中,你可以仅仅加载基础模块(通常被称作种子(seed)),而其他的所有模块则会从外部文件中加载。
  5. 一旦我们知道了所需的模块,并初始化他们,这意味着调用了每个模块的入口函数。
  6. 回调函数作为参数被最后传入构造器,它将使用最新生成的实例并在最后执行。

代码清单8:实现Sandbox构造器

01 <script>
02 function Sandbox() {
03     // turning arguments into an array
04     var args = Array.prototype.slice.call(arguments),
05     // the last argument is the callback
06     callback = args.pop(),
07     // modules can be passed as an array or as individual parameters
08     modules = (args[0] && typeof args[0] === "string") ?
09     args : args[0],
10     i;
11     // make sure the function is called
12     // as a constructor
13     if (!(this instanceof Sandbox)) {
14         return new Sandbox(modules, callback);
15     }
16     // add properties to 'this' as needed:
17     this.a = 1;
18     this.b = 2;
19     // now add modules to the core 'this' object
20     // no modules or "*" both mean "use all modules"
21     if (!modules || modules === '*') {
22         modules = [];
23             for (i in Sandbox.modules) {
24                 if (Sandbox.modules.hasOwnProperty(i)) {
25                     modules.push(i);
26             }
27         }
28     }
29     // init the required modules
30     for (i = 0; i < modules.length; i++) {
31         Sandbox.modules[modules[i]](this);
32     }
33     // call the callback
34     callback(this);
35 }
36 // any prototype properties as needed
37 Sandbox.prototype = {
38     name: "My Application",
39     version: "1.0",
40     getName: function() {
41         return this.name;
42     }
43 };
44 </script>

JavaScript SandBox沙箱设计模式的更多相关文章

  1. 一种JavaScript 类的设计模式

    一种JavaScript 类的设计模式尽管前面介绍了如何定义一个类,如何初始化一个类的实例,但既可以在function定义的函数体中添加成员,又可以用prototype 定义类的成员,代码显的很混乱, ...

  2. JavaScript 中常见设计模式整理

    开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式.本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知. JavaScript 中常见设计模 ...

  3. Javascript常用的设计模式详解

    Javascript常用的设计模式详解 阅读目录 一:理解工厂模式 二:理解单体模式 三:理解模块模式 四:理解代理模式 五:理解职责链模式 六:命令模式的理解: 七:模板方法模式 八:理解javas ...

  4. 关于JavaScript的沙箱模式

    从语言学的角度上来说,允许代码无节制地使用全局变量,是最错误的选择之一.而更可怕的,就是一个变量"可能"成为全局的(在未知的时间与地点).但是这两项,却伴随JavaScript这门 ...

  5. 关于javascript的沙箱模式以及缓存方法

    在javascript函数代码中,经常会不经意出现全局变量,很可能造成对全局对象的污染,由于这种弊端的存在,那么沙箱模式油然而生.沙箱模式又称为沙盒模式.隔离模式.在js中只有函数可以限定变量作用域, ...

  6. javascript学习(9)——[设计模式]单例

    单例模式,相信大家对此都不陌生,我们主要讲下javascript中几个比较常见的设计模式: (1).普通的单体 (2).具有局部变量的强大单体 (3).惰性单体 (4).分支单体 下面我们就一一进行介 ...

  7. GOF提出的23种设计模式是哪些 设计模式有创建形、行为形、结构形三种类别 常用的Javascript中常用设计模式的其中17种 详解设计模式六大原则

    20151218mark 延伸扩展: -设计模式在很多语言PHP.JAVA.C#.C++.JS等都有各自的使用,但原理是相同的,比如JS常用的Javascript设计模式 -详解设计模式六大原则 设计 ...

  8. javascript 对象的设计模式

    1.为什么学习设计模式:http://www.iteye.com/news/32092   或  https://blog.csdn.net/pigpigpig4587/article/details ...

  9. 写的一个轻量级javascript框架的设计模式

    公司一直使用jQuery框架,一些小的项目还是觉得jQuery框架太过于强大了,于是自己周末有空琢磨着写个自己的框架.谈到js的设计模式,不得不说说js的类继承机制,javascript不同于PHP可 ...

随机推荐

  1. linux命令(实用!)

    本文转载自网络 1.1 shell家族 shell:命令解释器,根据输入的命令执行相应命令. 察看当前系统下有哪些shell: cat /etc/shells 察看当前系统正在使用的shell ech ...

  2. Linux命令应用大词典-第37章 Linux系统故障排错

    37.1 mkbootdisk:创建用于运行系统的独立启动软盘 37.2 chroot:切换根目录环境 37.3 badblocks:搜索设备的坏块 37.4 mkinitrd:创建要载入ramdis ...

  3. C 基本运算

    一 算术运算 C语言一共有34种运算符 包括了常见的加减乘除运算 1. 加法运算+ 除开能做加法运算 还能表示正号: +5, +90 2. 减法运算- 除开能做减法运算 还能表示符号: -10, -2 ...

  4. tpo-09 C1 Advice on a term paper's topic

    第 1 段 1.Listen to a conversation between a student and her professor. 请听一段一名学生和教授讨论的对话. 第 2 段 1.Befo ...

  5. 域名、IP地址、URL关系

    域名是个文字形式记录的IP地址 IP地址是计算机在网络中的门牌号! URL是网页地址 例如1: http://zhidao.baidu.com/question/14674128.html 是URL ...

  6. Redis命令续

     Redis 集合命令 下表列出了 Redis 集合基本命令: 序号 命令及描述 1 SADD key member1 [member2] 向集合添加一个或多个成员 2 SCARD key 获取集合的 ...

  7. git revert 与 git reset

    Git版本回滚之 git revert 与 git reset 在使用 git 的时候,如果错误push之后,经常会回滚版本. git的回滚有两种方式: revert命令:这种方式,是用一种反向的 p ...

  8. cygwin—excellent work!

    使用cygwin的好处在于可以避免直接使用linux同时又能最大限度的节省资源,共享windows的资源. 安装cygwin 安装安简单,当然,你首先需要使用163或者国内或者亚洲比较好的镜像作为下载 ...

  9. LeetCode - 389. Find the Difference - 三种不同解法 - ( C++ ) - 解题报告

    1.题目大意 Given two strings s and t which consist of only lowercase letters. String t is generated by r ...

  10. [C++] Fucntions

    Statements A break statements terminate the nearest wile, do while, for or switch statement. A break ...