分类目录归档:Google代码欣赏

Python的确是非常棒的语言

早上6点半爬起来,花了几个小时学习Python。

只能感慨我来晚了,晚了12年。在我在微软做工程师的时候就应该学习这种语言了。显然Python有很多优于PHP的地方,就像PHP对于写网站有太多因为“幼稚”而优于Java的地方。

一句话形容Python和PHP的区别:

PHP去掉所有的额外输入$ ; { }, 把function改成def, => 改成点,基本上就是Python了。

哈哈。只是开个玩笑,但Python的很多设计还是如此的一致和简洁。比如class里面就是没有从天上掉下来的$this,处理帮助也都用函数help()。

语言本身是有高下差别的。只要看一下开发社区在“自己的时间里”用什么语言,就大概能够猜出来些门道。好多程序员在工作的地方必须用Java或者PHP,或者.NET,但的确有很多程序员选择了Python来写自己的项目。说明这里一定有一些东西在发生。不去看看是不知道的。

愿意学习一种新的语言是一个好的程序员身上带有的很特殊的味道。Hacker本身就是不遵循任何规矩,自己动手丰衣足食的那种精神,而语言作为作重要的一种工具,是一个好的hacker不断寻求的。对于语言的追求,可以当作选择好的程序员的一种标准。

在Blog上面集成Facebook Connect

刚刚把Facebook Connect在我的中文英文的blog上面实施了一下。这样子,如果你有Facebook的账号,你就可以直接点那个Facebook的按钮,你的Facebook的信息,就自动填在评论的表单里面,你的评论旁边就有了你在Facebook的头像,而且接下来,我还可以让在这里的评论自动发表在Facebook里面,可以让别人看到。

做这些事情还是需要一点简单的JavaScript的编程。我有两个惊讶的事情:第一,Facebook的编程模型的确诡异,几乎所有的函数调用都接受一个回调函数作为最后一个参数,函数执行不是以返回结束,而是通过调用回调函数结束,这样就出现了迷宫一样的一个函数潜逃一个函数的状况,以及结束时候多达10个的右花括号或者右括号。第二,Facebook如何在一个Blog站点上面集成这样一个通用的应用,居然在他们的网站上面没有例子。如下的几行代码,倒还真是让我花费了一段时间,直搞得我眼花缭乱,耗时2个小时才搞定。所以想我应该把过程记录下来,以便其他blogger可以借用,同时也显摆一下,说明Hello World级别的编程,我还是可以的。

步骤

假设你有一个blog,无论是MovableType架的,还是WordPress,或者自己写的,只要你有一个留评论的表单,应该都可以使用这个办法。这是一个纯JavaScript的应用,不需要服务器端的编程。

  1. 看看这篇文章《尝试Facebook Connect(英文). 按照里面的第一步 (包含8个小步骤),和第二步,注册一个新的API密钥,然后设置一下你的回调文件所在的文件夹。我设的是:http://home.wangjianshuo.com/scripts/facebook folder。这个里面放着Facebook给的xd_receiver.htm文件。按道理我应该复述一下步骤的,不过复述步骤是在99年的时候做微软的支持工程师的几年做腻了的事情,不想再做了。大家移步到那里去看吧。
  2. 进入你的编辑器,来编辑模板。MovableType中要改的是“Invidivial Arcihve Page”,其他的工具应该也有类似的名字。
  3. 把HTML的根tag html从

    <html xmlns=”http://www.w3.org/1999/xhtml”>

    改成

    <html xmlns=”http://www.w3.org/1999/xhtml” xmlns:fb=”http://www.facebook.com/2008/fbml”>

    。注意,就是添加一个fb的命名空间。这个可的确是不容易理解。不理解也罢。

  4. 找到输入名字,邮件和URL的地方(通常是一些input),把下面的代码粘贴进去。这个主要是一些格式上的东西,让你的那个Facebook的按钮在输入框的右边。我最终还是觉得用table来布局远比用CSS float等方式要来的直接和不容易出错。这段代码里面真正重要的,只有Facebook登陆按钮那一段,我用红色粗体表示。

    <table width=”80%” cellpadding=”0″ cellspacing=”0″>
    <tr><td valign=”top” id=”authorInfo”>

    Name: <br />
    <input name=”author” /><br />
    Email Address: (will not show)<br />
    <input name=”email” /><br />
    <input name=”url” /><br />
    </td><td width=”50%” align=”right” valign=”top” style=”border-left:1px solid #336699″>
    You can optionally sign in<br/> with your Facebook account.<br/>
    <fb:login-button autologoutlink=”true” length=”long”></fb:login-button>
    </td></tr></table>

  5. 把下面的一段贴在整个表单的后面,主要把apiKey,和xdPath替换成你自己的。我假设你的表单叫做comments_form,你的应该叫其他的名字。


    <script src=”http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php” type=”text/javascript”></script>
    <script type=”text/javascript”>
          var apiKey = “655b6e2e042a5426d8f0b13dca4708fa”;
    var xdPath = “/scripts/facebook/xd_receiver.htm”;
          FB.init(apiKey, xdPath);
          FB_RequireFeatures([“Connect”, “Api”], function() {
                      FB.Facebook.init(“655b6e2e042a5426d8f0b13dca4708fa”, xdPath);
                      FB.Facebook.get_sessionState().waitUntilReady(function(){
                                  FB.Facebook.apiClient.users_getInfo(new Array(FB.Facebook.apiClient.get_session().uid), [“name”,”proxied_email”, “profile_url”, “pic_small”], function(result, ex) {
                                              if(result.length > 0) {
                                                    document.comments_form.author.value = result[0].name;
                                                    document.comments_form.email.value = result[0].proxied_email;
                                                    document.comments_form.url.value = result[0].profile_url;
                                                    document.comments_form.author.readOnly = “readOnly”;
                                                    document.comments_form.email.readOnly = “readOnly”;
                                                    document.comments_form.url.readOnly = “readOnly”;
                                                    var authorInfo = document.getElementById(“authorInfo”);
                                                    var img = document.createElement(“img”);
                                                    img.src = result[0].pic_small;
                                                    var td = document.createElement(“td”);
                                                    td.appendChild(img);
                                                    authorInfo.parentNode.insertBefore(td, authorInfo);
                                              }                                    
                                        }
                                  );
                            }
                      );
                }
          );
    </script>

  6. 保存并且重新build一下你的文件

如果幸运的话,你看到的应该和我的这个blog的样子一样。不过。。。因为Facebook的异步和跨域通讯特征,我觉得一次调试成功的几率不大,尤其是根据这份极为简略的攻略来做。

如果有任何问题,可以留言。

看看这个机柜

这东西是8年前的一个机柜。

下面的照片里面,绿色和白色相间的一条是reset的线,连着一个按钮,一条是普通PC的硬盘灯。四个这样按钮和灯用四个螺丝钉直接固定在一个硬纸板上面。

显卡的位置什么也没有插,其他的扩展槽也都空着。

只有一个网卡,和网线。

CPU是奔二的。

这个是整个的。

这个惠普的Switch总共有80条进线。

这一个机柜,总共是20层。每一层的架子上面垫着一层松木板的隔层,隔层上面放四个一摸一样的主版,四块硬盘放在中间一条。

这家公司最初的时候有30个这样的柜子。如果按照每台都是这个样子的话,那就是2400台最普通的台式机在一起运作。而这家公司现在估计有45万台这样的服务器。

我想了一下,99年的时候,其实很值得拼一下这样的东东的。

Symbol表的启发

本文是《google.loader 代码欣赏》系列的一部分。

这段代码原本是非常好读懂的,但是当所有的函数名和变量名都用小写的a到z之间的一个字母代替以后,就变得不好读起来,尤其是这段令人抓狂的代码,

function k(c,a,b,d,e,f){
this.a=c;
this.g=a;
this.f=b;
this.d=d;
this.e=e;
this.b=f
}

不死一堆脑细胞是很难摸得到门路的。

其实,这说明,这个代码一定是经过编译的。说编译可能有点不是很准确,说“转化”或者更恰当些。为什么要这么做呢?

Symbol表

在整个代码的最后一句

g(“google_exportSymbol”,g);

泄露了些原因。

Symbol是什么?在Windows的世界(或者几乎所有的编译语言,或曰高级语言的世界里),都少不了Symbol这个东西。在编译的时候,所有的函数名,变量的名字,都会被一个地址或者一个标记代替,写进二进制文件里面,所以如果从一个.exe文件反编译出来,还是可以看得出执行的语句逻辑的(用汇编的形式),却永远无法获取原来的源代码。而Symbol文件,就是连接编译好的二进制程序,和源代码的桥梁,包含了关于函数,变量的名字以及源代码的文件名以及行数的信息。而Symbol文件编译以后,一般不和执行文件一起发布,需要另外去找。比方说当年调试IIS的时候,就把IIS的Symbol拿过来,再把源代码拿过来,用Windbg就可以开工了。。。这是以前的古老的Windows时代一个程序员要做的事情(对了,现在所有的Windows公开的Symbol可以在这里获得)。Symbol简直就是Windows世界里的解开二进制文件的一把神秘的钥匙。

Google Loader的Symbol库

既然有编译,就一定要有一张自己存着的对应表格。我猜测这个表格应该是这样样子的:

function “google_exportSymbol” => function “g”, @d:\my\code\uds\uds.js line #122
function “setApiKeyLookupMap” => function “p”, @d:\my\code\uds\uds.js line #102
function “load” => “o”, @d:\my\code\uds\uds.js line #77
function “writeLoadTag” => “m”, @d:\my\code\uds\uds.js line #115
var “google_exportSymbol”::”funcNameString” => “c”, @d:\my\code\uds\uds.js line#122
var “google_exportSymbol”::”function” => “a”, @d:\my\code\uds\uds.js line #122

需要说明的是,我没有原来的代码,也彻底不知道这个Symbol表是不是存在,甚至在这里开玩笑一样的假设原来的代码放在一台Windows的机器上面(显然这不是很可能),但我希望就是表达这么一个意思,就是说,如果有编译,就应该附属产生这样的一个Symbol表,只不过没有公布而已。如果没有这个表,debug会是一件和我们直接看源代码一样痛苦的过程。

DLL的exportSymbols

如果大家熟悉Windows的DLL文件结构的话,就会发现exportSymbols和importSymbols是一个常见的概念。就是说,虽说在DLL内部所有的函数的名字已经被编译的面目全非,就像这里的函数g(),函数q()一样,但是为了方便外界的调用,还是把一个标准的接口给输出(export)出去。打个比方,一个Windows XP上的c:\windows\system32\user32.dll就export了732个之多的函数(函数名是字符串,比如ActivateKeyboardLayout,实际的函数确实一个地址为:7E42D32Ah的EntryPoint。

微软的世界和Google的世界

一个微软的世界的基石应该是PE(Portal Exexcutable)的文件格式(也就是.EXE, .DLL所使用的格式),这些格式让DOS操作系统可以”load”到内存里面执行。

那么在Google的世界里面先出现了一个loader(就好比DOS的loader),然后作为load的基础函数出现了这么一个google_exportSymbols,这是巧合呢,还是意味着什么呢?

且听下回分解。

注:第二次(其实是不知道多少次)声明,以上纯属个人的主观臆断,就好像听到了“咕咚”的声音就惊呼“咕咚来了”的猴子一样。或许是真的“咕咚”来了,或许仅仅是一个熟了的果子掉到湖里而已。是哪一种?只有时间告诉我们。

用匿名函数避免命名冲突

本文是google.loader 代码欣赏系列的第二部分。

第一个匪夷所思的地方,可能就是这一段了

if (!google.loader) {
google.loader = {};
google.loader.ServiceBase = “http://www.google.com/uds”;
google.loader.ApiKey = “internal”;
google.loader.KeyVerified = true;
google.loader.LoadFailure = false;
google.loader.AdditionalParams = “”;
(function() {
//大量的函数定义,变量声明以及函数的执行(整整100多行)
})()
}

基本上就是说,它写了一个这样的语法:

(function(){})()

就算是看得懂,这么绕来绕去,除了炫耀之外,又有意义呢?

匿名函数

先撇开Google的代码,看一下JavaScript其实是支持这种形式的行数定义的:

function(msg){ alert(msg); }(“hello world”);

这其实是两步:第一步,定义了一个函数,相当于:

var abc = function(msg) { alert(msg);}

第二步,立刻执行它:

abc(“hello world”);

把两个语句拼接在一起,然后去掉abc这个函数的名字,就成了现在的语法形式。

结合前面的

google.loader = {};

一起看,其实在这里是定义了google.loader这个对象的一个匿名的成员函数,并且立刻执行了这个成员函数(就是说完成里面的一些列初始化的时候需要干的事情)。

匿名函数的用处

虽然可以理解这里是个匿名函数,但是为什么要这么做呢?

我猜想(请注意,所有这里想的都是一个猜想,并且可能是众多原因之中的一种而已,欢迎大家补充),最主要的考虑是避免命名冲突。

因为这段JavaScript作为Google的其他的各项服务的入口,是会被全球很多的网页引用的。像任何的JavaScript引用(包括PHP的引用),如果被引用的代码和外面的代码一不小心用了同样的名字,就不可避免的发生命名冲突。对于小范围的代码或许还可以用起很怪异的名字的方法企图避免,而对于Google这样的应用,靠运气就有些说不过去。如果是一个匿名函数,外面将永远无法直接访问它(总不能写一个这样的一个没有函数名的调用吧:())。从这一点来说,不可能有命名冲突。

封装

另外的一个好处,有可能是为了更好的封装。比如在这个函数里面,有很多的函数定义,比如

q(); p(); m(); g(); i()

还有大量的变量:

var j; var h; var l;

等等。在JavaScript里面没有简单的private这样的定义私有函数或者私有函数的方法。如果调用者可以随心所欲的访问到这些中间的(随时可能变化,甚至移除)成员。对于这么一个开肠破肚,一览无余的对象,从一个API提供者的角度来看(和使用者的角度来看),的确是个挺恐怖的事情。

如果放在一个匿名的函数里面,外界就再也没有办法直接访问到里面的函数了。内部的逻辑被完美的封装了起来。这样一来,这些函数和变量多么的安全!

安全是安全了,但是定义了这么多的函数,仅仅是为了不被外界访问吗?显然不是,所以接下来要做的事情,就是把这些函数输出出去,也就是google_exportSymbol,或者说是函数g()要做的事情。

且听下回分解。

注:接下来,或许还要说说: 输出函数供开发者使用, JavaScript中的面向对象, prototype的扩展方式, Undocumented的神秘参数, 编译后的JavaSscript好比汇编语言,和 Symbol表的启发。

google.loader 代码欣赏

缘起

五一期间和老冒聊天,其中就提起来保持技术的敏感性是聊起技术型CEO的优势,也是一直需要不断努力充电的。很受启发,所以就顺手拿起一段Google的Javascript,欣赏一番。

这段JavaScript的URI(沿用Tim Berneris Lee喜欢用的URI而不是URL)很酷:

http://www.google.com/jsapi

在我写这篇文章的时候,也可以用这个URL也可以访问到:http://www.google.com/uds相同的内容。

单单挑这一段代码有几个原因:

  1. 这是分析最近的Google的所有代码的起点。在最近的Google AJAX Feed API文档里面提到,“Google正在向一个新的编程模式转移”,说的就是这一行代码。比如AJAX Feed API的开始调用就用如下语法:

    <script type=”text/javascript” src=”http://www.google.com/jsapi?key=YOUR_KEY_HERE”></script>
    <script type=”text/javascript”>
    google.load(“feeds”, “1”);
    </script>

    其中调用的就是这段神秘的JavaScript。

  2. 这段在浏览器里看起来不过27行,按照最标准的折行以后也不过100行的代码,第一遍看起来,真是彻底不知道它在干些什么,甚至怀疑它是不是用JavaScript写的。如果能很透彻的看懂这个代码,也就说明JavaScript的基本概念掌握的还可以。研究的时候,的确重温了n年前学习JavaScript的一些概念。
  3. 第三个原因,就是通过研究这个代码,可以隐约的感觉到一个新的基于Web的操作系统的到来。这个Google Loader,和早年的DOS加载模块是如此的神似,却有几乎没有任何形似的地方。通过这个代码,让我们可以窥到一些未来的样子。
  4. 最后一个原因就是,在我写准备写点东西的时候,把这段代码中最有标志性的google_exportSymbol函数拿到Google里面搜索,居然只有一篇文章提到,而且还仅仅是贴了代码。难道这么重要的一个文件,全世界的开发者没有兴趣分析一下吗?

好了,现在开始看代码。

代码全文

经过简单的代码格式化(仅仅加上回车),这段代码看起来是这样的。努力想想,他究竟是干什么的呢?

if (!google) {
   var google = {};
}
if (!google.loader) {
   google.loader = {};
   google.loader.ServiceBase = “http://www.google.com/uds”;
   google.loader.ApiKey = “internal”;
   google.loader.KeyVerified = true;
   google.loader.LoadFailure = false;
   google.loader.AdditionalParams = “”;
   (function() {
      Function.prototype.__google_inherits=function(c){
         var a=function(){};
         a.prototype=c.prototype;
         this.prototype=new a;
         this.prototype.__google_super=function(b,d,e,f){
            var n=Array.prototype.h.apply(arguments,[1,arguments.length]);
            return b.apply(this,n)
         }
      };
      var h=null;
      var l=false;
      function i(c){this.a=c}
      i.prototype.c=function(c,a){
         var b=””;
         if(a!=undefined){
            if(a[“locale”]!=undefined){
               b+=”&hl=”+encodeURIComponent(a[“locale”])
            }
            if(a[“nocss”]!=undefined){
               b+=”&output=”+encodeURIComponent(“nocss=”+a[“nocss”])
            }
            if(a[“other_params”]!=undefined){
               b+=”&”+a[“other_params”]
            }
         }
         if(h!=null&&!l){
            b+=”&key=”+encodeURIComponent(h);
            l=true
         }
         return google.loader.ServiceBase+”/?file=”+this.a+”&v=”+c+google.loader.AdditionalParams+b
      };
      function k(c,a,b,d,e,f){
         this.a=c;
         this.g=a;
         this.f=b;
         this.d=d;
         this.e=e;
         this.b=f
      }
      k.__google_inherits(i);
      k.prototype.c=function(c,a){
         var b=””;
         if(this.d!=undefined){
            b+=”&”+this.d+”=”+encodeURIComponent(h?h:google.loader.ApiKey)
         }
         if(this.e!=undefined){
            b+=”&”+this.e+”=”+encodeURIComponent(c)
         }
         if(a!=undefined&&this.b!=undefined){
            for(var d in a){
               if(this.b[“:”+d]!=undefined){
                  b+=”&”+this.b[“:”+d]+”=”+encodeURIComponent(a[d])
               }
               else
                  if(d==”other_params”){
                     b+=”&”+a[d]
                  }
               }
            }
            google[this.a]={};
            if(!this.f&&b!=””){
               b[0]=”?”
            }
            return this.g+b
         };
         function o(c,a,b){
            var d=j[“:”+c];
            if(!d){
               var e=new Error(“Module: ‘”+c+”‘ not found!”);
               e.toString=function(){
                  return this.message
               };
            throw e;
         }
         else{
            m(“script”,d.c(a,b))
         }
      }
      function q(c){
         var a=window;
         if(a.addEventListener){
            a.addEventListener(“load”,c,false)
         }
         else if(a.attachEvent){
            a.attachEvent(“onload”,c)
         }
         else{
            a[“onload”]=c
         }
      }
      function p(c){
         var a=window.location.href;
         var b;
         var d=a.length;
         for(var e in c){
            var f=a.indexOf(e);
            if(f!=-1&&f<\/script>’)
         }else if(c==”css”){
            document.write(‘<link href=”‘+a+'” type=”text/css” rel=”stylesheet”></link>’)
         }
      }
      function g(c,a){
         var b=c.split(/\./);
         var d=window;
         for(var e=0;e

真的要分析完全这里面的有趣的东西,还真是要点时间。不如分步骤,一个话题一篇文章,这样看起来容易一些。估计一天晚上也写不完,正好大家和我一起来看这段代码。