JavaScript模式笔记

JavaScript模式学习笔记


一、基本技巧

  • 尽量少使用全局变量
  • 使用单一var模式,即在函数顶部只用一个var进行多个变量声明;
  • 为了避免变量提升,使用单一var模式;
  • for循环时缓存length,提高速度;
  • for循环遍历数组,for-in遍历对象;
  • 不要为内置的构造函数添加原型属性;
  • 避免使用隐式类型转换(),使用=;
  • 避免使用eval();
  • 使用parseInt()时指明转换的进制数
  • 良好的编码规范和命名规则
  • 通过良好的注释提供参考和维护代码
    注释eg:
/**
 * [function description]  函数功能描述
 * @constrctor 用于表示构造函数
 * @namespace namespaceName 用于命名包含所有对象的全局引用的名称
 * @method methodName 定义对象中的方法和方法名
 * @property propertyName 定义对象中的属性名
 * @param  {[type]} paramName   [description]  参数
 * @param  {[type]} paramName   [description]  参数
 * @return {[type]}             [description]  返回值
 */

二、字面量和构造函数

  • 可重用的属性和方法都应该放入原型中。
  • 创建对象和数组时使用字面量语法。
  • 安全的数组检测:
/**
 * 安全的数组检测
 * @param  {unknown}  value 待检测数据
 * @return {Boolean}       是否为数组
 */
function isArray(value) {
	if(typeof Array.isArray === 'undefined') {
		return Object.prototype.toString.call(value) === '[object Array]';
	} else {
		return Array.isArray(value);
	}
}

三、函数

  1. 即时函数
(function(){
	// dosomething
})();

var fnName=function(){
    alert('Hello World');
}();
//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数
function fnName(){
    alert('Hello World');
}();
//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用
function(){
    console.log('Hello World');    
}();
//语法错误,虽然匿名函数属于函数表达式,但是未进行赋值操作,
//所以javascript引擎将开头的function关键字当做函数声明,报错:要求需要一个函数名

即时函数能保证全局空间不会被临时变量所污染。
2. 即时对象初始化

({
	//配置常数
	width:600,
	height:600,
	// 定义方法
	gimmeMax: function() {
		return this.widht + "x" + this.height;
	},
	// 初始化
	init: function() {
		console.log(this.gimmeMax());
	}
}).init();
  1. 初始化时分支
    eg: 优化事件绑定
// 这样只会在初始化EventUtil时执行条件语句,在绑定事件时不用每次都执行条件查询
var EventUtil = {
	addEvent: null
};
if(typeof window.addEventListener === 'function') {
	EventUtil.addEvent = function(el, type, handler) {
		el.addEventListener(type, handler, false);
	}
}else if(typeof document.attachEvent === 'function') {
	EventUtil.addEvent = function(el, type, handler){
		el.attachEvent('on' + type, handler);
	}
}
  1. 配置对象

当函数的参数过多时,为了提供更整洁的API接口,可以安全忽略可选参数,更加易于阅读和维护,易于添加和删除参数,即可以使用参数对象。

	addPerson(obj){
		// do something...
	}
	var conf = {
		name: "batman",
		firstName: "chris",
		lastName: "bale"
	};
	addPerson(conf);
  1. 函数柯里化

当发现调用同一个函数,且传递的参数绝大多数是相同的时候,就可以对该函数使用柯里化。

function schonfinkelize(fn) {
	var slice = Array.prototype.slice,
		stored_args = slice.call(arguments, 1);
	return function() {
		var new_args = slice.call(arguments),
			args = stored_args.concat(new_args);
		return fn.apply(null, args);
	};
}
function add(a, b, c, d, e) {
	return a + b + c + d + e;
}
// 使用示例:schonfinkelize(add, 1, 2, 3)(5, 5);
// 输出16

四、对象创建模式

  1. 命名空间模式
    创建一个全局对象,然后使所有函数和变量成为该全局对象的属性和方法。
var MYOBJ = MYOBJ || {}; // 全局变量全部用大写形式命名
// 避免重复命名的命名函数
MYOBJ.namespace = function (nameString) {
	var parts = nameString.split('.'),
		parent = MYOBJ,
		i;
	if(parts[0] === "MYOBJ") {
		parts = parts.slice(1);
	}
	for(i = 0; i< parts.length; i++) {
		if(typeof parent[parts[i]] === 'undefined') {
			parent[parts[i]]={};		
		}
		parent = parent[parts[i]];
	}
	return parent;
}
  • 优点:可以避免过多的全局变量,避免命名冲突。
  • 缺点:每个函数变量前面都必须加前缀,增加代码量,任何代码都可以修改该全局实例。

依赖关系
在函数或者模块顶部显式地声明依赖的模块。

	var function myFunction() {
		//声明依赖
		var event = MYOBJ.event;
		// 使用依赖关系
		// do something
	}
  1. 私有属性和方法
	 //   将私有方法揭示为公有方法
	var myarray;  
	(function(){
		// 私有属性
		var astr = '[object Array]'
		// 私有方法
		function isArray(a) {
			return Object.prototype.toString.call(a) === astr;
		}
	// 公共接口
	myarray = {
		isArray: isArray	
	}
	})();
  1. 模块模式
    结合命名空间使用:
	var MYOBJ = MYOBJ || {};
	MYOBJ.namespace('MYOBJ.util.array');
	MYOBJ.util.array = (function(){
		// 私有属性
		var astr = '[object Array]';
		// 私有方法
		function isArray(a) {
			return Object.prototype.toString.call(a) === astr;
		}
		// 公共接口
		return {
			isArray: isArray	
		};
	})();

五、代码复用模式

  1. 类式继承模式
  • 默认继承模式:
	function inherit(Child, Parent) {
		Child.prototype = new Parent();
	}
  • 构造函数继承模式

缺点是无法从原型中继承任何方法。优点是可以获得父对象自身成员的真实副本,并不会存在子对象意外覆盖父对象的风险。

  • 组合继承模式

组合继承模式缺点是父构造函数被调用了两次,效率低下,且自身的属性会通过构造函数和原型继承两次。

  • 共享原型模式
	function inherit(Child, Parent) {
		Child.prototype = Parent.prototype;
	}

与默认方法相比,这样做的优点是效率比较高(不用执行和建立Parent的实例了),比较省内存。缺点是 Parent.prototype和Child.prototype现在指向了同一个对象,那么任何对Child.prototype的修改,都会反映到Parent.prototype。

  • 临时构造函数
	function inherit(Child, Parent) {
		var F = function () {}; // 临时构造函数
		F.prototype = Parent.prototype;
		Child.prototype = new F();
		Child.uber = Parent.prototype; // 储存超类
		Child.prototype.constructor = Child; // 重置构造函数指针
	}

2.原型继承(无类继承模式)

	function object(parent){
		function F() {};
		F.prototype = parent;
		return new F();
	}
	var parent = {
		name: "daddy"
	};
	var child = object(parent);

这种继承方式会共享相应的值,就像默认模式一样,对子类型的修改都会反应到父类型上。

  1. 寄生继承
	function createAnother(parent){
		var child = object(parent);
		return child;
	}

适合对象不是自定义类型和构造函数的情况。

  1. 寄生组合式继承(引用类型最理想的继承范式!)

在组合式继承的基础上优化,利用寄生继承继承父类的原型,使得继承时只调用一次父类构造函数。

	function inheritPrototype(child, parent){
		// ES5有原生object.create方法代替object方法
		var prototype = object(parent.prototype);
		prototype.constructor = child;
		child.prototype = prototype;
	}

// 在组合继承时将child.prototype = new Parent(); 改成inheritPrototype(child, parent); 

六、设计模式

  1. 单体模式
    单体模式的定义是产生一个类的唯一实例.
var singleton = function( fn ){
    var result;
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}
var createMask = singleton( function(){
	return document.body.appendChild( document.createElement('div') );
});
  1. 模块模式
	var myNamespace = (function () {
  var myPrivateVar, myPrivateMethod;
  // A private counter variable
  myPrivateVar = 0;
  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };
	return {
	    // A public variable
	    myPublicVar: "foo",
	    // A public function utilizing privates
	    myPublicFunction: function( bar ) {
      // Increment our private counter
      myPrivateVar++;
      // Call our private method using bar
      myPrivateMethod( bar );
    }
  };
})();

模块模式相对于真正的封装概念更清晰,其次,模块模式支持私有数据-因此,在模块模式中,公共部分代码可以访问私有数据,但是在模块外部,不能访问类的私有部分。
模块模式的缺点是因为我们采用不同的方式访问公有和私有成员,因此当我们想要改变这些成员的可见性的时候,我们不得不在所有使用这些成员的地方修改代码。

posted @ 2015-12-21 14:22  black_star  阅读(127)  评论(0编辑  收藏  举报