《JavaScript模式》笔记

每次去图书馆都能随手带两本不错的书回来。看了不少书。有不少收益。慢慢总结笔记。发现一次看很多本书,更容易理解书里的概念。


第一章

基本概念:

模式: 重现事件或对象的主题……是一个可用来产生其他事物的模板或模型。   

好处:1.经验总结。2.某种程度上的抽象。3.改善交流。共同语言。


设计模式。 编码模式:提供关于该语言很好的独特的体验。 反模式。



第二章、基本技巧


编写可维护的代码 

维护自己写的代码的问题:花时间重新学习和理解问题。花时间理解当时用于解决相应问题的代码。

如何叫可维护:阅读性、一致性、预见性、看起来如同一个人编写、有文档。


全局变量

问题:不好管理。容易跟别人或自己的代码冲突导致功能失调。

意外生成:    

1.函数中的隐式声明。有时候不小心就省略了var。

2.函数中  var a = b = 0 ;  b是全局变量。

3. 有时候 的  this.a = 1;这种也会变成全局变量。


var全局变量不能delete。 隐式的可以。


全局对象  在非浏览器下可能不叫 window 。   var globe = (function(){ return this;})();


如何减少全局变量:

1.单一var模式。   dom可以用局部变量表达起来。

2.即时函数。(作用域沙箱)。

3.命名空间模式。


for循环的优化

html容器是dom方法返回的对象。  如:document.getElementsByName()、document.images、document.links、document.forms、document.forms[0].elements

1.缓存length;    for(var i = 0,max = arr.length;i

2.var i,arr=[]; for(i = arr.length;i--;)

3.var arr = [],i=arr.length; while(i--){}


for in 主要用于非数组对象。   搭配 hasOwnProperty使用。


其他

switch case。   如果条件多。 可以用 var obj= {"case1":1,"case2":2,"case3":3}.  来代替。  obj['case1'];


不要增加内置原型。一定要增加请先检测是否存在该属性。


避免使用隐式类型转换。  要使用 ===。


使用 parseInt要约定 parseInt(year,10)。不要省略几进制。因为0开始的会当成8进制返回。



编码约定

缩进、大括号、空格
命名约定:构造函数的首字母大写
分割单词:大驼峰小驼峰下划线 

函数与变量:全部大写字母用来表示常量不可改变、下划线开头来表示私有函数。

注释与更新注释

第三章、字面量和构造函数

来自构造函数的对象。

对象构造函数的捕捉。

自定义构造函数。在函数体中重新定义自己?


以new操作符调用构造函数的返回值:

1、如果没有return或return的直接量。返回this。(如果构造函数中并不向this添加任何属性,将返回“空”对象。) 

2、如果有return对象。返回这个对象。


var this = Object.create(Person.prototype);


强制使用new模式:

1.返回一个想要的对象而不是this指代的对象。

2.自调用构造函数。  检测。if(!(this instanceof Person)){ return new Person()};



数组:

new Array(3) //长度为3

new Array(3.14) //error

检测是不是数组。 isArray

Object.prototype.toString.call([2,3])
"[object Array]"


JSON:

解析JSON: JSON.parse(jstr);$.parseJSON(jstr);eval('(+jstr+')');

序列化JSON: JSON.stringify(obj);


正则表达式

基本值类型包装器

错误对象。


第四章、函数

第一类对象。提供作用域。可调用。


函数就是对象。表现:

1.可运行时创建。

2.可分配给变量。

3.可当参数传递。

4.可添加属性和方法。


函数的命名属性。 .name。

函数的提升。


回调模式。 将函数当参数传递以备调用。

异步事件监听器。  大多数的客户端浏览器编程都是事件驱动的。

超时回调。

库中的回调模式。如jqery中的success。


返回函数。


自定义函数。

惰性函数。 函数第一次使用才真正被定义。自己给自己新的定义。

缺点:重定义时添加到原来函数的任何属性都会丢失。   引用丢失?


即时函数。 定义后立即执行。

作用域沙箱。

即时函数的参数传递。

即时函数的返回值。


即时对象的初始化 ({}).init();


初始化时分支。

跟自定义函数实现的效果有点像。 根据具体的情况。执行到那里,给函数合适的定义。  addEventListener与attachEvent浏览器嗅探。


函数属性。  

备忘模式。  比如缓存计算结果。调用次数等等。

函数的length。值为期望参数的数量。


配置对象。  函数大量传参数的时候。  可以把多个参数放到一个  obj中传入。

对象的方式来传递参数。不需要记忆顺序。安全忽略可选参数。容易添加和删。

curry:函数柯里化。 部分函数。



第五章、对象创建模式。 

 命名空间、依赖声明、模块模式、沙箱模式。


命名空间模式

var module1 = {}; module1.a = 1;

缺点:1.字多。2.长嵌套解析慢。


命名空间函数 

var MYAPP = MYAPP ||{}。

写一个函数来创建空对象,添加属性。 MYAPP.namespace("MYAPP.a.b.c.d.e");


声明依赖关系

在函数顶部声明是不错的。  var event = MYAPP.a.event,dom = MYAPP.a.dom;

好处:1.一眼看出依赖。2.局部变量解析快。3.局部变量方便代码压缩。4.方便验证所需的脚本在不在页面中。


私有属性和方法。

可使用闭包实现属性和方法私有。

特权方法:可访问私有成员的方法。


私有性失效。

返回的变量是对象或数组。  那么外面可以访问甚至修改这个私有变量。

使用对象克隆。


对象字面量以及私有性

可以使用一个额外的即时函数创建闭包来实现私有性。 


模块模式p97.


原型和私有性。prototype也可以用闭包这样子。

	Person.prototype = (
		function(){
			var name = 1; 
			return {getName:function(){
				return name;
			}
		}
	}
	)();



将私有方法揭示为公有方法。

arr = {isArr:isArr,indexOf:indexOf}


模块模式: 命名空间 即时函数 私有和特权成员 声明依赖。

提示模块模式。

创建构造函数的模块。

将全局变量导入到模块中。()(MYAPP,this);


沙箱模式。

全局构造函数。

增加模块。

实现构造函数。


静态成员  从一个实例到另一个实例都不会发生改变的属性和方法。

公有静态成员

私有静态成员。

同一个函数创建,所有对象共享。 构造函数外不可访问。


对象常量


链模式。返回this。像jquery。


method方法。为对象创建方法。


第六章 代码复用模式

优先使用对象组合,而不是类继承。

基于类特性    和  基于非类特性 的 继承特性。


类继承模式

类继承模式1

	function inheirt(C,P){   
		C.prototype = new P();   
	} 

原型属性应该指向一个对象而不是一个函数。

缺点:

1.继承了原型中的东西也继承了this上的属性。

2.不支持将参数传递到子构造函数中?


类继承模式2:借用构造函数。

	function C(a,b,c){
		P.apply(this,arguments);
	}


只继承了成员副本。没有继承原型里的东西。

借用构造函数实现多重继承。


类继承模式3:借用和设置原型。

就是上面两种方式加起来。 

	function C(a,b,c){
		P.apply(this,arguments);
	}
	function inheirt(C,P){   C.prototype = new P();    }   



类继承模式4:共享原型。

C.prototype = P.prototype;


类继承模式5:临时构造函数。

	function inheirt(C,P){
		var F = function(){};
		F.prototype = P.prototype;
		C.prototype = new F;
	}  


存储超类。上面的基础上增加指向原始父对象的引用。  C.uber = P.prototype;

重置构造函数指针。  C.prototype.constructor  = C;

优化:使用闭包,只创建一次临时构造函数。

	var inheirt = (function(){
		var F = function(){};
		return function inheirt(C,P){
			F.prototype = P.prototype;
			C.prototype = new F;
			C.uber = P.prototype;
			C.prototype.constructor  = C;
		}
	}())

Klass。


无类继承模式

原型继承

	function object(o){
		function F(){}
		F.prototype = o;
		return new F();
	}
	var kid = object(Person.prototype);

通过复制实现继承

浅复制。

深复制。

jqery库中的extend()方法可实现深度复制。


混入。mix-in。   从多个对象中复制出任意成员组合成一个新对象。  效果有点像上面的  借用构造函数实现多重继承。


借用方法: apply  call。

借用和绑定: bind。 将方法和对象绑定在一起。

可以使用闭包来实现。

	function bind(o,m){
		return function(){
			m.apply(o,[].slice.call(arguments));
		}
	}


[].slice.call(arguments)这句话相当于Array.slice.call(arguments),目的是将arguments对象的数组提出来转化为数组,arguments本身并不是数组而是对象 p142.

if(typeof Function.prototype.bind==="undefined"){
    Function.prototype.bind=function(thisArg){
        var fn=this,
            slice=Array.prototype.slice,
            args=slice.call(arguments,1);//arguments1
        return function(){
                return fn.apply(thisArg,args.concat(slice.call(arguments)));//arguments2
        }
    };
}

注意这个函数。  两个arguments 不是一样的。 

要理解一下这个函数。

var t1 = one.say.bind(two);  t1('haha');

var t2 = one.say.bind(two,'lala');  t2('lala')


代码复用才是最终目的,而继承只是实现这一目的的方法之一。



第七章.设计模式

单体模式。保证特定类只有一个实例。没有类,只有对象。

单体模式的思想在于保证一个特定类仅有一个实例。一个人创建新对象的时候,应该得到你第一次创建对象完全相同的对象
在javascript当中对象之间永远不会完全相等,除非他们是同一个对象。  在你的控制台输入 [1,2] == [1,2]试试。


用new操作符
可以使用全局变量来存储该实例
可以在构造函数的静态属性中换成该实例
可以将该实例包装在闭包中。

重现构造函数会丢失所有在初始定义和重定义时刻之间之间,添加到它里面的属性。    

工厂模式
内置对象工厂
而对于自然工厂的例子,可以考虑内置的全局object的构造函数

它根据输入类型而创建不同的对象。如果传递一个原始数字,后台他能number,构造函数创建一个对象。对于字符串和布尔值也同样。
无论是用new操作府与否,都可以调用object.

迭代器模式
在迭代器模式中通常有一个包含数据集合的对象,该数据可能存储在一个复杂数据结构内部,而要提供一种简单的方法能够访问数据结构中的每个元素,对象的消费者不需要知道如何组织数据,所有需要做的就是取出单个数据进行工作。
在迭代器模式中对象需要提供一个next方法。依次调用next必须返回下一个连续的元素。那一个所代表的意义是由你来决定的。

装饰者模式
在装饰者模式中可以在运行时动态添加附加功能的对象中。
装饰者模式的一个比较方法的特征在于其其行为的可定制和可配置特性,可以从仅仅具有一些基本功能的普通对象开始,然后从可用装饰池子中选择需要用于增强普通对象的那些功能,并且按照顺序进行装饰,尤其是当装饰顺序很重要的时候。

策略模式 

支持你在运行时选择算法。一个例子是表单验证。


外观模式

两个或更多的方法可能普遍被一起调用。在这样的情况下,另建一个方法包装重复的方法调用。

例子:1.浏览器事件阻止。2.用来处理浏览器差异。把对不同浏览器的方法包装在一起。


代理模式

书中的例子用来合并http请求以及缓存请求数据的。


中介者模式

专门的部门负责对象之间的通信。http://www.jspatterns.com/book/7/mediator.html


观察者模式

订阅/发布模式。所有的浏览器事件是该模式的例子。

这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另一个对象的特定活动并在状态改变后获得通知。




第八章、Dom和浏览器模式

DOM脚本、事件处理、远程脚本、页面载入JS的策略以及在网站上配置JS的步骤。


关注分离

内容 html 表现css 行为js。
渐进增强思想。简单的客户终端有基本体现,随着终端能力增强获得更好的效果。
有助于开发、维护、和升级现在web应用程序。知道在什么地方去排错。

在实际中,关注分离意味着:
将css关闭来测试页面是否可用,内容是否依然可读。
将js关闭来测试页面仍然可以执行其正常功能所有的链接能否正常工作,表单能否正常提交 。
不使用内联处理器onclick等,不使用内联样式。因为这些都不属于内容层。
语义化标签。

js层应该不引人注意的,不应该因为浏览器不能使用js而影响正常使用。

js应该是用来加强网页功能,还不能成为网页正常工作的必需组件。


处理浏览器差异:特性检测技术。

navigator.userAgent.indexOf('MISE')!==-1 //反模式
document.attachEvent //比较好的作法。


DOM脚本。

不同的浏览器处理dom树方式可能不一样。所以才使用各种库来加快开发速度。


DOM访问  代价是昂贵的,是制约js性能的主要瓶颈。

dom通常是独立与js引擎而实现的。  

好处:并不只是js可以访问dom。js也不一定会访问dom。
dom访问应该降到最低
1.避免在循环中使用dom访问。   针对不同浏览器可能快上几十到几百倍。
2.将dom引用分配给局部变量,使用这些局部变量。
3.在可能的情况下引入 selector API.
4.当在html容器中重复使用时,缓存重复的次数。参考第二章。

操纵dom  修改 删除dom元素。
会使浏览器重新绘制屏幕,也会导致reflow(重新计算元素的几何位置)。会带来巨大开销。

经验做法尽量减少更新dom。分批处理,并在“活动”文档树之外执行这些更新。
当需要一个相对比较大的子树,应该在子树完全建立好之后再添加到dom树中。
这时可以采用文档碎片(document fragment)技术来容纳所有节点。

appendChild。
document.createDocumentFragment();
添加新节点的时候这个是非常有用的。

但是更新已有的节点。方法是 为需要修改的子树的根结点创建一个克隆镜像。 .cloneNode(true);
然后在克隆镜像上面处理。 完成之后再 relpaceChild(clone,oldNode).


事件处理   W3C与IE.
是浏览器脚本领域中一个有许多不一致性导致工作失败的源头。用库可以减少在这方面做的工作。

事件处理。
通常事件处理是通过为元素附加事件监听器来实现的。

同一个事件,执行几个函数。
addEventListener(); 
IE8之前的版本中没有该方法,在这些老浏览器中使用attachEvent();

初始化分支模式

w3c的标准stopPropagation()与IE特有的方法 cancelBubble.
PreventDefault()和returnValue = false;


事件授权   事件委托?
类似于我写的扫雷中给canavs加的事件。分配到每一个格子上去了。
以及仿照百度网盘。 让新建文件夹时表格里的事件都点不了那个时候做的功能。
事件授权模式得益于事件冒泡。会减少为每个节点附加的事件监听数量。   

长期运行脚本
如果脚本的工作十分繁重,那么浏览器的ui将无法响应用户的任何操作。应该尽量避免。
js中没有线程,可以使用 setTimeout()来模拟线程,在最新版本的浏览器中可以使用web workers.

setTimeout()
将一个大任务分解成多个小任务,并为每一个小任务设置超时时间为1ms。使用户ui保持响应。

web workers

远程脚本

ajax

jsonp(可跨域)

框架和图像灯塔。(可跨域)

用于只需要向服务器发送数据而不需要服务器返回的时候。   也是一种跨域的方法?
new Image().src = '1.php' ?

配置JavaScript

精简和压缩脚本文件

Expires报头

使用CDN。
cdn是内容分发网络(content delivery network)的缩写

载入策略

你可能感兴趣的:(JavaScript)