《JavaScript模式》--第五章:对象创建模式

/*
	对象创建模式
*/
/*----------------------------START 命名空间--------------------------------------*/
/*
		JS中并没有内置命名空间
		要实现命名空间,可以为应用程序或库创建一个全局对象,然后可以将所有功能添加到该全局对象中,从而在具有大量函数、对象和其他变量的情况下并不会污染全局范围
*/
var MYAPP = {};

MYAPP.Parent = function() {};
MYAPP.Child = function() {};

MYAPP.some_var = 1;
MYAPP.modules = {};

MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = {
	a: 1,
	b: 2
};
MYAPP.modules.module2 = {};

/*
	优点
		可以避免代码中的命名冲突
		并且还可以避免在同一个页面中的您的二代码和第三方代码之间的命名冲突
	缺点
		需要输入更多的字符,每个变量和函数前都要附加前缀
		仅有一个全局实例意味着任何部分的代码都可以修改该全局实例
		长嵌套名字以为着更长的属性解析查询时间
*/
/*
	通用命名空间函数
*/
/*在添加一个属性或者创建一个命名空间之前,最好是首先检查它是否已经存在*/
var APP = {};
APP.namespace = function(ns_string) {
	var parts = ns_string.split('.'),
		parent = APP;

	if (parts[0] === 'APP') {
		parts = parts.slice(1);
	}

	for (var i = 0, m = parts.length; i < m; i++) {
		if (typeof parent[parts[i]] === "undefined") {
			parent[parts[i]] = {};
		}
		parent = parent[parts[i]];
	}
	return parent;
};
var module2 = APP.namespace('APP.modules.module2');
APP.namespace('a.b.c.d.e.f.g');
console.log(APP);
/* 
	输出:
	Object {namespace: function, modules: Object, a: Object}
		a: Object
			b: Object
				c: Object
					d: Object
						e: Object
							f: Object
								g: Object
								__proto__: Object
							__proto__: Object
						__proto__: Object
					__proto__: Object
				__proto__: Object
			__proto__: Object
		__proto__: Object
		modules: Object
			module2: Object
			__proto__: Object
		__proto__: Object
		namespace: function (ns_string) {
		__proto__: Object
*/

/*----------------------------END 命名空间--------------------------------------*/

/*----------------------------START 声明依赖关系-------------------------------------*/
/*
	在函数或模块顶部声明代码所依赖的模块是一个非常好的主意
		优点
			显式地依赖声明向用户表明了他们确定需要的特定脚本已经包含在该页面中
			在函数顶部的前期声明可以更容易地发现并解析依赖
			解析局部变量的速度要比解析全局变量要快,能提高性能
			使用一些小公鸡可以重命名局部变量,可以减低代码量
*/
/*----------------------------END 声明依赖关系-------------------------------------*/

/*----------------------------START 私有属性和方法-------------------------------------*/
/*
	JS中所有对象的成员都是公共的
	我们可以使用闭包来实现私有属性和方法。构造函数创建一个闭包,而在必报范围内部的任意变量都不会暴露给构造函数以外的代码
*/

function Constructor() {
	var name = "wly";
	this.getName = function() {
		return name;
	}
}
var obj = new Constructor();
console.log(obj.name);
console.log(obj.getName);
// 输出:
// undefined
// function (){
// 		return name;
// } 

/*
	特权方法
		所谓特权方法,就是指那些可以访问私有成员你的公共方法,如上例中的getName()方法

	私有性失效
		当直接从一个特权方法中返回私有的变量,而该变量是数组或是对象,那么外面的代码将可以随意更改这个变量
		因为传递的是引用,很好理解,故不举例
		解决方法
			返回一个新对象,仅仅包含客户关注的原对象中的数据
			创建一个克隆,返回那个克隆的副本
*/

/*
	对象字面量以及私有性
		在使用对象字面量的情况下,可以使用一个额外的匿名即时函数创建闭包来实现私有性
		这也就是众所周知的模块模式的基础框架
*/
var myObj = (function() {
	var name = "wly";
	return {
		getName: function() {
			return name;
		}
	};
}());
console.log(myObj.name);
console.log(myObj.getName());
// 输出:
// undefined
// wly

/*
	原型和私有性
		将私有成员和构造函数一起使用时,其中的一个缺点在于每次调用构造函数以创建对象时,这些私有成员都会被重新创建
		为了避免复制工作以及节省内存,可以将常用属性和方法添加到构造函数的prototype中,这样同一个构造函数创建的多个实例可以共享常见的部分数据
		由于prototype属性仅仅是一个对象,因此可以使用对象字面量创建该对象
*/

function SomeObj() {
	var name = "wly";
	this.getName = function() {
		return name;
	}
}

SomeObj.prototype = (function() {
	var address = "bupt";
	return {
		getAddress: function() {
			return address;
		}
	};
}());

var obj = new SomeObj();
console.log(obj.name);
console.log(obj.getName());
console.log(obj.address);
console.log(obj.getAddress());
// 输出:
// undefined 
// wly 
// undefined 
// bupt 

/*
	揭示模式
		将私有方法暴露成公有方法,在返回的对象中包含私有方法的引用
*/

var SomeObjConstructor = function() {
	var name = "wly",
		changeName = function() {
			name = "I am not wly";
		};
	return {
		getName: function() {
			return name;
		},
		setName: changeName
	};
};

var myObj = new SomeObjConstructor();
console.log(myObj.getName());
myObj.setName();
console.log(myObj.getName());
// 输出:
// wly
// I am not wly 

/*----------------------------END 私有属性和方法-------------------------------------*/

/*----------------------------START 模块模式-------------------------------------*/
/*
	模块模式是一种得到广泛应用的模式,其实际上就是以下模式的组合
		命名空间
		即时函数
		私有和特权成员
		声明依赖

	模块模式的步骤:
		首先建立一个命名空间
		然后定义这个模块
		向其私有作用域添加一些私有属性和方法
		返回一个对象,该对象中包含了模块的公共API
*/
/*使用命名空间模式创建命名空间*/
APP.namespace("APP.modules.myModule");

APP.modules.myModule = (function() {
	var otherModule_1 = APP.modules.otherModule_1,
		otherModule_2 = APP.modules.otherModule_2,
		name = "wly";
	return {
		getName: function() {
			return name;
		},
		setName: function() {
			name = "I am not wly";
		}
	};

}());

console.log(APP.modules.myModule.getName());
APP.modules.myModule.setName();
console.log(APP.modules.myModule.getName());
// 输出:
// wly
// I am not wly 

/*
	揭示模块模式
		模块模式中将所有方法定义为私有,只揭示那些公开的方法
*/
APP.namespace("APP.modules.myModule");

APP.modules.myModule = (function() {
	var otherModule_1 = APP.modules.otherModule_1,
		otherModule_2 = APP.modules.otherModule_2,
		name = "wly",
		getName = function() {
			return name;
		},
		setName = function() {
			name = "I am not wly";
		};
	return {
		getName: getName,
		setName: setName
	};

}());

console.log(APP.modules.myModule.getName());
APP.modules.myModule.setName();
console.log(APP.modules.myModule.getName());
// 输出:
// wly
// I am not wly

/*
	创建构造函数的模块
		在使用构造函数创建对象时,使用模块模式来执行创建对象的操作,与之前不同的是在于包装了模块的即时函数最终返回一个函数,而不是对象,这个返回的函数就是构造函数
*/
APP.namespace("APP.modules.myModule");

APP.modules.MyModule = (function(global) {
	var otherModule_1 = APP.modules.otherModule_1,
		otherModule_2 = APP.modules.otherModule_2,
		moduleConstrutor;
	moduleConstrutor = function(obj) {
		this.properties = obj;
		this.reset = function() {
			this.properties = {
				name: "none",
				address: "no house"
			};
			return this;
		};
	}
	moduleConstrutor.prototype = {
		constructor: APP.modules.myModule,
		version: "1.0",
		outputToConcole: function() {
			for (var tmp in this.properties) {
				console.log(this.properties[tmp]);
			}
			return this;
		},
		outputContext: function() {
			console.log(global);
		}
	};
	return moduleConstrutor;
}(this));

var newModule = new APP.modules.MyModule({
	name: "wly",
	address: "bupt"
});
newModule.outputToConcole().reset().outputToConcole().outputContext();
// 输出:
// wly 
// bupt 
// none 
// no house 
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

/*
	导入全局变量
		将全局变量作为参数传递到模块中,可以加快全局符号解析速度,具体使用如上
*/
/*----------------------------END 模块模式-------------------------------------*/

/*----------------------------START 沙箱模式-------------------------------------*/
/*
	沙箱模式解决了命名空间模式的几个缺点
		对单个全局变量的依赖变成了对应用程序的全局变量依赖
		对这种以点分割的名字来说,需要输入更长的字符,并且在运行时需要解析更长的时间

	沙箱模式提供了一个可用于模块运行的环境,且不会对其他模块和个人沙箱造成任何影响
*/

/*
	全局构造函数
		在沙箱模式中,则是一个全局构造函数,为Sandbox()

	可以给这个模式添加两个新的特征
		强制new模式
		Sandbox()构造函数可以接受一个或多个额外的配置参数,这些参数代表了对象实例所需要的模块名

	增加模块
		Sandbox.modules = {}
		Sandbox.modules.dom = function(box){};
		Sandbox.modules.event = function(box){};
		Sandbox.modules.ajax = function(box){};
*/
/*
	实现构造函数
*/

function Sandbox() {
	var args = Array.prototype.slice.call(arguments),
		callback = args.pop(),
		modules = (arg[0] && typeof args[0] === 'string') ? args : args[0],
		i;
	//强制new模式
	if (!(this instanceof Sandbox)) {
		return new Sandbox(modules, callback);
	}
	//添加属性到this中
	this.name = 'wly';
	this.address = 'bupt';
	//不指定模块名称和使用*都表示使用所有模块
	if (!modules || modules === '*') {
		modules = [];
		for (i in Sandbox.modules) {
			if (Sandbox.modules.hasOwnProperty(i)) {
				modules.push(i);
			}
		}
	}
	//初始化所有模块
	for (i = 0; i < modules.length; i++) {
		Sandbox.modules[modules[i]](this);
	}
	//回调函数调用
	callback(this);
};
//沙箱的原型属性
Sandbox.prototype = {
	version: "1.0",
	getName: function() {
		return this.name;
	}
};

/*----------------------------END 沙箱模式-------------------------------------*/

/*----------------------------START 静态成员-------------------------------------*/
/*
	公有静态成员
		JS中可以通过使用构造函数并且向其添加属性,从而获得类似的效果,备忘录模式就是这种思想
*/
var SomeObj = function() {};
SomeObj.staticMethod = function() {
	console.log("static method");
};
SomeObj.prototype.normalMethod = function() {
	console.log("normal method");
};
SomeObj.staticMethod();
var newObj = new SomeObj();
newObj.normalMethod();
console.log(typeof SomeObj.normalMethod);
console.log(typeof newObj.staticMethod);
// 输出:
// static method
// normal method
// undefined
// undefined
// 分析:
// 第一个undefined表示实例方法无法以静态方式调用
// 第二个undefined表示静态方法无法以实例方式调用

/*
	如需要让实例也能直接调用静态方法,需要将静态方法加入到构造函数的原型中
		需要注意这种模式下的this,构造函数直接调用时指向构造函数,而如果是对象进行静态方法调用,则this指向这个对象
*/
SomeObj.prototype.staticMethod = SomeObj.staticMethod;
newObj.staticMethod();
// 输出:
// static method

SomeObj.staticMethod_2 = function() {
	var msg = 'Static invoke';

	if (this instanceof SomeObj) {
		msg = 'Object invoke';
	}

	return msg;
};

SomeObj.prototype.staticMethod_2 = function() {
	return SomeObj.staticMethod_2.call(this);
};

console.log(SomeObj.staticMethod_2());
console.log(new SomeObj().staticMethod_2());
// 输出:
// Static invoke
// Object invoke

/*
	私有静态成员
		以同一个构造函数创建的所有对象共享该成员
		构造函数外部不可访问该成员
*/
var SomeObj = (function() {
	var count = 0;
	return function() {
		count++;
		console.log(count);
	};
}());
new SomeObj();
new SomeObj();
new SomeObj();
new SomeObj();
// 输出:
// 1
// 2
// 3
// 4

/*
	对象常量
		JS中没有常量这个概念,一般是以命名约定来标识常量,常量的名称全部用大写字母显示

	一个通用的常量对象实现方法实例
		set(name,value):定义一个新的常量
		isDefined(name) :检测指定常量是否存在
		get(name):读取指定常量的值
*/

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;
		}
	};
}());

console.log(constant.isDefined("name"));
console.log(constant.set("name","wly"));
console.log(constant.get("name"));
console.log(constant.set("name","not wly"));
console.log(constant.get("name"));
// 输出:
// false
// true
// wly
// false 
// wly 

/*----------------------------END 静态成员-------------------------------------*/

/*----------------------------START 链模式-------------------------------------*/
/*
	链模式
		使用户能够一个接一个的调用对象的方法,而无需将前一个操作返回的值赋给变量
		当创建的方法其返回的是无任何意义的值时,可以使他们返回this,即正在使用的对象实例

	链模式优点
		可以节省一些输入字符,而且可以创建更加简洁,更加清晰的代码
		可以帮助考虑分割函数,以创建更加简短、具有特定功能的函数,而不是创建尝试实现太多功能的函数

	链模式的缺点
		出错了难以调试

	jQuery使用了这种模式
*/
var someObj = {
	value : 1,
	increment : function(){
		this.value += 1;
		return this;
	},
	add : function(v){
		this.value += v;
		return this;
	},
	output : function(){
		console.log(this.value);
		return this;
	}
};
someObj.increment().output().add(5).output().add(3).output();
// 输出:
// 2 
// 7 
// 10 

/*----------------------------END 链模式-------------------------------------*/

/*----------------------------START method()方法模式-------------------------------------*/
/*
	method()方法
		Douglas Crockford引入
		有两个参数
			新方法的名称
			新方法的实现
*/
// 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 wly = new Person("wly");
console.log(wly.getName());
console.log(wly.setName("not wly").getName());
// 输出:
// wly
// not wly 
/*----------------------------END method()方法模式-------------------------------------*/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值