【转】对象创建模式
原帖:http://www.douban.com/note/169097922/
1. 命名空间模式
// unsafe
var MYAPP = {};
// better
if (typeof MYAPP === "undefined") {
var MYAPP = {};
}
// or shorter
var MYAPP = MYAPP || {};
//一次性创建长命名层级时
//确保每层都存在
MYAPP.namespace = function (ns_string) {
var parts = ns_string.split('.'),
parent = MYAPP,
i;
// strip redundant leading global
if (parts[0] === "MYAPP") {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if (typeof parent[parts[i] === "undefined") {
parent[parts[i] = {};
}
parent = parent[parts[i];
}
return parent;
};
// assign returned value to a local var
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true
// skip initial `MYAPP`MYAPP.namespace('modules.module51');
// long namespace
MYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property');
2. 声明依赖 -----------------------------------------------------------------
var myFunction = function () {
// dependencies
var event = YAHOO.util.Event,
dom = YAHOO.util.Dom;
// use event and dom variables
// for the rest of the function...
};
或
function test2() {
var modules = MYAPP.modules;
alert(modules.m1);
alert(modules.m2);
alert(modules.m51);
}
优点:
> 声明写在程序上部, 便于查找检查其依赖, 并确保相关模块文件的引入
> 缓存成本地变量会比全局变量运行更快
> 压缩工具会把本地变量替换成更短的名字
3. 私有变量和方法 ----------------------------------------------------------
3.1 在构造方法里用闭包创建私有成员:
function Gadget() {
// private member
var name = 'iPod';
// public function
this.getName = function () {
return name;
};
}
*如果返回的是object或array, 因为传递的是引用, 数据可能被外部修改:
function Gadget() {
// private member
var specs = {
screen_width: 320,
screen_height: 480,
color: "white"
};
// public function
this.getSpecs = function () {
return specs;
};
}
解决的办法
> 返回object或array等的clone对象
> 用新方法, 比如getDimensions()等直接返回数值对象
3.2 字面量对象实现隐私性:
var myobj; // this will be the object
(function () {
// private members
var name = "my, oh my";
// implement the public part
// note -- no `var`
myobj = {
// privileged method
getName: function () {
return name;
}
};
}());
或
var myobj = (function () {
// private members
var name = "my, oh my";
// implement the public part
return {
getName: function () {
return name;
}
};
}());
3.3 原型和隐私性:
*使用构造函数创建隐私性会在每次都创建新对象, 从而耗费内存, 可以使用prototype解决
function Gadget() {
// private member
var name = 'iPod';
// public function
this.getName = function () {
return name;
};
}
Gadget.prototype = (function () {
// private member
var browser = "Mobile Webkit";
// public prototype members
return {
getBrowser: function () {
return browser;
}
};
}());
3.4 揭秘模块模式
*无法像ECMAScript5中的对象一样可以设置freeze属性使外界无法修改
但是可以使用 revealing module pattern 暴露某些有必要保护的私有方法
var myarray;
(function () {
var astr = "[object Array]",
toString = Object.prototype.toString;
function isArray(a) {
return toString.call(a) === astr;
}
function indexOf(haystack, needle) {
var i = 0,
max = haystack.length;
for (; i < max; i += 1) {
if (haystack[i] === needle) {
return i;
}
}
return 1;
}
myarray = {
isArray: isArray,
indexOf: indexOf,
inArray: indexOf
};
}());
即使外界修改了对象的indexOf, inArray引用的私有方法indexOf的功能依然正常
myarray.indexOf = null;
myarray.inArray(["a", "b", "z"], "z"); // 2
4. 模块模式 ----------------------------------------------------------
*广泛使用的模式, 提供结构化并帮助应付增长的代码, 在黑盒中实现增删改
//首先定义命名空间
MYAPP.namespace('MYAPP.utilities.array');
//使用一个局部方法返回的对象创建模块, 从而实现私密性和公开的API
MYAPP.utilities.array = (function () {
// dependencies
var uobj = MYAPP.utilities.object,
ulang = MYAPP.utilities.lang,
// private properties
array_string = "[object Array]",
ops = Object.prototype.toString;
// private methods
// ...
// end var
// optionally one-time init procedures
// ...
// public API
return {
inArray: function (needle, haystack) {
for (var i = 0, max = haystack.length; i < max; i += 1) {
if (haystack[i] === needle) {
return true;
}
}
},
isArray: function (a) {
return ops.call(a) === array_string;
}
// ... more methods and properties
};
}());
4.1 揭秘模块模式
*结合使用模块和解密模式, 可以将所有方法设为私有, 并在局部方法的最后决定公开哪些API
MYAPP.utilities.array = (function () {
// private properties
var array_string = "[object Array]",
ops = Object.prototype.toString,
// private methods
inArray = function (haystack, needle) {
for (var i = 0, max = haystack.length; i < max; i += 1) {
if (haystack[i] === needle) {
return i;
}
}
return −1;
},
isArray = function (a) {
return ops.call(a) === array_string;
};
// end var
// revealing public API
return {
isArray: isArray,
indexOf: inArray
};
}());
4.2 创建构造器的模块
*某些情况下需要对象的构造器函数, 与上面例子的区别是返回一个function而非object
MYAPP.namespace('MYAPP.utilities.Array');
MYAPP.utilities.Array = (function () {
// dependencies
var uobj = MYAPP.utilities.object,
ulang = MYAPP.utilities.lang,
// private properties and methods...
Constr;
// end var
// optionally one-time init procedures
// ...
// public API -- constructor
Constr = function (o) {
this.elements = this.toArray(o);
};
// public API -- prototype
Constr.prototype = {
constructor: MYAPP.utilities.Array,
version: "2.0",
toArray: function (obj) {
for (var i = 0, a = [], len = obj.length; i < len; i += 1) {
a[i] = obj[i];
}
return a;
}
};
// return the constructor to be assigned to the new namespace
return Constr;
}());
var arr = new MYAPP.utilities.Array(obj);
4.3 将全局变量传入模块
*通常可以向局部方法中传递全局变量, 包括模块自身, 使其变为局部变量以加速
MYAPP.utilities.module = (function (app, global) {
// references to the global object
// and to the global app namespace object
// are now localized
}(MYAPP, this));
5. 沙盒模式 -------------------------------------------------------------------
*此模式着眼于解决命名空间模式的以下缺陷:
>一个页面中无法有两个以上的相同程序或库共存, 因为会共用全局的变量名
>需要打印长的命名空间名称, 并在运行时解析之
*沙盒模式使得模块在自己的沙盒中运行, 不受其他模块的影响
//这个Sandbox可以命名成自己的程序名, 比如MYAPP
function Sandbox() {
var args = Array.prototype.slice.call(arguments), //将参数表转换为数组
callback = args.pop(), //最后一个参数必须是callback方法
//模块名称可以用数组或分开传递进来
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
i;
//确保方法由new操作符调用
if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
//一些必要的属性
this.a = 1;
this.b = 2;
//将指定的模块附加到'this'对象上
//没指定模块, 或者使用了'*', 将附加所有模块
if (!modules || modules === '*') {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
//初始化所需模块
for (i = 0; i < modules.length; i += 1) {
Sandbox.modules[modules[i](this);
}
//调用callback, 其中使用的'this'就是附加了所需模块后的独立对象
callback(this);
}
//一些必要的受保护信息
Sandbox.prototype = {
name: "My Application",
version: "1.0",
getName: function () {
return this.name;
}
};
//模块定义
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 () {};
};
//使用实例
Sandbox(function (box) {
//参数表中的box不写也行, 直接用this
});
Sandbox('*', function () {
//等同于上一种
});
Sandbox(['ajax', 'event'], function (box) {
// console.log(box);
});
Sandbox('ajax', 'dom', function (box) {
// console.log(box);
});
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
});
http://www.nowamagic.net/javascript/js_SandBoxPatterm.php
http://www.nowamagic.net/javascript/js_SandBox.php
http://www.planabc.net/lab/yui/sandbox.html
6. 静态成员 ---------------------------------------------------------------
6.1 公开的静态成员
// constructor
var Gadget = function () {};
// a static method
Gadget.isShiny = function () {
return "you bet";
};
// a normal method added to the prototype
Gadget.prototype.setPrice = function (price) {
this.price = price;
};
6.2 私有静态成员
*所谓私有静态成员, 即只能在由构造函数创建的对象中共享的成员
var Gadget = (function () {
//静态变量/属性
var counter = 0;
// 返回的方法成为了新的构造函数
return function () {
console.log(counter += 1);
};
}()); //立即执行的局部方法
var g1 = new Gadget(); // 1
var g2 = new Gadget(); // 2
var g3 = new Gadget(); // 3
7. 对象常量 -------------------------------------------------------------
*一般采用通行的全大写变量的方法表示常量, 如果确实需要不变的值, 可以这样:
var constant = (function () {
var constants = {},
ownProp = Object.prototype.hasOwnProperty,
allowed = {
string: 1,
number: 1,
boolean: 1
},
prefix = (Math.random() + "_").slice(2);
return {
set: function (name, value) {
if (this.isDefined(name)) {
return false;
}
if (!ownProp.call(allowed, typeof value)) {
return false;
}
constants[prefix + name] = value;
return true;
},
isDefined: function (name) {
return ownProp.call(constants, prefix + name);
},
get: function (name) {
if (this.isDefined(name)) {
return constants[prefix + name];
}
return null;
}
};
}());
// check if defined
constant.isDefined("maxwidth"); // false
// define
constant.set("maxwidth", 480); // true
// check again
constant.isDefined("maxwidth"); // true
// attempt to redefine
constant.set("maxwidth", 320); // false
// is the value still intact?
constant.get("maxwidth"); // 480
8. 链式模式 -------------------------------------------------------------
*就是jQuery式的调用方法:
myobj.method1("hello").method2().method3("world").method4();
如果方法没有明确的有意义的返回值时, 就可以返回this, 这样就可以实现链式模式
var obj = {
value: 1,
increment: function () {
this.value += 1;
return this;
},
add: function (v) {
this.value += v;
return this;
},
shout: function () {
alert(this.value);
}
};
// chain method calls
obj.increment().add(3).shout(); // 5
// as opposed to calling them one by one
obj.increment();
obj.add(3);
obj.shout(); // 5
9. method()方法
*使用prototype定义方法可能在写法上带来不便, 结合链式模式, 可以采用method()方法
if (typeof Function.prototype.method !== "function") {
Function.prototype.method = function (name, implementation) {
this.prototype[name] = implementation;
return this;
};
}
var Person = function (name) {
this.name = name;
}.method('getName', function () {
return this.name;
}).method('setName', function (name) {
this.name = name;
return this;
});
var a = new Person('Adam');
a.getName(); // 'Adam'
a.setName('Eve').getName(); // 'Eve'