‘编写实现’ Category Archives
一
jQuery 1.4新增与更新
作者: boke ,分类: JavaScript
jQuery Minified (23kb Gzipped)
jQuery Development(156kb)
1.4重要新特性:
- 常用方法的性能大幅提升:重写了大部分较早期的函数;
- 更容易使用的设置函数(setter function):为所有对象新增了许多易用的设置函数;
- 对Ajax的改进:引入了许多Ajax和JSON处理方面的更新,包括HTML5元素的序列化;
- attribute(改进了.attr()的性能)、jQuery()核心函数、CSS(.css()性能有两倍提升)、特效和事件、DOM操作等也有显著改进
此次共有41个改动,包括25个更新,16个新增方法。参见:http://api.jquery.com/category/version/1.4/ 。网上看到金山简单翻译的jQuery 1.4新增与更新的文章。
七
CSS优化
作者: boke ,分类: markup language
本文总结了一些CSS优化的规则,包括一些简写技巧,选择器的使用,及其它注意事项。
css简写技巧
1、属性值为0,而非0px。如果属性值为0,那么可以不为其添加单位(如px、em等),例如:
padding: 10px 5px 0px 0px;
可以写成:
padding: 10px 5px 0 0;
2、背景属性合写。背景属性可能会包含设置背景色、背景图、背景图的位置和背景图重复方式的参数,可以将这些合并写到一句里,例如:
background-image: url(bg.png); background-position: 0 0; background-repeat: no-repeat;
可以写成:
background: url(bg.png) no-repeat 0 0;
4、文字属性合写。和背景一样,文字属性也有很多可能会用到的属性值,来个复杂的文字样式:
font-style:italic; font-variant:small-caps; font-weight:bold; font-size:12px; line-height:1.5; font-family:Arial, sans-serif;
可以写成:
font:italic small-caps bold 12px/1.5 Arial, sans-serif;
4、颜色简写。css里的颜色数值使用“#”加6个16进制数表示的。这6个数两个一组,分别表示red,green,blue的值,有人说如果每对的颜色值都是各自相等的,可以简写成一个数值。例如,#ff6600,可以简写成#f60。关于这写法没找到确实的数据证明对浏览器的渲染效率是否有影响,但十六进制的颜色值默认标准是大写字母及数字组成的六位数标注。在未知情况下不希望冒险而降低了渲染的效率,所以还是建议写成#FF6600。
5、margin、padding等的简写。有些人会这样声明边框:
margin-top:5px; margin-right:10px; margin-bottom:5px; margin-left:10px;
可以这样简写:
margin:5px 10px 5px 10px;
css里,像margin、padding、border-width等在写属性值时都是按照上-右-下-左的顺时针方向来写的,不要弄混顺序。像上面的例子,上下边距,左右边距分别是相等的,还可以进一步简化:
margin:5px 10px;
同理,padding、border-width等也是如此。
6、border的简写。举个例子,声明一个1像素的黑色实线边框:
border-width:1px; border-style:solid; border-color:#000;
可以简写成:
border:1px solid #000;
有人可能会记不清顺序,我通常这样记,“数字靠两端,字母摆中间”。
7、列表样式。用的不多,一个例子:
list-style-type:square; list-style-position:inside; list-style-image:url(filename.gif);
这样简写:
list-style:square inside url(filename.gir);
选择器使用
据说Mozilla样式系统的选择器是从右向左匹配的,这里有篇文章https://developer.mozilla.org/en/Writing_Efficient_CSS,看看就行,毕竟因为ie6的存在,像>,+这样的选择器我们是用不到的,关于浏览器对选择器的支持情况,可以看下http://www.masterboke.cn/2009/02/26/css-browser-support/。不管选择器是从左还是从右匹配,有三点注意一下:
1、避免使用通用选择符。想*{},#test *{}这样的选择器还是不要用。
2、不要用标签修饰id选择器。不要出现div #test{}这样的选择器,id选择器直接单独使用就行,不用再画蛇添足。
3、不要使用标签修饰class选择器。不要出现div .test{}这样的选择器。
其它注意事项
1、对css代码进行压缩。不管开发时是啥样的,呈现到用户面前时必定是压缩过的。
2、应该将CSS 放置于结构的上方(一般放置于head 元素内)。CSS 是解释型语言,ff和ie在等待CSS 传输完成之前不会渲染任何东西。放在顶部,浏览器能够有针对性的对 HTML 页面从顶到下进行解析和渲染。
3、尽量使用 <link rel=”stylesheet” href=”http://www.masterboke.cn/test.css” type=”text/css”> 的样式导入方式,而减少@import 的使用,更不要使用多层嵌套的@import 。因为在 IE 里,@import 相当于将<link> 放在页面尾部。
4、不要使用css表达式。CSS Expressions这个东西好像也只有ie里能用。
5、将css从页面里剥离,放在单独的文件里,这样能够使用缓存、压缩等策略,当然要注意不要引入过多的css文件,会增加http请求的。
6、避免使用滤镜。IE独有属性AlphaImageLoader用于修正7.0以下版本中显示PNG图片的半透明效果。这个滤镜的问题在于浏览器加载图片时它会终止内容的呈现并且冻结浏览器。在每一个元素(不仅仅是图片)它都会运算一次,增加了内存开支,因此它的问题是多方面的。完全避免使用AlphaImageLoader的最好方法就是使用PNG8格式来代替,这种格式能在IE中很好地工作。如果确实需要使用AlphaImageLoader,请使用下划线_filter又使之对IE7以上版本的用户无效。
六
Javascript类及对象创建
作者: boke ,分类: JavaScript
关于js里的类与c#等的区别,不在这里提,只说一下怎么创建类及对象。js里创建类及对象很简单,例如:
function Room(c){
this.windows = c;
this.owner = "A";
this.show = function(){
alert(this.windows);
}
}
var r1 = new Room(4);
var r2 = new Room(2);
像这样创建对象时,每次都会创建一个show()函数的副本,但其实r1和r2两个对象是可以共享同一个show()的,这样就浪费了内存。
当然,也有不浪费内存的写法,这就要用到对象的prototype属性,可以把它看成创建新对象所依赖的原型。当new时,原型的所有属性都被赋予新创建的对象,新对象中只有一个指向类中某个函数的指针,不会再生成一个副本。如下:
function Room(){
};
Room.prototype.windows = 2;
Room.prototype.owner = "A";
Room.prototype.show = function(){
alert(this.windows);
}
var r1 = new Room();
var r2 = new Room();
这样虽然没浪费内存,但当你想给r1做一些改动,比如,r1.owner=”B”,这时,r2也会被改动,这样肯定不行。
啊,不是说js里创建类和对象很简单嘛,怎么还有这么多问题呀!别急,其实第一个例子已经成功达到目的了,我们这不是在寻找更合理的创建对象的方法嘛。
说说我常用的两种创建对象的简单方法,不知道是不是最好的,依我目前所知,应该是最简单通用的了。
这有一种,学名叫啥我忘了,似乎应该有个学名吧。其实就是前面两种方法的混合使用,用第一种方法定义对象的属性,用prototype原型链定义对象的方法。再看上面那个例子:
function Room(c){
this.windows = c;
this.owner = "A";
}
Room.prototype.show = function(){
alert(this.windows);
}
var r1 = new Room(4);
var r2 = new Room(2);
r1.owner = "B";
alert(r1.owner);//输出B
alert(r2.owner);//输出A
恩,就这样。既不浪费,又可以针对具体对象做修改。还行,嚎~
再来看看另外一种方法,直接创建实例对象。如下:
Room={
init:function(c){
this.windows = c;
this.owner = "A";
},
show:function(){
alert(this.windows);
}
}
Room.init(4);
Room.owner = "B";
Room.show();
这种方式直接创建了一个对象,自然也就不用new了。不会像第一个例子那样浪费内存,也不会出现第二个例子那样情况(别问我为什么)。
在大多数情况下,我会使用第3,4个例子的方式创建类及对象,简单,通用,雅俗共赏。至于怎么用这两种方式?这就要看情况了。举例来说,页面里有一个菜单栏menu,菜单栏里会有多个菜单项menuItem,这样我就会像第四个例子那样创建一个menu对象,然后像第三个例子那样创建多个menuItem对象,并将其添加到menu里。
好久没写了,不知道容不容易看懂。上面仅仅是我的理解,哪里有问题,忘高手指正。
六
javascript性能优化(不定期整理)
作者: boke ,分类: JavaScript
javascript的性能优化通常包括两个方面,一是代码体积减小带来的下载时间减少,二是代码效率提高带来的执行时间减少。
关于代码体积的改善
javascript不同于c、c++这样的编译语言,开发人员不需要考虑变量名长度及注释等,因为在编译时这些都会被删除或替换。javascript的所有内容都需要下载到客户端执行,这些无意义的东西最好还是删除或缩小为妙。下面常用的减少代码体积的方法:
1、删除注释。
2、删除空格,制表符,换行符。
3、替换变量名。
当然,上面的三种情况不需要手工去做的,我们可以使用一些压缩工具JSMin,YUI Compressor,还有我正在用的Packer,TB Compressor等。经研究表明,即使是通过Gzip压缩后的代码,如果通过上面那些工具先进行一步压缩,仍可减少5%以上的体积。
4、避免使用布尔值。在js里true等于1,false等于0。
5、缩短否定检测。js代码里有许多检测某个值是否有效的判断语句,像
if(typeof g == undefined){
return;
}
if(g == null){
return;
}
if(g == false){
return;
}
if (g == 0){
return;
}
上面的语句除非变量g可以为0,或没有在作用域链中出现过,我们要用0和undefined判断,否则,这样写更简洁:
if (!g){
retrun;
}
关于代码效率的改善
=======================================(分割线,以上2009-07-08)
1、仔细检查函数中所有使用的变量,如果有一个变量不是当前作用域定义的,而且使用了不止一次,那么我们就应该把这个变量保存在局部变量中,而使用这个局部变量来进行读写操作。这样可以帮助我们将作用域外的变量的搜索深度减少到1。这对全局变量尤为重要,因为全局变量总是被放到作用域链的最后位置来搜索。
2、避免使用with语句。因为它会修改执行上下文(Execution Context)的作用域链,在最前面添加一个对象(Variable Object)。这就意味着在执行with的过程中,实际上的局部变量都被移到作用域链上的第二个位置,这会带来性能上的损失。
3、如果你确定一段代码肯定会抛出异常,那么就要避免使用try-catch,因为catch分支在作用域链上的处理方法和with是一样的。但try分支的代码是没有性能损失的,所以还是建议用try-catch来捕获那些不可预知的错误。
======================================(2009-06-19的分割线)
4、使用join代替“+”或“for”连接字符串。例如:
var wrap = [ vanillaDiv
, '<div style="position:absolute;top:0px;left:0px;padding:0px;'
, 'margin:0px;overflow:visible;height:'
, Dock.height, 'px;width:', Dock.width
, 'px;"></div></div>'
].join('');
现代浏览器对“+”连接字符串都做了相应的优化,可以根据应用情况和自己喜好权衡使用。这有文章Fastest way to build an HTML string,作者James Padolsey,一个很牛逼的18岁小孩。
5、将可反复利用的内容放在循环体外部。例如:
下面这个函数从DOM树中选择一个元素,然后对这个元素做一些事情。如果像下面这样,需要每次都从树中去找container这个元素,而且会每次都进行someArray的长度计算。
for(var i = 0; i < someArray.length; i++) {
var container = document.getElementById('container');
dosomething();
}
改进一下,写成下面这样,达到了同样的目的,却不用每次搜索DOM树了,也不用每次都计算someArray的长度了。
var container = document.getElementById('container');
for(var i = 0, len = someArray.length; i < len; i++) {
dosomething();
}
类似的应用还有地图蒙板这个例子,每次鼠标移动都要调用mbCtrl.move()这个函数,可以看一下index.js里的112-115行,如果直接这样用$(“#mapContainer > div:nth-child(3)”)选择器选择,每次鼠标移动都要搜索DOM树,实际应用时效率奇低呀。改进一下,
var mbCtrl = {
init:function(){
this.div3=$("#mapContainer > div:nth-child(3)");
},
move:function(){
this.div3=css({top:'0',left:'0',width:this.mwth,height:tc,display:"block",background:"#f00"});
}
}
在一开始就把要操作的对象选择出来,缓存起来,这样一个小改动,在一些浏览器(ie)里有成百倍的效率提高。
6、用{}代替new Object(),用[]代替new Array()。
这个不多说了,就是使用var obj={},arr=[]代替var obj=new Object(),arr=new Array(),这样就不用再进行一次实例化了,而且减少了代码体积。至于到底啥时候用object,啥时候用array,”A common error in JavaScript programs is to use an object when an array is required or an array when an object is required. The rule is simple: when the property names are small sequential integers, you should use an array. Otherwise, use an object.” – Douglas Crockford
=============================================(2009-07-03分割线)
7、if与switch的使用。if语句在js里应用很多,因为用户可能的操作习惯实在是没有规律,应该尽量减少判断执行次数。通常采用的优化方式有两种,最大可能排最前和分组判断。举个例子:
if (value > 3){
if (value == 5){
return result5;
}
else if (value == 4){
return result4;
}
else{
return result6;
}
}
else{
if (!value){
return result0;
}
else{
if (value == 1){
return result1;
}
else if (value == 2){
return result2;
}
else{
return result3;
}
}
}
让面的例子对if判断进行了分组,而且把最可能出现的判断条件放在了前面。当判断条件较多时,使用switch语句的效率要好于if语句,可以根据情况尽量使用switch语句处理。js里还有个好处就是case语句里可以使用任何类型的值,例如:
switch(value){
case "yellow":
alert('fuck');
break;
case "green":
alert('yada');
break;
case 3:
alert("what");
break;
default:
alert("go to hell");
}
这里还有个据说更好的方法:
switch(value){
case 0:
return result0;
case 1:
return result1;
case 2:
return result2;
case 3:
return result3;
case 4:
return result4;
case 5:
return result5;
}
var results = [result0, result1, result2, result3, result4, result5];
return results[value];
可以看出使用switch和数组达到了相同目的,但使用数组的效率更高一些。这里有个测试地址http://www.nczonline.net/experiments/javascript/performance/conditional-branching/
8、使用更有效率的循环写法。据说能节省50%的执行时间:
var values = [1,2,3,4,5];
var length = values.length;
//for loop
for (var i=length; i--;){
process(values[i]);
}
//do-while loop
var j=length;
do {
process(values[--j]);
} while (j);
//while loop
var k=length;
while (k--){
process(values[k]);
}
至于for-in循环,再对对象进行操作时很好用,但比较低效,所以除非要遍历对象,否则还是不要用它了。
9、优先使用javascript内置方法。js的内置方法都是在浏览器里的,通常都是由C++编译的,这比自己写的js方法要快很多很多。
10、最小化语句数量。对于js这种解释性语言,有理由相信语句越少,执行所需的时间越短。有几个例子:
var a = 3; var b = "yada"; var c = [1,2,3]; var d = new Date(); var name = names[i]; i++; var obj = new Object(); obj.color = "red"; obj.num = 5;
优化一下:
var a = 3, b = "yada", c = [1,2,3], d = new Date();
var name = names[i++];
var obj = {color:"red", num:5};
四
倒着想问题
作者: boke ,分类: 编写实现
倒着想问题,其实就是一种思维方式,即逆向思维,其实平时我们都会用这种思维方式去想问题,只是可能没注意到。这几天写程序时,特别注意了一下逆向思维的应用,发现它确实是一种非常好用的思维方式,能解决很多问题,结合几个实例,总结总结。
先看一个在地图上加蒙板的例子。

我要实现一个功能,当鼠标在地图上拖动时,会拖出一个矩形框,如上图中的5部分所示。让我们想想应该怎么实现?5部分是由1、2、3、4四个div围成的,我们是不是可以先确定好1、2、3、4这四个div,然后自然就形成了5部分呢?答案当然是肯定的,我们只要规定好1、2、3、4的top、left、width、height四个属性,就能确定这四个div,而它们的top、left、width、height属性,我们都可以通过鼠标的坐标来得到。当鼠标按下时,我们可以确定1、2的top、left、width、height,还有3的left,width。当拖动完成抬起鼠标时,剩下的3,4也可以确定了。这样,我们就实现了想要的效果。
这样做不管从理论上,还是实践上讲都没有问题,但是现实往往是残酷的。刚才这种情况,我们是从左上角开始拖动,会不会有人从右上角开始拖呢?答案也是肯定的,会,不但会从右上角拖,从右下角,左下角拖的也有,要好能拖出朵花来,我毫不怀疑有人会下大功夫拖出一朵玫瑰花来。如果不确定从哪儿开始拖,我们就不知道到底先确定哪几个div,只能分四种情况考虑,这样代码就麻烦了。
作为一个合格的社会主义新中国的IT民工,我决不允许这样的代码出现。我们不妨把问题倒过来想想。刚才是先确定的1、2、3、4四个div,由它们围成一个确定的5,那要是先确定5,再通过它去确定1、2、3、4呢?理论上是可行的。通过5的top,我们可以确定1;通过5的left、top,我们可以确定2;通过5的left、height,我们可以确定3;通过5的top、height、width,我们可以确定4。理论上可行,实践上我们也可以实现,5的top、left属性,永远都等于两次鼠标坐标变化时小的那个,5的width、height属性,永远都等于两次鼠标变化时差的绝对值。ok,搞定,简单多了吧。
再来看一个例子。
我给公司首页加输入的自动提示时,曾经考虑过不在页面里加一个定时器,而是采用射雕博客里的这篇文章里的方案三,当输入框里的文字发生变化,就使用oninput或onpropertychange事件发送请求。这里有个需要注意的地方,当用户输入过快时,这些不必要的请求是不应该被发送出去的,以防止服务器负担过重。不过这就有个问题,怎么确定到底发不发请求呢?当时射雕(也就是淘宝的正淳)教我设定一个时间阈值,判断两次文字发生变化的时间间隔,当时间间隔小于这个阈值时,就不发送请求,否则就发送。但这样做其实是不可行的,因为最后一次输入后就再也没有输入了,就得不到一个时间间隔,从而就不知道到底发送不发送这次请求。当时我也没想出来,就采用了像现在baidu,google等的方式,加一个定时器。
后来我又要实现一个功能,用户拖动地图时,地图中心点发生变化,这时我要用ajax将中心点的经纬度发给一个php程序处理,但如果用户拖动过快,即用户还没有确定要看哪部分地图,那么就不发送请求。上面那种情况同样的问题,我无法确定最后一次请求发送还是不发送。
让我们认真的想想。正常的思路是,默认不发送请求,当时间间隔大于某个阈值时,就发送请求。那么我们可不可以倒过来呢?默认发送请求,当时间间隔小于某个阈值,就把发送请求的动作取消。理论上没啥问题,现实中我们也可以实现,js有个东西叫setTimeout不是。于是乎,上面两个问题都搞定了,来看看地图的效果。
以上,我们可以看出逆向思维确实是可以帮助我们在程序开发的过程中解决许多难题的,当我们遇到难已解决的问题时,不妨试试。最后,我想说的是,在解决问题时,我们应该让脑袋灵活点,一条思路走不通,我们可以倒着、侧着,站着、躺着、走着,全方位、立体式、多角度的思考问题。