
看完what is closure我们大概对闭包会有一定了解了吧!但是为什么js要有闭包呢,为什么总是有那么多的攻城师们吵吵嚷嚷的说没有闭包过不下去了,虽然他们显然在夸大,因为闭包是内嵌型语言(可嵌套作用域,如js,ruby,python)的专有,在网上看到人家的总结:
- 函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
- 函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
而那些非内嵌型的像C它还不是保持着语言版的第二!
进入正题,闭包真的拥有很多很有的作用,但他也有很多弊端!
闭包的内存泄露问题
大家都知道闭包没有释放外部变量,确实会造出一点泄露,但你会说“别死抠了!”是的,真正的问题不是在这里,而是隐藏在我们显见的编程中!在这里不得不插一下DOM与JS这两个冤家。
DOM与JS
因为在浏览器解析中DOM与js分开为两个解析器的(例如在IE分别为mshtml.dll跟jscript.dll),为什么呢,可能是为了让其它脚本语言可以对DOM进行引用吧,像VB script!但这样就给了js中DOM操作一个非常大的性能问题,很明显吧!DOM提供的API被引用时,借口的连接就会造成消耗,时间、内存都是问题!就像你去食堂打饭,饭和菜是分开打的,那么我得多花时间跟ATP才能吃上饭!
而造成内存最多的是循环引用(问题值出现在IE与firefox),何为循环引用呢?
当程序中出现了两个对象,并且它们之间保持着相互引用的状态,就称为循环引用!
IE 和 Firefox 均使用引用计数来为 DOM 对象处理内存。在引用计数系统,每个所引用的对象都会保留一个计数,以获悉有多少对象正在引用它。如果计数为零,该对象就会被销毁,其占用的内存也会返回给堆。所以当DOM遇上循环引用时,你的js程序就存在性能问题了!而闭包就是造成这个问题的一个不经意的杀手!也就是说循环引用经常会出现在闭包中,从而让闭包背上了这个恶名,它好无辜!举个简单的例子:
给一个列表绑定click事件,输出他的顺序!
<ul>
<li>第一个</li>
<li>第二个</li>
<li>第三个</li>
<li>第四个</li>
<li>第五个</li>
</ul>
(function (){
var collects=document.getElementsByTagName('li');
for(var i=0;i<=4;i++)
collects[i].onclick=function(){alert(i)};
collects.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
})()
为了使内存对比明显,我特意加上collects.testString=new Array(10000).join(new Array(1000));我通过了ie内存泄漏监测工具对比解决了泄漏问题前后的内存状况:
左边是DW中的代码!在删掉collects=null前后的内存确实变化超明显!
解决的方法很简单,既然是对象关联,那么你只要将其中一个释放了就可以了!
可能细心的同学还会注意到上面还隐藏着常见闭包引起的修改外部变量问题,本来想输出各个li的顺序,结果都是输出5;这是因为i是属于内部函数的外部变量,而当调用onclick时会在闭包的作用域链中记录下i的值!解决的方法很简单,让i成为内部函数的局部变量就可以了!
(function (){
var collects=document.getElementsByTagName('li');
for(var i=0;i<=4;i++)
(function(k)
{
collects[k].onclick=function(){alert(k)};
})(i)
})()
循环引用的另一种内存泄漏--DOM监瑞脑消金兽听
dom 节点的事件 handler 属性设置监瑞脑消金兽听器函数,监瑞脑消金兽听器函数的 [[scope]] 作用域会引用到该 dom 节点而造成隐蔽的循环引用
处理的方法其实都很简单,removeListener就可以了
Published on 2011-09-02 16:38.
Filed under: web前端, 我最钟意Javascript (编辑此文)

1.继承+性能
关于原型的继承网络上一堆文章,我就不吹了.
Js 秘密花园也有讲:http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype
其实有个地方很多人都会误解:每实例化一个对象,对象都会实例化原型的方法跟属性,所以每个对象需要为它们开辟多原型所有的内存。这个是错的!所有实例对象将会公用同一个原型对象的方法,所以只会有一个方法的内存空间,但是这样又会促发另一个性能问题,就是当你要调用原型的方法时,它先在自身对象的找,没有就上原型链_pro_,看下这个属性有没有这个方法,所以会多一层查找。但是当有很多个实例时,相信内存更重要。
主要还是讲一下原型的利用,就是我所谓的小事啦
分享下我跟另一个partner写的扩展原型时很方便的方法。
var Utility={
beget:function(obj){
var f=function(){};
f.prototype=obj;
return new f();
},
extend:function(Sub,Super){
Sub.prototype=this.beget(Super.prototype)
},
extendPro:function(obj,extendFun){
if(typeof(obj)!==Object && !(obj instanceof Function))
return false;
for(var i=0;i < extendFun.length;i++){ obj.prototype[extendFun[i].funName]=extendFun[i].fun; } } }
让对象继承别的对象:
Utility.extend(Tower,Monster); //表示让Tower继承于Monster,及Monster.prototype==Tower._pro_
批处理自身扩展:
//将要扩展的方法名跟方法作为一个对象放到扩展数组里面,这样逻辑分明,修改也超级简单
Utility.extendPro(extendObj,[
{
funName:"init",
fun:function(initData){
//init code
}
},
{
funName:"getNews",
fun:function(name){
//get news code
}
}
]);
原型是JS的精粹,原型链的精妙
关于对象,关于对象的构造,可以写好多好多东西,在网上也有好多,但给一些觉得很不错的牛人的翻译:
原型链可以无穷无尽,它也区别了脚本语言跟C++后台语言,让Js变得很富特色,更有味道。
具体可以看一些这张经典的原型图,还是非常清晰的。
Published on 2012-04-13 20:53.
Filed under: web前端 (编辑此文)
在找暑假实习前还是毅然找了个地方实习,主要是锻炼下HTML5实践能力!于是简单的行李,一个钟的轻轨便到了深圳,一个小屋子里,几个不认识的人,开始了所谓的高强度开发!
游戏我放到stackMod上面,大家可以看看http://dev.seawar.jaycelin.stackmobapp.com/
经过了苦逼的一个星期终于将雏形搞掂,并在第二个星期完善了它,加入AI,测试成功!
What is easel
一个比较成熟的针对HTML5的JS框架
官方API
官方有非常详细的文档,第一天就在上面看API,看DEMO,有JS基础一下就入门,很快就可以实践。
先不谈AI,游戏的总体框架好重要
在Lanston的合作下,完成了初步架构,并在后面不断小完善,这时候Js原型的好处就发挥到淋漓尽致了。但究竟分多少层关键还是看每个游戏的需求,但始终觉得框架式开发比敏捷来得靠谱,这个问题还跟技术总监争了好久!
将所有动态的对象都看成一个组件,于是便有了一个第一层的原型component,它包含了初始化每个组件的图片,init跟dispose方法;接下来还有两层,逐步到最后的每一个动态对象上。
最后因为有很多关,需要有个Game对象出来,我们做的这个游戏支持离线玩,所以Model层也放在Browser端,所以大量的数据需要用manifest将它们cache起来!
总体框架有了就在这些对象中加入逻辑,一步步Debug!
封装得好好的easel
一层包着一层
保留了Canvas最外层的Stage包住了Canvas上所有元素,引入一个容器新概念Container,它可以包住一个或多个元素,方便总体操作,比如一个组件包含了几个元素,这个组件在移动时便可以对Container进行操作,接下来一层是封装得很好的一个个API,比如Shape,Graphics,Text,下面讲怎么封装得好!
剔除了原生API的烦杂操作,更加OO化
用原生API去Draw一个矩形
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
Details »
Published on 2012-03-30 22:08.
Filed under: html5+css3 (编辑此文)
先推荐网上不错的日期js库:http://momentjs.com/
其实这类资源网络上一抓一把,但是想要针对项目实用的还是不多,因为我接触的那类都经常在日期而非深入到hour、minute、second!所以想干脆自己编个小库吧,这样以后写代买将省力很多,在这里分享下,希望对大家有用!
接口+继承+代码优化思想
先分享下我觉得一个很不错的js编程小技巧,达到很大的代码共用性!
因为很多js库会在原生的对象上进行直接原型扩展,但这是很不好的习惯,不仅加重了每个新实例对象的内存消耗,而且容易造成污染性误解(以为有这东西)!而这也是建js库一个准则:尽量少的原型扩展,特别是越根部的对象!
js建库准则
js建库准则(Dean Edwards在开发base2时候的一些体会)翻译版:http://biaoge.me/2009/12/239
js建库学习好地方:http://ejohn.org/blog/building-a-javascript-library/
假如你有时间,再看一个建js库超级前沿的文档,包括css3,浏览器最新API(如querySelector) build-a-javascript-framework
用继承提高代码共用性
因为不在原生对象上进行扩展,所以需要一个对外的命名空间,而在这个对象下会有一些接口供外部调用,而为了提高本身js库的健壮性,需要在最大程度减小对外接口(最理想的就是只保存用户需要的接口)!
那么这里便有一个问题,我将它实例化吧:
var namespace={
IOfirst:function(){},
IOsecond:function(){}
}
在对象namespace下有个东西需要给IOfirst跟IOsecond共用,而又不想暴露这个接口!
我是通过继承将所有对外接口通过一个父接口包装,然后在一个init方法下统一赋值,而在init这方法下,由于闭包的作用,达到了代码的共用性!
具体做法:
动态添加对外接口,加强代码灵活性
Details »
Published on 2011-11-18 17:29.
Filed under: web前端 (编辑此文)
其实让javascript这种发展了很多年的历史的语言,很多代码多让人写了,基本你要的问题在网上都能找到答案!但是浏览器在更新,浏览器对js的支持情况,以及浏览器自身扩展的一些属性的情况下,一些代码将不能用,而也有一些也不那么高性能了!所以很多优秀的Js库也才在不断更新!而这方面也是一个优秀的编程者应该注意到的地方,下面给出几个我在项目过程发现的一些不错的解决兼容性的但比我们之前的方法好的方法:
获取元素的所有子元素,不包含chrome以及firefox下空的textNode
因为chrome跟firefox较新版本的浏览器(而chrome跟firefox大家都会保持比较高地版本)引进了firstElementChild、childElementCount、nextElementSibling等新的属性,而通过这些属性获取将非常高效!所以下面是根据这些新属性写出的获取children的函数!
//getChildren function : because the empty textNodes were distinguished as a childNode but ie not!
//arguments declaration:
//parentNode--the parent of the elements you want to get;
//example:var parent=document.getElementById("par"); var childs=[]; childs=getChildren(parent);
function getChildren(parentNode){
var parentNode=parentNode||document.body;
if(document.all)
return parentNode.child||parentNode.childNodes;
else
{
var childs=[];
var len=parentNode.childElementCount;
var firstElement=parentNode.firstElementChild;
for(var i=len-1;i>=0;i--)
{
childs.push(firstElement);
if(i!=0)
firstElement=firstElement.nextElementSibling;
}
return childs;
}
}
当然大家也会有getFirstChild、getLastChild,getNextSibling的需求吧,其实看上面就知一通百了!
function getFirstChild(parentNode){
var parentNode=parentNode||document.body;
if(br.ie)
return parentNode.firstChild;
else
return parentNode.firstElementChild;
}
function getLastChild(parentNode){
var parentNode=parentNode||document.body;
if(br.ie)
return parentNode.lastChild;
else
return parentNode.lastElementChild;
}
function getNextSibling(node){
if(br.ie)
return node.nextSibling;
else
return node.nextElementSibling;
}
Details »
Published on 2011-10-26 21:08.
Filed under: web前端 (编辑此文)