邵珠庆の日记 生命只有一次,你可以用它来做很多伟大的事情–Make the world a little better and easier


123月/15

Console命令详解,让调试js代码变得更简单

发布在 邵珠庆


控制台(Console)是Firebug的第一个面板,也是最重要的面板,主要作用是显示网页加载过程中产生各类信息。

一、显示信息的命令

Firebug内置一个console对象,提供5种方法,用来显示信息。

最简单的方法是console.log(),可以用来取代alert()或document.write()。比如,在网页脚本中使用console.log("Hello World"),加载时控制台就会自动显示如下内容。

另外,根据信息的不同性质,console对象还有4种显示信息的方法,分别是一般信息console.info()、除错信息console.debug()、警告提示console.warn()、错误提示console.error()。

比如,在网页脚本中插入下面四行:

  console.info("这是info");

console.debug("这是debug");

console.warn("这是warn");

console.error("这是error");

加载时,控制台会显示如下内容。

可以看到,不同性质的信息前面有不同的图标,并且每条信息后面都有超级链接,点击后跳转到网页源码的相应行。

二、占位符

console对象的上面5种方法,都可以使用printf风格的占位符。不过,占位符的种类比较少,只支持字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o)四种。

比如,

  console.log("%d年%d月%d日",2011,3,26);

console.log("圆周率是%f",3.1415926);

%o占位符,可以用来查看一个对象内部情况。比如,有这样一个对象:

  var dog = {} ;

dog.name = "大毛" ;

dog.color = "黄色";

然后,对它使用o%占位符。

  console.log("%o",dog);

三、分组显示

如果信息太多,可以分组显示,用到的方法是console.group()和console.groupEnd()。

  console.group("第一组信息");

console.log("第一组第一条");

console.log("第一组第二条");

console.groupEnd();

console.group("第二组信息");

console.log("第二组第一条");

console.log("第二组第二条");

console.groupEnd();

点击组标题,该组信息会折叠或展开。

四、console.dir()

console.dir()可以显示一个对象所有的属性和方法。

比如,现在为第二节的dog对象,添加一个bark()方法。

  dog.bark = function(){alert("汪汪汪");};

然后,显示该对象的内容,

  console.dir(dog);

五、console.dirxml()

console.dirxml()用来显示网页的某个节点(node)所包含的html/xml代码。

比如,先获取一个表格节点,

  var table = document.getElementById("table1");

然后,显示该节点包含的代码。

  console.dirxml(table);

六、console.assert()

console.assert()用来判断一个表达式或变量是否为真。如果结果为否,则在控制台输出一条相应信息,并且抛出一个异常。

比如,下面两个判断的结果都为否。

  var result = 0;

console.assert( result );

var year = 2000;

console.assert(year == 2011 );

七、console.trace()

console.trace()用来追踪函数的调用轨迹。

比如,有一个加法器函数。

  function add(a,b){

return a+b;

}

我想知道这个函数是如何被调用的,在其中加入console.trace()方法就可以了。

  function add(a,b){

console.trace();

return a+b;

}

假定这个函数的调用代码如下:

  var x = add3(1,1);

function add3(a,b){return add2(a,b);}

function add2(a,b){return add1(a,b);}

function add1(a,b){return add(a,b);}

运行后,会显示add()的调用轨迹,从上到下依次为add()、add1()、add2()、add3()。

八、计时功能

console.time()和console.timeEnd(),用来显示代码的运行时间。

  console.time("计时器一");

for(var i=0;i<1000;i++){

for(var j=0;j<1000;j++){}

}

console.timeEnd("计时器一");

九、性能分析

性能分析(Profiler)就是分析程序各个部分的运行时间,找出瓶颈所在,使用的方法是console.profile()。

假定有一个函数Foo(),里面调用了另外两个函数funcA()和funcB(),其中funcA()调用10次,funcB()调用1次。

  function Foo(){

for(var i=0;i<10;i++){funcA(1000);}

funcB(10000);

}

function funcA(count){

for(var i=0;i<count;i++){}

}

function funcB(count){

for(var i=0;i<count;i++){}

}

然后,就可以分析Foo()的运行性能了。

  console.profile('性能分析器一');

Foo();

console.profileEnd();

控制台会显示一张性能分析表,如下图。

标题栏提示,一共运行了12个函数,共耗时2.656毫秒。其中funcA()运行10次,耗时1.391毫秒,最短运行时间0.123毫秒,最长0.284毫秒,平均0.139毫秒;funcB()运行1次,耗时1.229ms毫秒。

除了使用console.profile()方法,firebug还提供了一个"概况"(Profiler)按钮。第一次点击该按钮,"性能分析" 开始,你可以对网页进行某种操作(比如ajax操作),然后第二次点击该按钮,"性能分析"结束,该操作引发的所有运算就会进行性能分析。

十、属性菜单

控制台面板的名称后面,有一个倒三角,点击后会显示属性菜单。

默认情况下,控制台只显示Javascript错误。如果选中Javascript警告、CSS错误、XML错误都送上,则相关的提示信息都会显示。

这里比较有用的是"显示XMLHttpRequests",也就是显示ajax请求。选中以后,网页的所有ajax请求,都会在控制台面板显示出来。

比如,点击一个YUI示例,控制台就会告诉我们,它用ajax方式发出了一个GET请求,http请求和响应的头信息和内容主体,也都可以看到。

271月/15

Code Review 同行代码审查实战分析

发布在 邵珠庆

代码审查(Code Review)是软件开发中常用的手段,和QA测试相比,它更容易发现较难发现的问题,还可以帮助团队成员提高编程技能,统一编程风格等。本文作者从实际出发,详细分析了开发者在代码审查过程中会遇到的问题及解决方法。

以下为译文:

数百万年前,人类祖先人猿学会直立行走——解放双手——最终进化到人;而代码审查在开发过程中有着异曲同工之妙——区别出野蛮开发和先进开发。

然而,在实际工作中,以下声音总不绝于耳:

  1. “代码审查在项目中简直就是浪费时间!”
  2. “我根本没有时间去做复查。”
  3. “由于我那个谨慎的拍档还没做好复查,进度只能延后了。”
  4. “你确定,我的同事要求我修改代码吗?请向他们解释一下,任何轻微地改动都会让我的代码失去原有的优雅。”

那么问题来了,我们为什么需要做代码复查?

作为专业的软件开发人员,持续提高代码质量是工作生涯不断追求的目标之一。无论我们有多么优秀,都离不开团队;而代码复查是个人与团队的润滑剂:

  • 当局者迷,旁观者清。代码复查如同为我们安装了后视镜;
  • 使我们的代码多了至少一个知音人;
  • 能帮助新员工在这个过程中学习和领悟到前辈的代码精髓;
  • 有助开展知识共享,众“智”成城。

要做就要做到最好

如果想要达成上述种种美好结果,是离不开时间和工作的科学安排的。仅仅做好代码缩进、变量命名规范等基本工作,还不能算得上完美。而如果曾经尝试过结对编程,或许你会发现其所花的时间往往都比代码复查要多。

我的建议是用开长总时长的四分之一时间来进行代码复查。举例来说,如果开发总用时为两天,那么应该花上大约四个小时来进行复查。

当然,如果能把事情做对,可不必拘泥于时间的多少。进一步说,我们必须能看明白将要复查的代码。这不仅代表要掌握基本的语言语法,更关键的是要掌握整个代码的架构,所用组件或库等细节。如果不能做到对每一行代码所做的事情都了然于胸,这样的复查工作是没有多少价值的。所以要做好这点是不能一味讲求速度的,必须花一番功夫来从头到尾对代码进行梳理分析。

此外还有两件事是务必要做到的:

  1. 复查工作中包含所有必须的测试工作;
  2. 做好设计文档的编写工作。

避免拖延症

今天的工作今天完成是最完美的工作状态,否则一旦拖延症出现,再多再好的复查都只会成为开发过程中的绊脚石。好的复查需要紧密而持久的努力,不是搞搞突击就能做好的。

因此,开发者应当尽力做到日清日结。把复查作为每天工作的开端是个不错的主意;理清旧的思路将有助于开展新的编码任务。也或许有的人喜欢在午休或下班前进行,无论在什么时候进行,以下几点是应该避免的:

  • 让积压工作越积越多;
  • 由于复查没有做好而导致进度延后;
  • 由于代码更新频率快就放弃做复查;
  • 往往在最后一刻才去做复查。

编写出可复查的代码

不应该把所有复查工作都推给复查员。如果我的同事花了一周时间添加了看起来比较乱的代码,这对复查工作无疑是重大打击,也很难让人摸清其思路和结构。

所以我们在编程时,要有意识地把代码划分为可操作单元。我们使用的方法是scrum,它为我们的开发工作做了很明晰的指导,同时使得整个开发过程有迹可循,便于进行追溯和回顾。

其次,在与复查员进行讨论前要搞好关系。这样将有助于双方对彼此有所了解,从而减少讨论时矛盾发生的机率。

再者,项目结构应当在设计文档中描述得清楚具体。这对于项目新成员的成长是大有裨益的,同时能帮助复查员提高工作效率。

最后也是最重要的一点是在自我复查过程中做好注释。换言之要先自行对代码过一遍,把需要做出说明的地方标示出来并解释清楚。有研究表明,开发者在对自己的进行复查和注释时,经常会找出不少瑕疵。

大型代码重构

有时候如果需要进行代码重构,这势必会影响到很多组件,特别是较大型的应用。在这种情况下,最好的解决方案是逐步推进重构工作。先对要做的变更进行划分,然后根据修改意图进行分段式重构。当这部分变更完成并做好复查后,再执行第二部分的重构,重复该步骤直至完成全部工作。这或许增加了重构用时,但会带来更高质量的代码同时可以减轻复查员的工作量。

如果实际情况真的不允许进行逐步重构,可以试试结对编程。

解决矛盾

在一个技术团队中,各人有各自的观点,如何达成共识是成败的关键。作为开发者,应该保持开明的心态并虚心接受不同的意见。避免固步自封,避免对自我复查工作的不屑一顾。如果有人提议把我们一些重复的代码做成一个可复用函数,这并不代表我们之前的工作是毫无价值的。

而作为复查员,要懂得人情世故。在给出修改意见前,先考虑清楚这真的会更好抑或仅仅是风格上的不同看法。提议说法可以是:“如果尝试另一种方法,或许会更好”或“有同事建议这样做”,而要避免的是:“就连我家宠物都能写出比这好的算法!”

如果真的一时僵持不下,争议双方不妨请教第三个开发人员,从他的角度来再次审度各自的观点,直到形成共识,三人行,必有我师焉。

107月/14

开发者的10个最佳代码游乐场

发布在 邵珠庆

如果你是开发者,在寻找最佳的代码游乐场,享受代码的乐趣,那么你来到了正确的地方。在这篇文章中我已经收集了用于测试、调试和分享代码的10个最好的免费代码游乐场(Code Playground)和沙盒工具。如果你知道其他好的代码游乐场,可以通过评论告诉我。Enjoy!

1. Codepen

 CodePen是一个web前后端游乐场。它基于激发、传授和分享。能高亮热门示例(“Pen”),且提供高级功能,如共享和嵌入。需要建立一个减少测试案例来演示并找出一个bug?CodePen绝对适合这个场景。想炫耀你的最新创作和获得同行的反馈? CodePen是绝一不二的选择。

2. CSSDesk

CSSDesk是一个在线CSS沙箱。它使人们能够快速测试的CSS代码片段,并观察实时结果。我有非常看好这个项目。有行号和文本框输入实时语法高亮显示。其中一个最好的一点是它允许用户与他人分享他们的代码。

3. Google’s Code Playground

 谷歌的代码游乐场是一个基于Web的工具,可以让Web开发人员尝试谷歌所有提供的API,调整代码,和查看结果。谷歌的代码游乐场可以让您尽情享受而不必打开一个外部编辑器,所有的API都被装载在(左侧)“Pick an API”框中。

4. JS Bin

JS Bin是一个web app,专门设计于帮助JavaScript和CSS亲们在某些上下文中测试和协作调试代码。 JS Bin允许你编辑和测试JavaScript和HTML。一旦满意,你可以保存并发送这URL给同行审查或寻求帮助。然后,他们可以做更进一步的修改,如果需要保存更新。

5. JSFiddle

JSFiddle中是一个shell编辑器,通过创建基于流行JS框架的自定义环境,简化JavaScript的编写。你可以选择框架和版本(可选的有MooTools,jQuery,ojo, Prototype, YUI, Glow,Vanilla)。此外,如果行的话,你可以添加jQuery UI或MooTools More之类的免费框架。它具有一个极好的特征是能够产生一个唯一的URL以保存和分享代码。另外,JSFiddle也自带嵌入特征。它是无需网站就可尝试和分享JS代码的极佳平台。

6. Dabblet

 Dabblet是快速测试CSS和HTML代码片段的交互式游乐场。它使用-prefix-free,不必在你的CSS代码添加任何前缀。可以在Github gist保存工作内容,将其嵌入到其他网站和与他人分享。它目前只支持Chrome,Safari和Firefox的现代版本。所有发布的代码属于发布者,没有强制许可证。 Dabblet本身是开源软件,在NPOSL-3.0许可下发布。

7. Editr

Editr是一个集HTML,CSS和JavaScript的游乐场,你可以在自己的服务器上托管。它是基于ACE的编辑器。它的超级容易安装。它支持一个页面多个实例。可以通过JS对象或HTML属性进行配置。 Editr支持3种视觉布局:水平,垂直和单一。前两个是用于在线编辑。第三个是用于演示。 Editr基于MIT License。

8. Livewave

 Liveweave是用于HTML5、CSS3、JavaScript的游乐场和用于网页设计师和开发者实时编辑器。这是用来测试,练习和分享成果的好工具!面板大小可调,可以让你随心所欲写(或织,如我们称呼它名字一样)代码。 Liveweave还内置了对HTML5和CSS3的标签/属性上下文敏感的代码提示,使生活变得更加简单。

9. D3 Playground

D3.js设计目的在于让你能够以交互方式使用D3.js库。每一次编辑(产生有效代码)都会实时影响D3.js游乐场。因为CSS是可视化的重要组成部分,你也可以在线编辑CSS。

10. HTML5 Playground

 HTML5的游乐场包括一个可以看HTML5动作的代码库。它们包括一些基础启动例程,如HTML5 Doctype和音频标记(仍然是兼容浏览器中回放特性的好demo)。拥有一个交互表格,包含HTML5元素,如范围、日期输入和针对电子邮件地址和网站的自动验证。

155月/14

升级Universal Analytics部署代码

发布在 邵珠庆

https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced
 
<!-- Google Analytics -->
<script>
/**
 * Creates a temporary global ga object and loads analy  tics.js.
 * Paramenters o, a, and m are all used internally.  They could have been declared using 'var',
 * instead they are declared as parameters to save 4 bytes ('var ').
 *
 * @param {Window}      i The global context object.
 * @param {Document}    s The DOM document object.
 * @param {string}      o Must be 'script'.
 * @param {string}      g URL of the analytics.js script. Inherits protocol from page.
 * @param {string}      r Global name of analytics object.  Defaults to 'ga'.
 * @param {DOMElement?} a Async script tag.
 * @param {DOMElement?} m First script tag in document.
 */
(function(i, s, o, g, r, a, m){
  i['GoogleAnalyticsObject'] = r; // Acts as a pointer to support renaming.

  // Creates an initial ga() function.  The queued commands will be executed once analytics.js loads.
  i[r] = i[r] || function() {
    (i[r].q = i[r].q || []).push(arguments)
  },

  // Sets the time (as an integer) this tag was executed.  Used for timing hits.
  i[r].l = 1 * new Date();

  // Insert the script tag asynchronously.  Inserts above current tag to prevent blocking in
  // addition to using the async attribute.
  a = s.createElement(o),
  m = s.getElementsByTagName(o)[0];
  a.async = 1;
  a.src = g;
  m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-XXXX-Y', 'auto'); // Creates the tracker with default parameters.
ga('send', 'pageview');            // Sends a pageview hit.
</script>
<!-- End Google Analytics -->
811月/13

谷歌Web界面和搜索语言代码

发布在 邵珠庆

谷歌Web界面和搜索语言代码

本文包含谷歌Web界面语言列表
验证码(hl=)和搜索语言代码(lr=) ,可以被插入
Web浏览器时,在地址栏的URL (网页地址)
使用谷歌的服务,如果你的问题,请
与谷歌的语言设置。在那里的那一刻( 2012年6月)
149 Web界面语言代码和46个搜索语言代码。该
已收集到的HTML源代码的谷歌语言代码
首选项页面,你可以找到他们从那里还可以。此页面
主要的目的是列出的语言代码,这样你就可以复制
您正在使用的URL的字符(例如HL = EN或LR = lang_en )
但你也可以直接到您的首选语言界面
通过点击相应的链接在一个网页或搜索语言页
下面的两个列表。

记住,不是所有的谷歌服务已被翻译成
下面列出的所有语言。此外,增加后的语言代码
任何的URL不会起作用,所以你可能需要做一些试验
找出你可以将它们添加。

用法示例
例如,如果您使用的是谷歌网页搜索页面和URL
在您的网页浏览器的地址栏中:

   http://www.google.com/search ?

您可以定义web界面语言是英语,加入
HL = EN结尾的网址,变更后的网址看起来
像这样:

   http://www.google.com/search?hl=en

当你使用一个以上的设置代码,你需要在URL中使用
与他们之间的字符。例如,如果你也想定义
搜索语言是英语,你需要使用&字符
像这样的代码之间:

   http://www.google.com/search?hl=en&lr=lang_en

因为上面的URL可能不会工作,使选定的搜索
语言将被使用,如果你尝试在该网页上做一个搜索,你也
需要添加一个搜索查询的URL (Q =) ,所以产生的URL
是:

   http://www.google.com/search?hl=en&lr=lang_en&q=example

记住按后键盘上的回车键(或等效键)
添加了所需的字符。

Web界面语言代码
hl=af          Afrikaans
hl=ak          Akan
hl=sq          Albanian
hl=am          Amharic
hl=ar          Arabic
hl=hy          Armenian
hl=az          Azerbaijani
hl=eu          Basque
hl=be          Belarusian
hl=bem         Bemba
hl=bn          Bengali
hl=bh          Bihari
hl=xx-bork     Bork, bork, bork!
hl=bs          Bosnian
hl=br          Breton
hl=bg          Bulgarian
hl=km          Cambodian
hl=ca          Catalan
hl=chr         Cherokee
hl=ny          Chichewa
hl=zh-CN       Chinese (Simplified)
hl=zh-TW       Chinese (Traditional)
hl=co          Corsican
hl=hr          Croatian
hl=cs          Czech
hl=da          Danish
hl=nl          Dutch
hl=xx-elmer    Elmer Fudd
hl=en          English
hl=eo          Esperanto
hl=et          Estonian
hl=ee          Ewe
hl=fo          Faroese
hl=tl          Filipino
hl=fi          Finnish
hl=fr          French
hl=fy          Frisian
hl=gaa         Ga
hl=gl          Galician
hl=ka          Georgian
hl=de          German
hl=el          Greek
hl=gn          Guarani
hl=gu          Gujarati
hl=xx-hacker   Hacker
hl=ht          Haitian Creole
hl=ha          Hausa
hl=haw         Hawaiian
hl=iw          Hebrew
hl=hi          Hindi
hl=hu          Hungarian
hl=is          Icelandic
hl=ig          Igbo
hl=id          Indonesian
hl=ia          Interlingua
hl=ga          Irish
hl=it          Italian
hl=ja          Japanese
hl=jw          Javanese
hl=kn          Kannada
hl=kk          Kazakh
hl=rw          Kinyarwanda
hl=rn          Kirundi
hl=xx-klingon  Klingon
hl=kg          Kongo
hl=ko          Korean
hl=kri         Krio (Sierra Leone)
hl=ku          Kurdish
hl=ckb         Kurdish (Soranî)
hl=ky          Kyrgyz
hl=lo          Laothian
hl=la          Latin
hl=lv          Latvian
hl=ln          Lingala
hl=lt          Lithuanian
hl=loz         Lozi
hl=lg          Luganda
hl=ach         Luo
hl=mk          Macedonian
hl=mg          Malagasy
hl=ms          Malay
hl=ml          Malayalam
hl=mt          Maltese
hl=mi          Maori
hl=mr          Marathi
hl=mfe         Mauritian Creole
hl=mo          Moldavian
hl=mn          Mongolian
hl=sr-ME       Montenegrin
hl=ne          Nepali
hl=pcm         Nigerian Pidgin
hl=nso         Northern Sotho
hl=no          Norwegian
hl=nn          Norwegian (Nynorsk)
hl=oc          Occitan
hl=or          Oriya
hl=om          Oromo
hl=ps          Pashto
hl=fa          Persian
hl=xx-pirate   Pirate
hl=pl          Polish
hl=pt-BR       Portuguese (Brazil)
hl=pt-PT       Portuguese (Portugal)
hl=pa          Punjabi
hl=qu          Quechua
hl=ro          Romanian
hl=rm          Romansh
hl=nyn         Runyakitara
hl=ru          Russian
hl=gd          Scots Gaelic
hl=sr          Serbian
hl=sh          Serbo-Croatian
hl=st          Sesotho
hl=tn          Setswana
hl=crs         Seychellois Creole
hl=sn          Shona
hl=sd          Sindhi
hl=si          Sinhalese
hl=sk          Slovak
hl=sl          Slovenian
hl=so          Somali
hl=es          Spanish
hl=es-419      Spanish (Latin American)
hl=su          Sundanese
hl=sw          Swahili
hl=sv          Swedish
hl=tg          Tajik
hl=ta          Tamil
hl=tt          Tatar
hl=te          Telugu
hl=th          Thai
hl=ti          Tigrinya
hl=to          Tonga
hl=lua         Tshiluba
hl=tum         Tumbuka
hl=tr          Turkish
hl=tk          Turkmen
hl=tw          Twi
hl=ug          Uighur
hl=uk          Ukrainian
hl=ur          Urdu
hl=uz          Uzbek
hl=vi          Vietnamese
hl=cy          Welsh
hl=wo          Wolof
hl=xh          Xhosa
hl=yi          Yiddish
hl=yo          Yoruba
hl=zu          Zulu

搜索语言代码
lr=lang_af    Afrikaans
lr=lang_ar    Arabic
lr=lang_hy    Armenian
lr=lang_be    Belarusian
lr=lang_bg    Bulgarian
lr=lang_ca    Catalan
lr=lang_zh-CN Chinese (Simplified)
lr=lang_zh-TW Chinese (Traditional)
lr=lang_hr    Croatian
lr=lang_cs    Czech
lr=lang_da    Danish
lr=lang_nl    Dutch
lr=lang_en    English
lr=lang_eo    Esperanto
lr=lang_et    Estonian
lr=lang_tl    Filipino
lr=lang_fi    Finnish
lr=lang_fr    French
lr=lang_de    German
lr=lang_el    Greek
lr=lang_iw    Hebrew
lr=lang_hi    Hindi
lr=lang_hu    Hungarian
lr=lang_is    Icelandic
lr=lang_id    Indonesian
lr=lang_it    Italian
lr=lang_ja    Japanese
lr=lang_ko    Korean
lr=lang_lv    Latvian
lr=lang_lt    Lithuanian
lr=lang_no    Norwegian
lr=lang_fa    Persian
lr=lang_pl    Polish
lr=lang_pt    Portuguese
lr=lang_ro    Romanian
lr=lang_ru    Russian
lr=lang_sr    Serbian
lr=lang_sk    Slovak
lr=lang_sl    Slovenian
lr=lang_es    Spanish
lr=lang_sw    Swahili
lr=lang_sv    Swedish
lr=lang_th    Thai
lr=lang_tr    Turkish
lr=lang_uk    Ukrainian
lr=lang_vi    Vietnamese


简短的文字只有语言代码一览表

af, ach, ak, am, ar, az, be, bem, bg, bh, bn, br, bs, ca, chr, ckb, 
co, crs, cs, cy, da, de, ee, el, en, eo, es, es-419, et, eu, fa, fi, 
fo, fr, fy, ga, gaa, gd, gl, gn, gu, ha, haw, hi, hr, ht, hu, hy, ia, 
id, ig, is, it, iw, ja, jw, ka, kg, kk, km, kn, ko, kri, ku, ky, la, 
lg, ln, lo, loz, lt, lua, lv, mfe, mg, mi, mk, ml, mn, mo, mr, ms, mt,
ne, nl, nn, no, nso, ny, nyn, oc, om, or, pa, pcm, pl, ps, pt-BR, 
pt-PT, qu, rm, rn, ro, ru, rw, sd, sh, si, sk, sl, sn, so, sq, sr, 
sr-ME, st, su, sv, sw, ta, te, tg, th, ti, tk, tl, tn, to, tr, tt, 
tum, tw, ug, uk, ur, uz, vi, wo, xh, xx-bork, xx-elmer, xx-hacker, 
xx-klingon, xx-pirate, yi, yo, zh-CN, zh-TW, zu.
链接

相关网页

谷歌翻译
谷歌音译
谷歌在你的语言
谷歌翻译工具
谷歌翻译博客


新闻

现在谷歌搜索支持切诺基(2011年3月)
现在我们听! ( 2010年12月)
屏幕上的键盘谷歌翻译(2010年12月)
在香港(2010年12月,谷歌推出广东话语音搜寻)
音译走向全球(2009年12月)
51语言在Google翻译(2009年8月)
 
版权所有(C) 2007-2014 :shaozhuqing( shaozhuqing.com )。

 
免责声明:本文提供不带任何明示或
暗示的保证。虽然已采取一切努力,以确保
本文中包含的信息的准确度的
贡献者的错误或遗漏,或不承担任何责任
从使用的信息造成的损害所载。


最后编辑时间: 2012年6月11日
整理:邵珠庆( shaozhuqing.com )
网址: http://shaozhuqing.com/?p=2769
246月/13

HTML/CSS/Javascript代码在线压缩、格式化(美化)工具

发布在 邵珠庆

CSS 格式化ProCSSor - http://procssor.com/

CSS 压缩CSS Compressor - http://www.cssdrive.com/index.php/main/csscompressor

HTML 格式化Tabifier - http://tools.arantius.com/tabifier

HTML 压缩Compress HTML - http://www.textfixer.com/html/compress-html-compression.php

Javascript 格式化Online javascript beautifier - http://jsbeautifier.org/

Javascript 压缩Closure Compiler - http://closure-compiler.appspot.com/

代码高亮Quick Highlighter - http://quickhighlighter.com/

273月/13

编写可读代码的艺术

发布在 邵珠庆

代码为什么要易于理解

“Code should be written to minimize the time it would take for someone else to understand it.”

日常工作的事实是:

  • 写代码前的思考和看代码的时间远大于真正写的时间
  • 读代码是很平常的事情,不论是别人的,还是自己的,半年前写的可认为是别人的代码
  • 代码可读性高,很快就可以理解程序的逻辑,进入工作状态
  • 行数少的代码不一定就容易理解
  • 代码的可读性与程序的效率、架构、易于测试一点也不冲突

整本书都围绕“如何让代码的可读性更高”这个目标来写。这也是好代码的重要标准之一。

如何命名

变量名中应包含更多信息

使用含义明确的词,比如用download而不是get,参考以下替换方案:

1
2
3
4
send -> deliver, dispatch, announce, distribute, route
 find -> search, extract, locate, recover
start -> lanuch, create, begin, open
 make -> create,set up, build, generate, compose, add, new

避免通用的词

tmpretval这样词,除了说明是临时变量和返回值之外,没有任何意义。但是给他加一些有意义的词,就会很明确:

1
2
3
tmp_file = tempfile.NamedTemporaryFile()
...
SaveData(tmp_file, ...)

不使用retval而使用变量真正代表的意义:

1
sum_squares += v[i]; // Where's the "square" that we're summing? Bug!

嵌套的for循环中,ij也有同样让人困惑的时候:

1
2
3
4
for (int i = 0; i < clubs.size(); i++)
 for (int j = 0; j < clubs[i].members.size(); j++)
 for (int k = 0; k < users.size(); k++) if (clubs[i].members[k] == users[j])
 cout << "user[" << j << "] is in club[" << i << "]" << endl;

换一种写法就会清晰很多:

1
if (clubs[ci].members[mi] == users[ui]) # OK. First letters match.

所以,当使用一些通用的词,要有充分的理由才可以。

使用具体的名字

CanListenOnPort就比ServerCanStart好,can start比较含糊,而listen on port确切的说明了这个方法将要做什么。

--run_locally就不如--extra_logging来的明确。

增加重要的细节,比如变量的单位_ms,对原始字符串加_raw

如果一个变量很重要,那么在名字上多加一些额外的字就会更加易读,比如将string id; // Example: "af84ef845cd8"换成string hex_id;

1
2
3
4
Start(int delay) --> delay → delay_secs
 CreateCache(int size) --> size → size_mb
ThrottleDownload(float limit) --> limit → max_kbps
 Rotate(float angle) --> angle → degrees_cw

更多例子:

1
2
3
4
password -> plaintext_password
 comment -> unescaped_comment
 html -> html_utf8
 data -> data_urlenc

对于作用域大的变量使用较长的名字

在比较小的作用域内,可以使用较短的变量名,在较大的作用域内使用的变量,最好用长一点的名字,编辑器的自动补全都可以很好的减少键盘输入。对于一些缩写前缀,尽量选择众所周知的(如str),一个判断标准是,当新成员加入时,是否可以无需他人帮助而明白前缀代表什么。

合理使用_-等符号,比如对私有变量加_前缀。

1
2
3
4
5
6
7
8
var x = new DatePicker(); // DatePicker() 是类的"构造"函数,大写开始
var y = pageHeight(); // pageHeight() 是一个普通函数
 
var $all_images = $("img"); // $all_images 是jQuery对象
var height = 250; // height不是
 
//id和class的写法分开
<div id="middle_column" class="main-content"> ...

命名不能有歧义

命名的时候可以先想一下,我要用的这个词是否有别的含义。举个例子:

1
results = Database.all_objects.filter("year <= 2011")

现在的结果到底是包含2011年之前的呢还是不包含呢?

使用minmax代替limit

1
2
3
4
5
6
7
CART_TOO_BIG_LIMIT = 10
 if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT:
 Error("Too many items in cart.")
 
MAX_ITEMS_IN_CART = 10
 if shopping_cart.num_items() > MAX_ITEMS_IN_CART:
 Error("Too many items in cart.")

对比上例中CART_TOO_BIG_LIMITMAX_ITEMS_IN_CART,想想哪个更好呢?

使用firstlast来表示闭区间

1
2
3
4
print integer_range(start=2, stop=4)
# Does this print [2,3] or [2,3,4] (or something else)?
 
set.PrintKeys(first="Bart", last="Maggie")

firstlast含义明确,适宜表示闭区间。

使用beiginend表示前闭后开(2,9))区间

1
2
3
PrintEventsInRange("OCT 16 12:00am", "OCT 17 12:00am")
 
PrintEventsInRange("OCT 16 12:00am", "OCT 16 11:59:59.9999pm")

上面一种写法就比下面的舒服多了。

Boolean型变量命名

1
bool read_password = true;

这是一个很危险的命名,到底是需要读取密码呢,还是密码已经被读取呢,不知道,所以这个变量可以使用user_is_authenticated代替。通常,给Boolean型变量添加ishascanshould可以让含义更清晰,比如:

1
2
SpaceLeft() --> hasSpaceLeft()
bool disable_ssl = false --> bool use_ssl = true

符合预期

1
2
3
4
5
6
7
public class StatisticsCollector {
 public void addSample(double x) { ... }
 public double getMean() {
 // Iterate through all samples and return total / num_samples
 }
 ...
}

在这个例子中,getMean方法遍历了所有的样本,返回总额,所以并不是普通意义上轻量的get方法,所以应该取名computeMean比较合适。

漂亮的格式

写出来漂亮的格式,充满美感,读起来自然也会舒服很多,对比下面两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
class StatsKeeper {
 public:
 // A class for keeping track of a series of doubles
 void Add(double d); // and methods for quick statistics about them
 private: int count; /* how many so far
 */ public:
 double Average();
 private: double minimum;
 list<double>
 past_items
 ;double maximum;
};

什么是充满美感的呢:

1
2
3
4
5
6
7
8
9
10
11
12
// A class for keeping track of a series of doubles
// and methods for quick statistics about them.
class StatsKeeper {
 public:
 void Add(double d);
 double Average();
 private:
 list<double> past_items;
 int count; // how many so far
 double minimum;
 double maximum;
};

考虑断行的连续性和简洁

这段代码需要断行,来满足不超过一行80个字符的要求,参数也需要注释说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PerformanceTester {
 public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(
 500, /* Kbps */
 80, /* millisecs latency */
 200, /* jitter */
 1 /* packet loss % */);
 
 public static final TcpConnectionSimulator t3_fiber = new TcpConnectionSimulator(
 45000, /* Kbps */
 10, /* millisecs latency */
 0, /* jitter */
 0 /* packet loss % */);
 
 public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(
 100, /* Kbps */
 400, /* millisecs latency */
 250, /* jitter */
 5 /* packet loss % */);
}

考虑到代码的连贯性,先优化成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PerformanceTester {
 public static final TcpConnectionSimulator wifi =
 new TcpConnectionSimulator(
 500, /* Kbps */
 80, /* millisecs latency */ 200, /* jitter */
 1 /* packet loss % */);
 
 public static final TcpConnectionSimulator t3_fiber =
 new TcpConnectionSimulator(
 45000, /* Kbps */
 10, /* millisecs latency */
 0, /* jitter */
 0 /* packet loss % */);
 
 public static final TcpConnectionSimulator cell =
 new TcpConnectionSimulator(
 100, /* Kbps */
 400, /* millisecs latency */
 250, /* jitter */
 5 /* packet loss % */);
}

连贯性好一点,但还是太罗嗦,额外占用很多空间:

1
2
3
4
5
6
7
8
9
10
11
12
public class PerformanceTester {
 // TcpConnectionSimulator(throughput, latency, jitter, packet_loss)
 // [Kbps] [ms] [ms] [percent]
 public static final TcpConnectionSimulator wifi =
 new TcpConnectionSimulator(500, 80, 200, 1);
 
 public static final TcpConnectionSimulator t3_fiber =
 new TcpConnectionSimulator(45000, 10, 0, 0);
 
 public static final TcpConnectionSimulator cell =
 new TcpConnectionSimulator(100, 400, 250, 5);
}

用函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Turn a partial_name like "Doug Adams" into "Mr. Douglas Adams".
// If not possible, 'error' is filled with an explanation.
string ExpandFullName(DatabaseConnection dc, string partial_name, string* error);
 
DatabaseConnection database_connection;
string error;
assert(ExpandFullName(database_connection, "Doug Adams", &error)
 == "Mr. Douglas Adams");
assert(error == "");
assert(ExpandFullName(database_connection, " Jake Brown ", &error)
 == "Mr. Jacob Brown III");
assert(error == "");
assert(ExpandFullName(database_connection, "No Such Guy", &error) == "");
assert(error == "no match found");
assert(ExpandFullName(database_connection, "John", &error) == "");
assert(error == "more than one result");

上面这段代码看起来很脏乱,很多重复性的东西,可以用函数封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CheckFullName("Doug Adams", "Mr. Douglas Adams", "");
CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");
CheckFullName("No Such Guy", "", "no match found");
CheckFullName("John", "", "more than one result");
 
void CheckFullName(string partial_name,
 string expected_full_name,
 string expected_error) {
 // database_connection is now a class member
 string error;
 string full_name = ExpandFullName(database_connection, partial_name, &error);
 assert(error == expected_error);
 assert(full_name == expected_full_name);
}

列对齐

列对齐可以让代码段看起来更舒适:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CheckFullName("Doug Adams" , "Mr. Douglas Adams" , "");
CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");
CheckFullName("No Such Guy" , "" , "no match found");
CheckFullName("John" , "" , "more than one result");
 
commands[] = {
 ...
 { "timeout" , NULL , cmd_spec_timeout},
 { "timestamping" , &opt.timestamping , cmd_boolean},
 { "tries" , &opt.ntry , cmd_number_inf},
 { "useproxy" , &opt.use_proxy , cmd_boolean},
 { "useragent" , NULL , cmd_spec_useragent},
 ...
};

代码用块区分

1
2
3
4
5
6
7
8
9
10
11
12
13
class FrontendServer {
 public:
 FrontendServer();
 void ViewProfile(HttpRequest* request);
 void OpenDatabase(string location, string user);
 void SaveProfile(HttpRequest* request);
 string ExtractQueryParam(HttpRequest* request, string param);
 void ReplyOK(HttpRequest* request, string html);
 void FindFriends(HttpRequest* request);
 void ReplyNotFound(HttpRequest* request, string error);
 void CloseDatabase(string location);
 ~FrontendServer();
};

上面这一段虽然能看,不过还有优化空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class FrontendServer {
 public:
 FrontendServer();
 ~FrontendServer();
 // Handlers
 void ViewProfile(HttpRequest* request);
 void SaveProfile(HttpRequest* request);
 void FindFriends(HttpRequest* request);
 
 // Request/Reply Utilities
 string ExtractQueryParam(HttpRequest* request, string param);
 void ReplyOK(HttpRequest* request, string html);
 void ReplyNotFound(HttpRequest* request, string error);
 
 // Database Helpers
 void OpenDatabase(string location, string user);
 void CloseDatabase(string location);
};

再来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Import the user's email contacts, and match them to users in our system.
# Then display a list of those users that he/she isn't already friends with.
def suggest_new_friends(user, email_password):
 friends = user.friends()
 friend_emails = set(f.email for f in friends)
 contacts = import_contacts(user.email, email_password)
 contact_emails = set(c.email for c in contacts)
 non_friend_emails = contact_emails - friend_emails
 suggested_friends = User.objects.select(email__in=non_friend_emails)
 display['user'] = user
 display['friends'] = friends
 display['suggested_friends'] = suggested_friends
 return render("suggested_friends.html", display)

全都混在一起,视觉压力相当大,按功能化块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def suggest_new_friends(user, email_password):
 # Get the user's friends' email addresses.
 friends = user.friends()
 friend_emails = set(f.email for f in friends)
 
 # Import all email addresses from this user's email account.
 contacts = import_contacts(user.email, email_password)
 contact_emails = set(c.email for c in contacts)
 
 # Find matching users that they aren't already friends with.
 non_friend_emails = contact_emails - friend_emails
 suggested_friends = User.objects.select(email__in=non_friend_emails)
 
 # Display these lists on the page. display['user'] = user
 display['friends'] = friends
 display['suggested_friends'] = suggested_friends
 
 return render("suggested_friends.html", display)

让代码看起来更舒服,需要在写的过程中多注意,培养一些好的习惯,尤其当团队合作的时候,代码风格比如大括号的位置并没有对错,但是不遵循团队规范那就是错的。

如何写注释

当你写代码的时候,你会思考很多,但是最终呈现给读者的就只剩代码本身了,额外的信息丢失了,所以注释的目的就是让读者了解更多的信息。

应该注释什么

不应该注释什么

这样的注释毫无价值:

1
2
3
4
5
6
7
8
9
10
// The class definition for Account
class Account {
 public:
 // Constructor
 Account();
 // Set the profit member to a new value
 void SetProfit(double profit);
 // Return the profit from this Account
 double GetProfit();
};

不要像下面这样为了注释而注释:

1
2
3
4
// Find a Node with the given 'name' or return NULL.
// If depth <= 0, only 'subtree' is inspected.
// If depth == N, only 'subtree' and N levels below are inspected.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

不要给烂取名注释

1
2
3
// Enforce limits on the Reply as stated in the Request,
// such as the number of items returned, or total byte size, etc.
void CleanReply(Request request, Reply reply);

注释的大部分都在解释clean是什么意思,那不如换个正确的名字:

1
2
// Make sure 'reply' meets the count/byte/etc. limits from the 'request'
void EnforceLimitsFromRequest(Request request, Reply reply);

记录你的想法

我们讨论了不该注释什么,那么应该注释什么呢?注释应该记录你思考代码怎么写的结果,比如像下面这些:

1
2
3
4
5
6
7
// Surprisingly, a binary tree was 40% faster than a hash table for this data.
// The cost of computing a hash was more than the left/right comparisons.
 
// This heuristic might miss a few words. That's OK; solving this 100% is hard.
 
// This class is getting messy. Maybe we should create a 'ResourceNode' subclass to
// help organize things.

也可以用来记录流程和常量:

1
2
3
4
5
6
7
// TODO: use a faster algorithm
// TODO(dustin): handle other image formats besides JPEG
 
NUM_THREADS = 8 # as long as it's >= 2 * num_processors, that's good enough.
 
// Impose a reasonable limit - no human can read that much anyway.
const int MAX_RSS_SUBSCRIPTIONS = 1000;

可用的词有:

  • TODO : Stuff I haven’t gotten around to yet
  • FIXME : Known-broken code here
  • HACK : Adimittedly inelegant solution to a problem
  • XXX : Danger! Major problem here

站在读者的角度去思考

当别人读你的代码时,让他们产生疑问的部分,就是你应该注释的地方。

1
2
3
4
5
6
7
struct Recorder {
 vector<float> data;
 ...
 void Clear() {
 vector<float>().swap(data); // Huh? Why not just data.clear()?
 }
};

很多C++的程序员啊看到这里,可能会想为什么不用data.clear()来代替vector.swap,所以那个地方应该加上注释:

1
2
// Force vector to relinquish its memory (look up "STL swap trick")
vector<float>().swap(data);

说明可能陷阱

你在写代码的过程中,可能用到一些hack,或者有其他需要读代码的人知道的陷阱,这时候就应该注释:

1
void SendEmail(string to, string subject, string body);

而实际上这个发送邮件的函数是调用别的服务,有超时设置,所以需要注释:

1
2
// Calls an external service to deliver email. (Times out after 1 minute.)
void SendEmail(string to, string subject, string body);

全景的注释

有时候为了更清楚说明,需要给整个文件加注释,让读者有个总体的概念:

1
2
// This file contains helper functions that provide a more convenient interface to our
// file system. It handles file permissions and other nitty-gritty details.

总结性的注释

即使是在函数内部,也可以有类似文件注释那样的说明注释:

1
2
3
4
5
# Find all the items that customers purchased for themselves.
for customer_id in all_customers:
 for sale in all_sales[customer_id].sales:
 if sale.recipient == customer_id:
 ...

或者按照函数的步进,写一些注释:

1
2
3
4
5
6
7
8
def GenerateUserReport():
 # Acquire a lock for this user
 ...
 # Read user's info from the database
 ...
 # Write info to a file
 ...
 # Release the lock for this user

很多人不愿意写注释,确实,要写好注释也不是一件简单的事情,也可以在文件专门的地方,留个写注释的区域,可以写下你任何想说的东西。

注释应简明准确

前一个小节讨论了注释应该写什么,这一节来讨论应该怎么写,因为注释很重要,所以要写的精确,注释也占据屏幕空间,所以要简洁。

精简注释

1
2
3
4
// The int is the CategoryType.
// The first float in the inner pair is the 'score',
// the second is the 'weight'.
typedef hash_map<int, pair<float, float> > ScoreMap;

这样写太罗嗦了,尽量精简压缩成这样:

1
2
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;

避免有歧义的代词

1
// Insert the data into the cache, but check if it's too big first.

这里的it's有歧义,不知道所指的是data还是cache,改成如下:

1
// Insert the data into the cache, but check if the data is too big first.

还有更好的解决办法,这里的it就有明确所指:

1
// If the data is small enough, insert it into the cache.

语句要精简准确

1
# Depending on whether we've already crawled this URL before, give it a different priority.

这句话理解起来太费劲,改成如下就好理解很多:

1
# Give higher priority to URLs we've never crawled before.

精确描述函数的目的

1
2
// Return the number of lines in this file.
int CountLines(string filename) { ... }

这样的一个函数,用起来可能会一头雾水,因为他可以有很多歧义:

  • ”” 一个空文件,是0行还是1行?
  • “hello” 只有一行,那么返回值是0还是1?
  • “hellon” 这种情况返回1还是2?
  • “hellon world” 返回1还是2?
  • “hellonr crueln worldr” 返回2、3、4哪一个呢?

所以注释应该这样写:

1
2
// Count how many newline bytes ('n') are in the file.
int CountLines(string filename) { ... }

用实例说明边界情况

1
2
3
// Rearrange 'v' so that elements < <span class="wp_keywordlink"><a href="http://blog.jobbole.com/24057/" title="Pivot — 创业者最重要的本领" rel="nofollow" target="_blank">Pivot</a></span> come before those >= <span class="wp_keywordlink"><a href="http://blog.jobbole.com/24057/" title="Pivot — 创业者最重要的本领" rel="nofollow" target="_blank">Pivot</a></span>;
// Then return the largest 'i' for which v[i] < pivot (or -1 if none are < pivot)
int Partition(vector<int>* v, int pivot);

这个描述很精确,但是如果再加入一个例子,就更好了:

1
2
3
// ...
// Example: Partition([8 5 9 8 2], 8) might result in [5 2 | 8 9 8] and return 1
int Partition(vector<int>* v, int pivot);

说明你的代码的真正目的

1
2
3
4
5
6
7
8
void DisplayProducts(list<Product> products) {
 products.sort(CompareProductByPrice);
 // Iterate through the list in reverse order
 for (list<Product>::reverse_iterator it = products.rbegin(); it != products.rend();
 ++it)
 DisplayPrice(it->price);
 ...
}

这里的注释说明了倒序排列,单还不够准确,应该改成这样:

1
2
// Display each price, from highest to lowest
for (list<Product>::reverse_iterator it = products.rbegin(); ... )

函数调用时的注释

看见这样的一个函数调用,肯定会一头雾水:

1
Connect(10, false);

如果加上这样的注释,读起来就清楚多了:

1
2
3
4
def Connect(timeout, use_encryption): ...
 
# Call the function using named parameters
Connect(timeout = 10, use_encryption = False)

使用信息含量丰富的词

1
2
3
4
// This class contains a number of members that store the same information as in the
// database, but are stored here for speed. When this class is read from later, those
// members are checked first to see if they exist, and if so are returned; otherwise the
// database is read from and that data stored in those fields for next time.

上面这一大段注释,解释的很清楚,如果换一个词来代替,也不会有什么疑惑:

1
// This class acts as a caching layer to the database.

简化循环和逻辑

流程控制要简单

让条件语句、循环以及其他控制流程的代码尽可能自然,让读者在阅读过程中不需要停顿思考或者在回头查找,是这一节的目的。

条件语句中参数的位置

对比下面两种条件的写法:

1
2
3
4
5
if (length >= 10)
while (bytes_received < bytes_expected)
 
if (10 <= length)
while (bytes_expected > bytes_received)

到底是应该按照大于小于的顺序来呢,还是有其他的准则?是的,应该按照参数的意义来

  • 运算符左边:通常是需要被检查的变量,也就是会经常变化的
  • 运算符右边:通常是被比对的样本,一定程度上的常量

这就解释了为什么bytes_received < bytes_expected比反过来更好理解。

if/else的顺序

通常,if/else的顺序你可以自由选择,下面这两种都可以:

1
2
3
4
5
6
7
8
9
10
11
if (a == b) {
 // Case One ...
} else {
 // Case Two ...
}
 
if (a != b) {
 // Case Two ...
} else {
 // Case One ...
}

或许对此你也没有仔细斟酌过,但在有些时候,一种顺序确实好过另一种:

  • 正向的逻辑在前,比如if(debug)就比if(!debug)
  • 简单逻辑的在前,这样ifelse就可以在一个屏幕显示 – 有趣、清晰的逻辑在前

举个例子来看:

1
2
3
4
5
6
7
8
9
if (!url.HasQueryParameter("expand_all")) {
 response.Render(items);
 ...
} else {
 for (int i = 0; i < items.size(); i++) {
 items[i].Expand();
 }
 ...
}

看到if你首先想到的是expand_all,就好像告诉你“不要想大象”,你会忍不住去想它,所以产生了一点点迷惑,最好写成:

1
2
3
4
5
6
7
8
9
if (url.HasQueryParameter("expand_all")) {
 for (int i = 0; i < items.size(); i++) {
 items[i].Expand();
 }
 ...
} else {
 response.Render(items);
 ...
}

三目运算符(?:)

1
2
3
4
5
6
7
8
time_str += (hour >= 12) ? "pm" : "am";
 
Avoiding the ternary operator, you might write:
 if (hour >= 12) {
 time_str += "pm";
 } else {
 time_str += "am";
}

使用三目运算符可以减少代码行数,上例就是一个很好的例证,但是我们的真正目的是减少读代码的时间,所以下面的情况并不适合用三目运算符:

1
2
3
4
5
6
7
return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
 
if (exponent >= 0) {
 return mantissa * (1 << exponent);
} else {
 return mantissa / (1 << -exponent);
}

所以只在简单表达式的地方用。

避免使用do/while表达式

1
2
3
do {
 continue;
} while (false);

这段代码会执行几遍呢,需要时间思考一下,do/while完全可以用别的方法代替,所以应避免使用。

尽早return

1
2
3
4
5
public boolean Contains(String str, String substr) {
 if (str == null || substr == null) return false;
 if (substr.equals("")) return true;
 ...
}

函数里面尽早的return,可以让逻辑更加清晰。

减少嵌套

1
2
3
4
5
6
7
8
9
10
11
if (user_result == SUCCESS) {
 if (permission_result != SUCCESS) {
 reply.WriteErrors("error reading permissions");
 reply.Done();
 return;
 }
 reply.WriteErrors("");
} else {
 reply.WriteErrors(user_result);
}
reply.Done();

这样一段代码,有一层的嵌套,但是看起来也会稍有迷惑,想想自己的代码,有没有类似的情况呢?可以换个思路去考虑这段代码,并且用尽早return的原则修改,看起来就舒服很多:

1
2
3
4
5
6
7
8
9
10
11
12
if (user_result != SUCCESS) {
 reply.WriteErrors(user_result);
 reply.Done();
 return;
}
if (permission_result != SUCCESS) {
 reply.WriteErrors(permission_result);
 reply.Done();
 return;
}
reply.WriteErrors("");
reply.Done();

同样的,对于有嵌套的循环,可以采用同样的办法:

1
2
3
4
5
6
7
8
9
for (int i = 0; i < results.size(); i++) {
 if (results[i] != NULL) {
 non_null_count++;
 if (results[i]->name != "") {
 cout << "Considering candidate..." << endl;
 ...
 }
 }
}

换一种写法,尽早return,在循环中就用continue:

1
2
3
4
5
6
7
8
for (int i = 0; i < results.size(); i++) {
 if (results[i] == NULL) continue;
 non_null_count++;
 
 if (results[i]->name == "") continue;
 cout << "Considering candidate..." << endl;
 ...
}

拆分复杂表达式

很显然的,越复杂的表达式,读起来越费劲,所以应该把那些复杂而庞大的表达式,拆分成一个个易于理解的小式子。

用变量

将复杂表达式拆分最简单的办法,就是增加一个变量:

1
2
3
4
5
6
if line.split(':')[0].strip() == "root":
 
//用变量替换
username = line.split(':')[0].strip()
if username == "root":
 ...

或者这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (request.user.id == document.owner_id) {
 // user can edit this document...
}
...
if (request.user.id != document.owner_id) {
// document is read-only...
}
 
//用变量替换
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
 // user can edit this document...
}
...
if (!user_owns_document) {
 // document is read-only...
}

逻辑替换

  • 1) not (a or b or c) <–> (not a) and (not b) and (not c)
  • 2) not (a and b and c) <–> (not a) or (not b) or (not c)

所以,就可以这样写:

1
2
3
4
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
 
//替换
if (!file_exists || is_protected) Error("Sorry, could not read file.");

不要滥用逻辑表达式

1
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

这样的代码完全可以用下面这个替换,虽然有两行,但是更易懂:

1
2
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());

像下面这样的表达式,最好也不要写,因为在有些语言中,x会被赋予第一个为true的变量的值:

1
x = a || b || c

拆解大表达式

1
2
3
4
5
6
7
8
9
10
11
12
var update_highlight = function (message_num) {
 if ($("#vote_value" + message_num).html() === "Up") {
 $("#thumbs_up" + message_num).addClass("highlighted");
 $("#thumbs_down" + message_num).removeClass("highlighted");
 } else if ($("#vote_value" + message_num).html() === "Down") {
 $("#thumbs_up" + message_num).removeClass("highlighted");
 $("#thumbs_down" + message_num).addClass("highlighted");
 } else {
 $("#thumbs_up" + message_num).removeClass("highighted");
 $("#thumbs_down" + message_num).removeClass("highlighted");
 }
};

这里面有很多重复的语句,我们可以用变量还替换简化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var update_highlight = function (message_num) {
 var thumbs_up = $("#thumbs_up" + message_num);
 var thumbs_down = $("#thumbs_down" + message_num);
 var vote_value = $("#vote_value" + message_num).html();
 var hi = "highlighted";
 
 if (vote_value === "Up") {
 thumbs_up.addClass(hi);
 thumbs_down.removeClass(hi);
 } else if (vote_value === "Down") {
 thumbs_up.removeClass(hi);
 thumbs_down.addClass(hi);
 } else {
 thumbs_up.removeClass(hi);
 thumbs_down.removeClass(hi);
 }
}

消除变量

前一节,讲到利用变量来拆解大表达式,这一节来讨论如何消除多余的变量。

没用的临时变量

1
2
now = datetime.datetime.now()
root_message.last_view_time = now

这里的now可以去掉,因为:

  • 并非用来拆分复杂的表达式
  • 也没有增加可读性,因为`datetime.datetime.now()`本就清晰
  • 只用了一次

所以完全可以写作:

1
root_message.last_view_time = datetime.datetime.now()

消除条件控制变量

1
2
3
4
5
6
7
8
boolean done = false;
while (/* condition */ && !done) {
 ...
 if (...) {
 done = true;
 continue;
 }
}

这里的done可以用别的方式更好的完成:

1
2
3
4
5
6
while (/* condition */) {
 ...
 if (...) {
 break;
 }
}

这个例子非常容易修改,如果是比较复杂的嵌套,break可能并不够用,这时候就可以把代码封装到函数中。

减少变量的作用域

我们都听过要避免使用全局变量这样的忠告,是的,当变量的作用域越大,就越难追踪,所以要保持变量小的作用域。

1
2
3
4
5
6
7
8
9
10
11
12
class LargeClass {
 string str_;
 void Method1() {
 str_ = ...;
 Method2();
 }
 void Method2() {
 // Uses str_
 }
 // Lots of other methods that don't use str_
 ... ;
}

这里的str_的作用域有些大,完全可以换一种方式:

1
2
3
4
5
6
7
8
9
10
class LargeClass {
 void Method1() {
 string str = ...;
 Method2(str);
 }
 void Method2(string str) {
 // Uses str
 }
 // Now other methods can't see str.
};

str通过变量函数参数传递,减小了作用域,也更易读。同样的道理也可以用在定义类的时候,将大类拆分成一个个小类。

不要使用嵌套的作用域

1
2
3
4
5
6
7
8
9
# No use of example_value up to this point.
if request:
 for value in request.values:
 if value > 0:
 example_value = value
 break
 
for logger in debug.loggers:
 logger.log("Example:", example_value)

这个例子在运行时候会报example_value is undefined的错,修改起来不算难:

1
2
3
4
5
6
7
8
9
example_value = None
if request:
 for value in request.values:
 if value > 0: example_value = value
 break
 
if example_value:
 for logger in debug.loggers:
 logger.log("Example:", example_value)

但是参考前面的消除中间变量准则,还有更好的办法:

1
2
3
4
5
6
7
8
9
def LogExample(value):
 for logger in debug.loggers:
 logger.log("Example:", value)
 
 if request:
 for value in request.values:
 if value > 0:
 LogExample(value) # deal with 'value' immediately
 break

用到了再声明

在C语言中,要求将所有的变量事先声明,这样当用到变量较多时候,读者处理这些信息就会有难度,所以一开始没用到的变量,就暂缓声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
def ViewFilteredReplies(original_id):
 filtered_replies = []
 root_message = Messages.objects.get(original_id)
 all_replies = Messages.objects.select(root_id=original_id)
 root_message.view_count += 1
 root_message.last_view_time = datetime.datetime.now()
 root_message.save()
 
 for reply in all_replies:
 if reply.spam_votes <= MAX_SPAM_VOTES:
 filtered_replies.append(reply)
 
 return filtered_replies

读者一次处理变量太多,可以暂缓声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
def ViewFilteredReplies(original_id):
 root_message = Messages.objects.get(original_id)
 root_message.view_count += 1
 root_message.last_view_time = datetime.datetime.now()
 root_message.save()
 
 all_replies = Messages.objects.select(root_id=original_id)
 filtered_replies = []
 for reply in all_replies:
 if reply.spam_votes <= MAX_SPAM_VOTES:
 filtered_replies.append(reply)
 
 return filtered_replies

变量最好只写一次

前面讨论了过多的变量会让读者迷惑,同一个变量,不停的被赋值也会让读者头晕,如果变量变化的次数少一些,代码可读性就更强。

一个例子

假设有一个页面,如下,需要给第一个空的input赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<input type="text" id="input1" value="Dustin">
<input type="text" id="input2" value="Trevor">
<input type="text" id="input3" value="">
<input type="text" id="input4" value="Melissa">
...
var setFirstEmptyInput = function (new_value) {
 var found = false;
 var i = 1;
 var elem = document.getElementById('input' + i);
 while (elem !== null) {
 if (elem.value === '') {
 found = true;
 break;
 }
 i++;
 elem = document.getElementById('input' + i);
 }
 if (found) elem.value = new_value;
 return elem;
};

这段代码能工作,有三个变量,我们逐一去看如何优化,found作为中间变量,完全可以消除:

1
2
3
4
5
6
7
8
9
10
11
12
13
var setFirstEmptyInput = function (new_value) {
 var i = 1;
 var elem = document.getElementById('input' + i);
 while (elem !== null) {
 if (elem.value === '') {
 elem.value = new_value;
 return elem;
 }
 i++;
 elem = document.getElementById('input' + i);
 }
 return null;
};

再来看elem变量,只用来做循环,调用了很多次,所以很难跟踪他的值,i也可以用for来修改:

1
2
3
4
5
6
7
8
9
10
11
var setFirstEmptyInput = function (new_value) {
 for (var i = 1; true; i++) {
 var elem = document.getElementById('input' + i);
 if (elem === null)
 return null; // Search Failed. No empty input found.
 if (elem.value === '') {
 elem.value = new_value;
 return elem;
 }
 }
};

工程师就是将大问题分解为一个个小问题,然后逐个解决,这样也易于保证程序的健壮性、可读性。如何分解子问题,下面给出一些准则:

  • 看看这个方法或代码,问问你自己“这段代码的最终目标是什么?”
  • 对于每一行代码,要问“它与目标直接相关,或者是不相关的子问题?”
  • 如果有足够多行的代码是处理与目标不直接相关的问题,那么抽离成子函数

来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
ajax_post({
 url: 'http://example.com/submit',
 data: data,
 on_success: function (response_data) {
 var str = "{n";
 for (var key in response_data) {
 str += " " + key + " = " + response_data[key] + "n";
 }
 alert(str + "}");
 // Continue handling 'response_data' ...
 }
});

这段代码的目标是发送一个ajax请求,所以其中字符串处理的部分就可以抽离出来:

1
2
3
4
5
6
7
var format_pretty = function (obj) {
 var str = "{n";
 for (var key in obj) {
 str += " " + key + " = " + obj[key] + "n";
 }
 return str + "}";
};

意外收获

有很多理由将format_pretty抽离出来,这些独立的函数可以很容易的添加feature,增强可靠性,处理边界情况,等等。所以这里,可以将format_pretty增强,就会得到一个更强大的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var format_pretty = function (obj, indent) {
 // Handle null, undefined, strings, and non-objects.
 if (obj === null) return "null";
 if (obj === undefined) return "undefined";
 if (typeof obj === "string") return '"' + obj + '"';
 if (typeof obj !== "object") return String(obj);
 if (indent === undefined) indent = "";
 
 // Handle (non-null) objects.
 
 var str = "{n";
 for (var key in obj) {
 str += indent + " " + key + " = ";
 str += format_pretty(obj[key], indent + " ") + "n"; }
 return str + indent + "}";
};

这个函数输出:

1
2
3
4
5
6
7
8
9
10
11
{
 key1 = 1
 key2 = true
 key3 = undefined
 key4 = null
 key5 = {
 key5a = {
 key5a1 = "hello world"
 }
 }
}

多做这样的事情,就是积累代码的过程,这样的代码可以复用,也可以形成自己的代码库,或者分享给别人。

业务相关的函数

那些与目标不相关函数,抽离出来可以复用,与业务相关的也可以抽出来,保持代码的易读性,例如:

1
2
3
4
5
6
7
8
9
10
11
business = Business()
business.name = request.POST["name"]
 
url_path_name = business.name.lower()
url_path_name = re.sub(r"['.]", "", url_path_name)
url_path_name = re.sub(r"[^a-z0-9]+", "-", url_path_name)
url_path_name = url_path_name.strip("-")
business.url = "/biz/" + url_path_name
 
business.date_created = datetime.datetime.utcnow()
business.save_to_database()

抽离出来,就好看很多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CHARS_TO_REMOVE = re.compile(r"['.']+")
CHARS_TO_DASH = re.compile(r"[^a-z0-9]+")
 
def make_url_friendly(text):
 text = text.lower()
 text = CHARS_TO_REMOVE.sub('', text)
 text = CHARS_TO_DASH.sub('-', text)
 return text.strip("-")
 
business = Business()
business.name = request.POST["name"]
business.url = "/biz/" + make_url_friendly(business.name)
business.date_created = datetime.datetime.utcnow()
business.save_to_database()

简化现有接口

我们来看一个读写cookie的函数:

1
2
3
4
5
6
7
8
var max_results;
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
 var c = cookies[i];
 c = c.replace(/^[ ]+/, ''); // remove leading spaces
 if (c.indexOf("max_results=") === 0)
 max_results = Number(c.substring(12, c.length));
}

这段代码实在太丑了,理想的接口应该是这样的:

1
2
set_cookie(name, value, days_to_expire);
delete_cookie(name);

对于并不理想的接口,你永远可以用自己的函数做封装,让接口更好用。

按自己需要写接口

1
2
3
4
5
6
7
ser_info = { "username": "...", "password": "..." }
user_str = json.dumps(user_info)
cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE)
encrypted_bytes = cipher.update(user_str)
encrypted_bytes += cipher.final() # flush out the current 128 bit block
url = "http://example.com/?user_info=" + base64.urlsafe_b64encode(encrypted_bytes)
...

虽然终极目的是拼接用户信息的字符,但是代码大部分做的事情是解析python的object,所以:

1
2
3
4
5
def url_safe_encrypt(obj):
 obj_str = json.dumps(obj)
 cipher = Cipher("aes_128_cbc", key=PRIVATE_KEY, init_vector=INIT_VECTOR, op=ENCODE) encrypted_bytes = cipher.update(obj_str)
 encrypted_bytes += cipher.final() # flush out the current 128 bit block
 return base64.urlsafe_b64encode(encrypted_bytes)

这样在其他地方也可以调用:

1
2
user_info = { "username": "...", "password": "..." }
url = "http://example.com/?user_info=" + url_safe_encrypt(user_info)

分离子函数是好习惯,但是也要适度,过度的分离成多个小函数,也会让查找变得困难。

代码应该是一次只完成一个任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var place = location_info["LocalityName"]; // e.g. "Santa Monica"
if (!place) {
 place = location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
}
if (!place) {
 place = location_info["AdministrativeAreaName"]; // e.g. "California"
}
if (!place) {
 place = "Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
 place += ", " + location_info["CountryName"]; // e.g. "USA"
} else {
 place += ", Planet Earth";
}
 
return place;

这是一个用来拼地名的函数,有很多的条件判断,读起来非常吃力,有没有办法拆解任务呢?

1
2
3
4
var town = location_info["LocalityName"]; // e.g. "Santa Monica"
var city = location_info["SubAdministrativeAreaName"]; // e.g. "Los Angeles"
var state = location_info["AdministrativeAreaName"]; // e.g. "CA"
var country = location_info["CountryName"]; // e.g. "USA"

先拆解第一个任务,将各变量分别保存,这样在后面使用中不需要去记忆那些繁长的key值了,第二个任务,解决地址拼接的后半部分:

1
2
3
4
5
6
7
// Start with the default, and keep overwriting with the most specific value. var second_half = "Planet Earth";
if (country) {
 second_half = country;
}
if (state && country === "USA") {
 second_half = state;
}

再来解决前半部分:

1
2
3
4
5
6
7
8
9
10
var first_half = "Middle-of-Nowhere";
if (state && country !== "USA") {
 first_half = state;
}
if (city) {
 first_half = city;
}
if (town) {
 first_half = town;
}

大功告成:

1
return first_half + ", " + second_half;

如果注意到有USA这个变量的判断的话,也可以这样写:

1
2
3
4
5
6
7
8
9
var first_half, second_half;
if (country === "USA") {
 first_half = town || city || "Middle-of-Nowhere";
 second_half = state || "USA";
} else {
 first_half = town || city || state || "Middle-of-Nowhere";
 second_half = country || "Planet Earth";
}
return first_half + ", " + second_half;

要把一个复杂的东西解释给别人,一些细节很容易就让人产生迷惑,所以想象把你的代码用平实的语言解释给别人听,别人是否能懂,有一些准则可以帮助你让代码更清晰:

  • 用最平实的语言描述代码的目的,就像给读者讲述一样
  • 注意描述中关键的字词
  • 让你的代码符合你的描述

下面这段代码用来校验用户的权限:

1
2
3
4
5
6
7
8
9
10
11
$is_admin = is_admin_request();
if ($document) {
 if (!$is_admin && ($document['username'] != $_SESSION['username'])) {
 return not_authorized();
 }
} else {
 if (!$is_admin) {
 return not_authorized();
 }
}
// continue rendering the page ...

这一段代码不长,里面的逻辑嵌套倒是复杂,参考前面章节所述,嵌套太多非常影响阅读理解,将这个逻辑用语言描述就是:

1
2
3
4
有两种情况有权限:
1、你是管理员(admin)
2、你拥有这个文档
否则就没有权限

根据描述来写代码:

1
2
3
4
5
6
7
8
if (is_admin_request()) {
 // authorized
} elseif ($document && ($document['username'] == $_SESSION['username'])) {
 // authorized
} else {
 return not_authorized();
}
// continue rendering the page ...

最易懂的代码就是没有代码!

  • 去掉那些没意义的feature,也不要过度设计
  • 重新考虑需求,解决最简单的问题,也能完成整体的目标
  • 熟悉你常用的库,周期性研究他的API

还有一些与测试相关的章节,留给你自己去研读吧,再次推荐此书:

271月/13

《Head First 设计模式》代码之PHP版

发布在 邵珠庆

《Head First 设计模式》是本不错的讲解设计模式的书,不像F4写的那么枯燥,应该算是比较容易理解的好书。书中的例子都比较浅显易懂,不过由于是外国佬写的,所以例子的习惯不是很附合中国特色,可能偶尔看起来有些别扭,还有语言习惯也不是中国风。当然看过这本书之后,你才能深刻理解设计模式到底能为你解决哪些题,不能为你解决哪些问题(比如不能代替你的编码)。

策略模式

 

[php] view plaincopy

 
  1. <?php  
  2. /** 
  3.  * 策略模式 
  4.  * 定义了算法族,分别封装起来,让它们之间可以互相替换, 
  5.  * 此模式让算法的变化独立于使用算法的客户。 
  6.  */  
  7. //飞行行为接口  
  8. interface FlyBehavior {  
  9.     public function fly();  
  10. }  
  11. //呱呱叫行为接口  
  12. interface QuackBehavior {  
  13.     public function quack();  
  14. }  
  15. //翅膀飞行  
  16. class FlyWithWings implements FlyBehavior {  
  17.     public function fly() {  
  18.         echo "I'm flying!!/n";  
  19.     }  
  20. }  
  21. //不会飞  
  22. class FlyNoWay implements FlyBehavior {  
  23.     public function fly() {  
  24.         echo "I can't fly!/n";  
  25.     }  
  26. }  
  27. class FlyRocketPowered implements FlyBehavior {  
  28.     public function fly() {  
  29.         echo "I'm flying with a rocket!/n";  
  30.     }  
  31. }  
  32. class Qquack implements QuackBehavior {  
  33.     public function quack() {  
  34.         echo "Quack/n";  
  35.     }  
  36. }  
  37. class Squeak implements QuackBehavior {  
  38.     public function quack() {  
  39.         echo "Squeak/n";  
  40.     }  
  41. }  
  42. class MuteQuack implements QuackBehavior {  
  43.     public function quack() {  
  44.         echo "<< Silence >>/n";  
  45.     }  
  46. }  
  47. abstract class Duck {  
  48.     protected $quack_obj;  
  49.     protected $fly_obj;  
  50.     public abstract function display();  
  51.     public function performQuack() {  
  52.         $this->quack_obj->quack();  
  53.     }  
  54.     public function performFly() {  
  55.         $this->fly_obj->fly();  
  56.     }  
  57.     public function swim() {  
  58.         echo "All ducks float, even decoys!/n";  
  59.     }  
  60.     public function setFlyBehavior(FlyBehavior $fb) {  
  61.         $this->fly_obj = $fb;  
  62.     }  
  63.     public function setQuackBehavior(QuackBehavior $qb) {  
  64.         $this->quack_obj = $qb;  
  65.     }  
  66. }  
  67. class ModelDuck extends Duck {  
  68.     public function __construct() {  
  69.         $this->fly_obj = new FlyNoWay();  
  70.         $this->quack_obj = new MuteQuack();  
  71.     }  
  72.     public function display() {  
  73.         echo "I'm a model duck!/n";  
  74.     }  
  75. }  
  76. $model = new ModelDuck();  
  77. $model->performFly();  
  78. $model->performQuack();  
  79. //提供新的能力  
  80. $model->setFlyBehavior(new FlyRocketPowered());  
  81. $model->setQuackBehavior(new Squeak());  
  82. $model->performFly();  
  83. $model->performQuack();  
  84. ?>  

 

单件模式

 

[php] view plaincopy

 
  1. <?php  
  2. /** 
  3.  * 单件模式 
  4.  * 确保一个类只有一个实例,并提供一个全局访问点。 
  5.  */  
  6. class MyClass {  
  7.     private static $uniqueInstance;  
  8.     private function __construct() {  
  9.     }  
  10.     public static function getInstance() {  
  11.         if (self::$uniqueInstance == null) {  
  12.             self::$uniqueInstance = new MyClass();  
  13.         }  
  14.         return self::$uniqueInstance;  
  15.     }  
  16. }  
  17. $myClass = MyClass::getInstance();  
  18. var_dump($myClass);  
  19. $myClass = MyClass::getInstance();  
  20. var_dump($myClass);  
  21. ?>  

 

工厂方法模式

 

[php] view plaincopy

 
  1. <?php  
  2. abstract class PizzaStore {  
  3.     public function orderPizza($type) {  
  4.         $pizza = $this->createPizza($type);  
  5.         $pizza->prepare();  
  6.         $pizza->bake();  
  7.         $pizza->cut();  
  8.         $pizza->box();  
  9.         return $pizza;  
  10.     }  
  11.     public abstract function createPizza($type);  
  12. }  
  13. class NYPizzaStore extends PizzaStore {  
  14.     public function createPizza($type) {  
  15.         if ($type == "cheese") {  
  16.             return new NYStyleCheesePizza();  
  17.         } elseif ($type == "veggie") {  
  18.             return new NYStyleVeggiePizza();  
  19.         } elseif ($type == "clam") {  
  20.             return new NYStyleClamPizza();  
  21.         } elseif ($type == "papperoni") {  
  22.             return new NYStylePapperoniPizza();  
  23.         } else {  
  24.             return null;  
  25.         }  
  26.     }  
  27. }  
  28. class ChicagoPizzaStore extends PizzaStore {  
  29.     public function createPizza($type) {  
  30.         if ($type == "cheese") {  
  31.             return new ChicagoStyleCheesePizza();  
  32.         } elseif ($type == "veggie") {  
  33.             return new ChicagoStyleVeggiePizza();  
  34.         } elseif ($type == "clam") {  
  35.             return new ChicagoStyleClamPizza();  
  36.         } elseif ($type == "papperoni") {  
  37.             return new ChicagoStylePapperoniPizza();  
  38.         } else {  
  39.             return null;  
  40.         }  
  41.     }  
  42. }  
  43. abstract class Pizza {  
  44.     public $name;  
  45.     public $dough;  
  46.     public $sauce;  
  47.     public $toppings = array();  
  48.     public function prepare() {  
  49.         echo "Preparing " . $this->name . "/n";  
  50.         echo "Yossing dough.../n";  
  51.         echo "Adding sauce.../n";  
  52.         echo "Adding toppings: /n";  
  53.         for ($i = 0; $i < count($this->toppings); $i++) {  
  54.             echo "    " . $this->toppings[$i] . "/n";  
  55.         }  
  56.     }  
  57.     public function bake() {  
  58.         echo "Bake for 25 minutes at 350/n";  
  59.     }  
  60.     public function cut() {  
  61.         echo "Cutting the pizza into diagonal slices/n";  
  62.     }  
  63.     public function box() {  
  64.         echo "Place pizza in official PizzaStore box/n";  
  65.     }  
  66.     public function getName() {  
  67.         return $this->name;  
  68.     }  
  69. }  
  70. class NYStyleCheesePizza extends Pizza {  
  71.     public function __construct() {  
  72.         $this->name = "NY Style Sauce and cheese Pizza";  
  73.         $this->dough = "Thin Crust Dough";  
  74.         $this->sauce = "Marinara Sauce";  
  75.         $this->toppings[] = "Grated Reggiano Cheese";  
  76.     }  
  77. }  
  78. class NYStyleVeggiePizza extends Pizza {  
  79.     public function __construct() {  
  80.         $this->name = "NY Style Sauce and veggie Pizza";  
  81.         $this->dough = "Thin Crust Dough";  
  82.         $this->sauce = "Marinara Sauce";  
  83.         $this->toppings[] = "Grated Reggiano veggie";  
  84.     }  
  85. }  
  86. class NYStyleClamPizza extends Pizza {  
  87.     public function __construct() {  
  88.         $this->name = "NY Style Sauce and clam Pizza";  
  89.         $this->dough = "Thin Crust Dough";  
  90.         $this->sauce = "Marinara Sauce";  
  91.         $this->toppings[] = "Grated Reggiano clam";  
  92.     }  
  93. }  
  94. class NYStylePapperoniPizza extends Pizza {  
  95.     public function __construct() {  
  96.         $this->name = "NY Style Sauce and papperoni Pizza";  
  97.         $this->dough = "Thin Crust Dough";  
  98.         $this->sauce = "Marinara Sauce";  
  99.         $this->toppings[] = "Grated Reggiano papperoni";  
  100.     }  
  101. }  
  102. class ChicagoStyleCheesePizza extends Pizza {  
  103.     public function __construct() {  
  104.         $this->name = "Chicago Style Deep Dish Cheese Pizza";  
  105.         $this->dough = "Extra Thick Crust Dough";  
  106.         $this->sauce = "Plum Tomato Sauce";  
  107.         $this->toppings[] = "Shredded Mozzarella Cheese";  
  108.     }  
  109.     public function cut() {  
  110.         echo "Cutting the pizza into square slices/n";  
  111.     }  
  112. }  
  113. $myStore = new NYPizzaStore();  
  114. $chicagoStore = new ChicagoPizzaStore();  
  115. $pizza = $myStore->orderPizza("cheese");  
  116. echo "Ethan ordered a " . $pizza->getName() . "/n";  
  117. $pizza = $chicagoStore->orderPizza("cheese");  
  118. echo "Ethan ordered a " . $pizza->getName() . "/n";  
  119. ?>  

 

工厂模式

 

[php] view plaincopy

 
  1. <?php  
  2. abstract class PizzaStore {  
  3.     public function orderPizza($type) {  
  4.         $pizza = $this->createPizza($type);  
  5.         $pizza->prepare();  
  6.         $pizza->bake();  
  7.         $pizza->cut();  
  8.         $pizza->box();  
  9.         return $pizza;  
  10.     }  
  11.     public abstract function createPizza($type);  
  12. }  
  13. class NYPizzaStore extends PizzaStore {  
  14.     public function createPizza($type) {  
  15.         $pizza = null;  
  16.         $ingredientFactory = new NYPizzaIngredientFactory();  
  17.         if ($type == "cheese") {  
  18.             $pizza = new CheesePizza($ingredientFactory);  
  19.             $pizza->setName('New York Style Cheese Pizza');  
  20.         } elseif ($type == "veggie") {  
  21.             $pizza = new VeggiePizza($ingredientFactory);  
  22.             $pizza->setName('New York Style Veggie Pizza');  
  23.         } elseif ($type == "clam") {  
  24.             $pizza = new ClamPizza($ingredientFactory);  
  25.             $pizza->setName('New York Style Clam Pizza');  
  26.         } elseif ($type == "papperoni") {  
  27.             $pizza = new PapperoniPizza($ingredientFactory);  
  28.             $pizza->setName('New York Style Papperoni Pizza');  
  29.         }  
  30.         return $pizza;  
  31.     }  
  32. }  
  33. class ChicagoPizzaStore extends PizzaStore {  
  34.     public function createPizza($type) {  
  35.         if ($type == "cheese") {  
  36.             return new ChicagoStyleCheesePizza();  
  37.         } elseif ($type == "veggie") {  
  38.             return new ChicagoStyleVeggiePizza();  
  39.         } elseif ($type == "clam") {  
  40.             return new ChicagoStyleClamPizza();  
  41.         } elseif ($type == "papperoni") {  
  42.             return new ChicagoStylePapperoniPizza();  
  43.         } else {  
  44.             return null;  
  45.         }  
  46.     }  
  47. }  
  48. interface PizzaIngredientFactory {  
  49.     public function createDough();  
  50.     public function createSauce();  
  51.     public function createCheese();  
  52.     public function createVeggies();  
  53.     public function createPepperoni();  
  54.     public function createClam();  
  55. }  
  56. class NYPizzaIngredientFactory implements PizzaIngredientFactory {  
  57.     public function createDough() {  
  58.         return new ThinCrustDough();  
  59.     }  
  60.     public function createSauce() {  
  61.         return new MarinaraSauce();  
  62.     }  
  63.     public function createCheese() {  
  64.         return new ReggianoCheese();  
  65.     }  
  66.     public function createVeggies() {  
  67.         $veggies = array(  
  68.         new Garlic(),  
  69.         new Onion(),  
  70.         new Mushroom(),  
  71.         new RedPepper(),  
  72.         );  
  73.         return $veggies;  
  74.     }  
  75.     public function createPepperoni() {  
  76.         return new SlicedPepperoni();  
  77.     }  
  78.     public function createClam() {  
  79.         return new FreshClams();  
  80.     }  
  81. }  
  82. class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {  
  83.     public function createDough() {  
  84.         return new ThickCrustDough();  
  85.     }  
  86.     public function createSauce() {  
  87.         return new PlumTomatoSauce();  
  88.     }  
  89.     public function createCheese() {  
  90.         return new Mozzarella();  
  91.     }  
  92.     public function createVeggies() {  
  93.         $veggies = array(  
  94.         new BlackOlives(),  
  95.         new Spinach(),  
  96.         new EggPlant(),  
  97.         );  
  98.         return $veggies;  
  99.     }  
  100.     public function createPepperoni() {  
  101.         return new SlicedPepperoni();  
  102.     }  
  103.     public function createClam() {  
  104.         return new FrozenClams();  
  105.     }  
  106. }  
  107. abstract class Pizza {  
  108.     public $name;  
  109.     public $dough;  
  110.     public $sauce;  
  111.     public $veggies = array();  
  112.     public $cheese;  
  113.     public $pepperoni;  
  114.     public $clam;  
  115.     public abstract function prepare();  
  116.     public function bake() {  
  117.         echo "Bake for 25 minutes at 350/n";  
  118.     }  
  119.     public function cut() {  
  120.         echo "Cutting the pizza into diagonal slices/n";  
  121.     }  
  122.     public function box() {  
  123.         echo "Place pizza in official PizzaStore box/n";  
  124.     }  
  125.     public function getName() {  
  126.         return $this->name;  
  127.     }  
  128.     public function setName($name) {  
  129.         $this->name = $name;  
  130.     }  
  131.     public function __toString() {  
  132.     }  
  133. }  
  134. class CheesePizza extends Pizza {  
  135.     public $ingredientFactory;  
  136.     public function __construct(PizzaIngredientFactory $ingredientFactory) {  
  137.         $this->ingredientFactory = $ingredientFactory;  
  138.     }  
  139.     public function prepare() {  
  140.         echo "Preparing " . $this->name . "/n";  
  141.         $this->dough = $this->ingredientFactory->createDough();  
  142.         $this->sauce = $this->ingredientFactory->createSauce();  
  143.         $this->cheese = $this->ingredientFactory->createCheese();  
  144.     }  
  145. }  
  146. class ClamPizza extends Pizza {  
  147.     public $ingredientFactory;  
  148.     public function __construct(PizzaIngredientFactory $ingredientFactory) {  
  149.         $this->ingredientFactory = $ingredientFactory;  
  150.     }  
  151.     public function prepare() {  
  152.         echo "Preparing " . $this->name . "/n";  
  153.         $this->dough = $this->ingredientFactory->createDough();  
  154.         $this->sauce = $this->ingredientFactory->createSauce();  
  155.         $this->cheese = $this->ingredientFactory->createCheese();  
  156.         $clam = $this->ingredientFactory->createClam();  
  157.     }  
  158. }  
  159. $nyPizzaStore = new NYPizzaStore();  
  160. $nyPizzaStore->orderPizza('cheese');  
  161. ?>  

 

观察者模式

 

[php] view plaincopy

 
  1. <?php  
  2. /** 
  3.  * 观察者模式 
  4.  * 定义了对象之间的一对多依赖,当一个对象改变状态时, 
  5.  * 它的所有依赖者都会收到通知并自动更新。 
  6.  */  
  7. interface Subject {  
  8.     public function registerObserver(Observer $o);  
  9.     public function removeObserver(Observer $o);  
  10.     public function notifyObservers();  
  11. }  
  12. interface Observer {  
  13.     public function update($temperature, $humidity, $pressure);  
  14. }  
  15. interface DisplayElement {  
  16.     public function display();  
  17. }  
  18. class WeatherData implements Subject {  
  19.     private $observers = array();  
  20.     private $temperature;  
  21.     private $humidity;  
  22.     private $pressure;  
  23.     public function __construct() {  
  24.         $this->observers = array();  
  25.     }  
  26.     public function registerObserver(Observer $o) {  
  27.         $this->observers[] = $o;  
  28.     }  
  29.     public function removeObserver(Observer $o) {  
  30.         if (($key = array_search($o, $this->observers)) !== false) {  
  31.             unset($this->observers[$key]);  
  32.         }  
  33.     }  
  34.     public function notifyObservers() {  
  35.         foreach ($this->observers as $observer) {  
  36.             $observer->update($this->temperature, $this->humidity, $this->pressure);  
  37.         }  
  38.     }  
  39.     public function measurementsChanged() {  
  40.         $this->notifyObservers();  
  41.     }  
  42.     public function setMeasurements($temperature, $humidity, $pressure) {  
  43.         $this->temperature = $temperature;  
  44.         $this->humidity = $humidity;  
  45.         $this->pressure = $pressure;  
  46.         $this->measurementsChanged();  
  47.     }  
  48. }  
  49. class CurrentConditionsDisplay implements Observer, DisplayElement {  
  50.     private $temperature;  
  51.     private $humidity;  
  52.     private $weatherData;  
  53.     public function __construct(Subject $weatherData) {  
  54.         $this->weatherData = $weatherData;  
  55.         $weatherData->registerObserver($this);  
  56.     }  
  57.     public function update($temperature, $humidity, $pressure) {  
  58.         $this->temperature = $temperature;  
  59.         $this->humidity = $humidity;  
  60.         $this->display();  
  61.     }  
  62.     public function display() {  
  63.         echo "温度:" . $this->temperature . "; 湿度:" . $this->humidity . "%/n";  
  64.     }  
  65. }  
  66. class StatistionsDisplay implements Observer, DisplayElement {  
  67.     private $temperature;  
  68.     private $humidity;  
  69.     private $pressure;  
  70.     private $weatherData;  
  71.     public function __construct(Subject $weatherData) {  
  72.         $this->weatherData = $weatherData;  
  73.         $weatherData->registerObserver($this);  
  74.     }  
  75.     public function update($temperature, $humidity, $pressure) {  
  76.         $this->temperature = $temperature;  
  77.         $this->humidity = $humidity;  
  78.         $this->pressure = $pressure;  
  79.         $this->display();  
  80.     }  
  81.     public function display() {  
  82.         echo "温度:" . $this->temperature . "; 湿度:" . $this->humidity . "%; 气压:" . $this->pressure . "/n";  
  83.     }  
  84. }  
  85. $weatherData = new WeatherData();  
  86. $currentDisplay = new CurrentConditionsDisplay($weatherData);  
  87. $statistionDisplay = new StatistionsDisplay($weatherData);  
  88. $weatherData->setMeasurements(10, 20, 30);  
  89. $weatherData->removeObserver($currentDisplay);  
  90. $weatherData->setMeasurements(30, 40, 50);  
  91. ?>  

 

命令模式

 

[php] view plaincopy

 
  1. <?php  
  2. class Light {  
  3.     public function __construct() {  
  4.     }  
  5.     public function on() {  
  6.         echo "Light On/n";  
  7.     }  
  8.     public function off() {  
  9.         echo "Light Off/n";  
  10.     }  
  11. }  
  12. interface Command {  
  13.     public function execute();  
  14. }  
  15. class LightOnCommand implements Command {  
  16.     public $light;  
  17.     public function __construct(Light $light) {  
  18.         $this->light = $light;  
  19.     }  
  20.     public function execute() {  
  21.         $this->light->on();  
  22.     }  
  23. }  
  24. class SimpleRemoteControl {  
  25.     public $slot;  
  26.     public function __construct() {  
  27.     }  
  28.     public function setCommand(Command $command) {  
  29.         $this->slot = $command;  
  30.     }  
  31.     public function buttonWasPressed() {  
  32.         $this->slot->execute();  
  33.     }  
  34. }  
  35. class RemoteControlTest {  
  36.     public static function main() {  
  37.         $remote = new SimpleRemoteControl();  
  38.         $light = new Light();  
  39.         $lightOn = new LightOnCommand($light);  
  40.         $remote->setCommand($lightOn);  
  41.         $remote->buttonWasPressed();  
  42.     }  
  43. }  
  44. RemoteControlTest::main();  
  45. ?>  

 

 

装饰者模式

 

[php] view plaincopy

 
  1. <?php  
  2. /** 
  3.  * 装饰着模式 
  4.  * 动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。 
  5.  */  
  6. abstract class Beverage {  
  7.     public $description = "Unknown Beverage";  
  8.     public function getDescription() {  
  9.         return $this->description;  
  10.     }  
  11.     public abstract function cost();  
  12. }  
  13. abstract class CondimentDecorator extends Beverage {  
  14.     //JAVA代码里这里是个抽象类,PHP不允许这么做  
  15.     public function getDescription() {  
  16.         return $this->description;  
  17.     }  
  18. }  
  19. class Espresso extends Beverage {  
  20.     public function __construct() {  
  21.         $this->description = "Espresso";  
  22.     }  
  23.     public function cost() {  
  24.         return 1.99;  
  25.     }  
  26. }  
  27. class HouseBlend extends Beverage {  
  28.     public function __construct() {  
  29.         $this->description = "HouseBlend";  
  30.     }  
  31.     public function cost() {  
  32.         return .89;  
  33.     }  
  34. }  
  35. class DarkRoast extends Beverage {  
  36.     public function __construct() {  
  37.         $this->description = "DarkRoast";  
  38.     }  
  39.     public function cost() {  
  40.         return .99;  
  41.     }  
  42. }  
  43. class Mocha extends CondimentDecorator {  
  44.     public $beverage;  
  45.     public function __construct(Beverage $beverage) {  
  46.         $this->beverage = $beverage;  
  47.     }  
  48.     public function getDescription() {  
  49.         return $this->beverage->getDescription() . ", Mocha";  
  50.     }  
  51.     public function cost() {  
  52.         return .20 + $this->beverage->cost();  
  53.     }  
  54. }  
  55. class Whip extends CondimentDecorator {  
  56.     public $beverage;  
  57.     public function __construct(Beverage $beverage) {  
  58.         $this->beverage = $beverage;  
  59.     }  
  60.     public function getDescription() {  
  61.         return $this->beverage->getDescription() . ", Whip";  
  62.     }  
  63.     public function cost() {  
  64.         return .10 + $this->beverage->cost();  
  65.     }  
  66. }  
  67. class Soy extends CondimentDecorator {  
  68.     public $beverage;  
  69.     public function __construct(Beverage $beverage) {  
  70.         $this->beverage = $beverage;  
  71.     }  
  72.     public function getDescription() {  
  73.         return $this->beverage->getDescription() . ", Soy";  
  74.     }  
  75.     public function cost() {  
  76.         return .15 + $this->beverage->cost();  
  77.     }  
  78. }  
  79. $beverage = new Espresso();  
  80. echo $beverage->getDescription() . "/n";  
  81. $beverage2 = new DarkRoast();  
  82. $beverage2 = new Mocha($beverage2);  
  83. $beverage2 = new Mocha($beverage2);  
  84. $beverage2 = new Whip($beverage2);  
  85. echo $beverage2->getDescription() . " $" . $beverage2->cost() . "/n";  
  86. $beverage3 = new HouseBlend();  
  87. $beverage3 = new Soy($beverage3);  
  88. $beverage3 = new Mocha($beverage3);  
  89. $beverage3 = new Whip($beverage3);  
  90. echo $beverage3->getDescription() . " $" . $beverage3->cost() . "/n";  
  91. ?>  

 

状态模式

 

[php] view plaincopy

 
  1. <?php  
  2. class GumballMachine {  
  3.     const SOLD_OUT = 0;  
  4.     const NO_QUARTER = 1;  
  5.     const HAS_QUARTER = 2;  
  6.     const SOLD = 3;  
  7.     public $state = self::SOLD_OUT;  
  8.     public $count = 0;  
  9.     public function __construct($count) {  
  10.         $this->count = $count;  
  11.         if ($count > 0) {  
  12.             $this->state = self::NO_QUARTER;  
  13.         }  
  14.     }  
  15.     public function insertQuarter() {  
  16.         if ($this->state == self::HAS_QUARTER) {  
  17.             echo "You can't insert another quarter!/n";  
  18.         } else if ($this->state == self::NO_QUARTER) {  
  19.             $this->state = self::HAS_QUARTER;  
  20.             echo "You inserted a quarter!/n";  
  21.         } else if ($this->state == self::SOLD_OUT) {  
  22.             echo "You can't insert a quarter, the machine is sold out!/n";  
  23.         } else if ($this->state == self::SOLD) {  
  24.             echo "Please wait, we're already giving you a gumball!/n";  
  25.         }  
  26.     }  
  27. }  
  28. $obj = new GumballMachine(0);  
  29. print_r($obj)  
  30. ?>  

 

69月/11

超过 30 个有用的 CSS、jQuery插件和代码技巧

发布在 邵珠庆

Grid Navigation Effects with jQuery

The 30 CSS Selectors you Must Memorize

Beautiful Slide Out Navigation: A CSS and jQuery Tutorial

Fixed Fade Out Menu: A CSS and jQuery Tutorial

Beautiful Slide Out Navigation Revised

Beautiful Background Image Navigation with jQuery

Twitter API and jQuery Showcase: Display your Followers or Friends

CSS and jQuery Tutorial: Fancy Apple-Style Icon Slide Out Navigation

A Fresh Bottom Slide Out Menu with jQuery

Vertical Sliding Accordion with jQuery

Animated Text and Icon Menu with jQuery

Circular Content Carousel with jQuery

Portfolio Image Navigation with jQuery

Expanding Fullscreen Grid Portfolio

Simple jQuery Fluid Thumbnail menu Bar

Slides, A Slideshow Plugin for jQuery

leanModal – a JQuery modal plugin that works with your CSS

Colorful Sliders With jQuery & CSS3

Contextual Slideout Tips With jQuery & CSS3

Awesome Cufonized Fly-out Menu with jQuery and CSS3

Thumbnails Navigation Gallery with jQuery

Creating a Fancy menu using CSS3 and jQuery

Feature Table Design

CSS & jQuery clickable map of Europe

Easy Display Switch with CSS and jQuery

FancyForm

Perfect Full Page Background Image

Greyscale Hover Effect w/ CSS & jQuery

Greyscale Hover Effect w/ CSS & jQuery

Codename Rainbows

Pure CSS3 bokeh effect with some jQuery help

Accessible, Custom Designed Checkbox and Radio Button Inputs Styled with CSS (and a dash of jQuery)

jQuery UI Selectmenu: An ARIA-Accessible Plugin for Styling a Custom HTML Select Element

jQuery & CSS Sprite Animation Explained In Under 5 Minutes

238月/11

收集的一些主流网站的GA代码

发布在 邵珠庆

豆瓣首页、豆瓣读书:
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-7019765-1']);
_gaq.push(['_addOrganic', 'baidu', 'word']);
_gaq.push(['_addOrganic', 'soso', 'w']);
_gaq.push(['_addOrganic', '3721', 'name']);
_gaq.push(['_addOrganic', 'yodao', 'q']);
_gaq.push(['_addOrganic', 'vnet', 'kw']);
_gaq.push(['_addOrganic', 'sogou', 'query']);
_gaq.push(['_addIgnoredOrganic', '豆瓣']);
_gaq.push(['_addIgnoredOrganic', 'douban']);
_gaq.push(['_addIgnoredOrganic', '豆瓣网']);
_gaq.push(['_addIgnoredOrganic', 'www.douban.com']);
_gaq.push(['_setDomainName', '.douban.com']);
_gaq.push(['_trackPageview']);
_gaq.push(['_setVar', '224']);

(function() {
    var ga = document.createElement('script');
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    ga.setAttribute('async', 'true');
    document.documentElement.firstChild.appendChild(ga);
})();
</script>

携程首页、国内机票:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
        var pageTracker = _gat._getTracker("UA-3748357-1");
        pageTracker._setDomainName("ctrip.com");
        pageTracker._addOrganic("soso", "w");
        pageTracker._addOrganic("sogou", "query");
        pageTracker._addOrganic("yodao", "q");
        pageTracker._initData();
        pageTracker._trackPageview();
} catch(e){;};
</script>

麦包包首页

<script type="text/javascript">

//ga
var _ga_username = '{if $globals.user_login}{$globals.user_info.username}{else}非会员{/if}';
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
google_analytics();
function google_analytics(){
try {
var pageTracker = _gat._getTracker("UA-10586344-1");
pageTracker._addOrganic("baidu","word");
pageTracker._addOrganic("soso","w");
pageTracker._addOrganic("3721","name");
pageTracker._addOrganic("yodao","q");
pageTracker._addOrganic("vnet","kw");
pageTracker._addOrganic("sogou","query");
pageTracker._addIgnoredOrganic("麦包包");
pageTracker._addIgnoredOrganic("mbaobao");
pageTracker._addIgnoredOrganic("www.mbaobao.com");
pageTracker._setVar((isset(_ga_username) ? _ga_username : '非会员'));
pageTracker._trackPageview();

$('#a_add_favorites').bind("click", function()
{
pageTracker._trackEvent("网站操作", "收藏网站");
});
} catch(err) {
setTimeout(function(){
google_analytics();
}, 1000);
}
}

</script>

敦煌网首页、商品详情页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-425001-1");
pageTracker._setDomainName(".dhgate.com");
pageTracker._setAllowHash("false");
pageTracker._trackPageview(location.pathname + location.search + escape(location.hash));
} catch(err) {}</script>

京东首页、商品详情页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-2176661-1");
pageTracker._setDomainName("360buy.com");
pageTracker._addOrganic("soso", "w");
pageTracker._addOrganic("sogou", "query");
pageTracker._addOrganic("yodao", "q");
pageTracker._trackPageview();
</script>

走秀网首页、商品详情页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-9540643-1");
pageTracker._initData();
pageTracker._setDomainName(".zoshow.com");
pageTracker._setAllowLinker(true);
pageTracker._trackPageview();
</script>

篱笆网首页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-5496041-1");
pageTracker._setDomainName("liba.com");
pageTracker._initData();
pageTracker._trackPageview();
</script>

篱笆网商品详情页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-5496041-1");
pageTracker._setDomainName("liba.com");
pageTracker._initData();
pageTracker._trackPageview(location.pathname + decodeURI(location.search));
</script>

钻石小鸟:
<script type="text/javascript" src="http://www.google-analytics.com/ga.js"></script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-97277-13");
pageTracker._addOrganic("soso","w");
pageTracker._addOrganic("sogou","query");
pageTracker._addOrganic("youdao","lq");
pageTracker._initData();
pageTracker._setDomainName(".zbird.com");
pageTracker._trackPageview();
</script>

博客大巴首页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-2120146-1");
pageTracker._initData();
pageTracker._trackPageview();
document.onclick = function(e) {
e = e || event;
var el = e.target || e.srcElement;
if (el.tagName=='A' && e.button == 0 && el.href.match("http:\/\/") && el.target=="_blank") {
pageTracker._trackPageview("\/ad/" + window.location.href.replace("http:\/\/www.blogbus.com/", "") + el.href.replace("http:\/\/", "\/"));
        }
}
</script>

博客大巴频道页、文章页、文章列表页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-1649222-2");
pageTracker._addOrganic("baidu","word");
pageTracker._addOrganic("baidu","wd");
pageTracker._addOrganic("soso","w");
pageTracker._addOrganic("vnet","kw");
pageTracker._initData();
pageTracker._trackPageview();
} catch(err) {}</script>

博客大巴注册、登录页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-2120146-17");
pageTracker._trackPageview();
} catch(err) {}</script>

博客大巴搜索列表页:
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-2120146-11");
pageTracker._trackPageview();

天极网:

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var _ga = _gat._getTracker("UA-11613621-1");
_ga._setDomainName(".yesky.com");
_ga._addOrganic("soso","w");
_ga._addOrganic("3721","name");
_ga._addOrganic("yodao","q");
_ga._addOrganic("vnet","kw");
_ga._addOrganic("sogou","query");
_ga._trackPageview();
} catch(err) {}</script>

酷讯网:

<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-4247795-5']);
  _gaq.push(['_setCustomVar','1','{$username}','{$cu_ip}','2']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga
.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
</script>

   下一页