Prototype1.5.1源代碼解讀分析

本來是打算寫一本AJAX的教程的,可又怕自己沒有那麼多時間和能力來完成這個艱鉅的任務。所以決定陸陸續續的把自己學ajax的學習心得記下來,以饗廣大的熱愛自考中國的會員。同時也是給MIANMIAN的版塊加點人氣(有點點假公濟私哦)。這篇文章主要內容是對被廣泛使用prototype框架的源代碼進行解讀。

 

Prototype是爲應用javascript編程開發的一個通用幫助庫(general helper library)。其重點在於擴展javascript語言本身,以便支持更加面向對象的編程風格。什麼?javascript語言支持面向對象編程?是的,我可以肯定的告訴你,javascript語言支持面向對象編程。很多人錯誤的認爲javascript語言所能做的要麼是一些簡單的表單驗證,要麼是許多華而不實的網頁特效。這是不是有點讓你驚訝?prototype可以看作是庫的開發人員使用的庫,它可以在http://www.prototypejs.org下載到最新的版本。目前的最新版本是1.5.1.理解prototype的源代碼有助於幫助我們編寫構造良好的庫。好了,閒話少說,讓我們一起來解讀prototype吧!

 

Prototype對象

源代碼

下面是prototype框架第一段代碼:

 

var Prototype = {

 

 

  Version: '1.5.1',

 

 

 

  Browser: {

 

 

    IE:     !!(window.attachEvent && !window.opera),

 

 

    Opera:  !!window.opera,

 

 

    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,

 

 

    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1

 

 

  },

 

 

 

  BrowserFeatures: {

 

 

    XPath: !!document.evaluate,

 

 

    ElementExtensions: !!window.HTMLElement,

 

 

    SpecificElementExtensions:

 

 

      (document.createElement('div').__proto__ !==

 

 

       document.createElement('form').__proto__)

 

 

  },

 

 

 

  ScriptFragment: '<script[^>]*>([/u0001-/uFFFF]*?)</script>',

 

 

  JSONFilter: /^///*-secure-/s*(.*)/s*/*///s*$/,

 

 

 

  emptyFunction: function() { },

 

 

  K: function(x) { return x }

 

 

}

語法

這段代碼用到的語法:

 

 

什麼是對象?對象是一種複合數據類型,他們將多個數據值集中在一個單元中,而且允許使用名字來存取這些值。解釋對象的另一種方式是,對象是一個無序的屬性集合,每個屬性都有自己的名字和值。存儲在對象中的已命名的值既可以是數字和字符串這樣的原始值,也可以是對象。Javascript對象本質上一個化裝了的關聯數組,由以名字作爲鍵的字段和方法組成。對象是由運算符new創建的。在這個運算符之後必須有用於初始化對象的構造函數名。創建一個新的javascript對象的最簡單的方法是調用object內建的構造函數:

 

 

var myObject=new Object;

 

 

使用JSON.JSON是語言的一個核心特徵,他提供了一種創建數組和對象圖(object graph)的簡單機制。爲了理解JSON,需要知道javascript是如何工作的。我們首先來討論一些關於他們的基礎知識。Javascript有一個內建的array類,可以使用new關鍵字初始化:

 

 

var book=new Array();

 

 

數組有按照數字來分配的值,就像CJAVA的數組一樣:

 

 

book[4]=”100個感動中國的自考人

 

 

數組也可以使用一個健值來關聯,就像JAVAmap一樣。實際上這可以用於任何javascript對象:

 

 

book[“BestSeller”]=”100個感動中國的自考人

 

 

創建一個數字索引的數組的快捷方法是使用方括號,將所有的成員寫成一個用用逗號分隔的值的列表,就像這樣:

 

 

book=[“ajax基礎”,”ajax實戰”,”ajax黑客”]

 

 

爲了創建javascript對象,我們將對象屬性說明放在大括號中,其中的屬性說明由逗號隔開。對象直接量中每個屬性說明都由屬性名加上冒號和屬性值構成。例如:

 

 

book={

 

 

BestSeller:”100個感動中國的自考人”,

 

 

cookbook:”湘菜烹製大全

 

 

}

 

 

函數直接量是用關鍵字function後加可選的函數名、用括號括起來的參數列表和用花括號括起來的函數體定義的。簡言之,函數直接量看起來就像個函數定義,只不過沒有函數名。他們之間最大的差別是函數直接量可以出現在其他javascript表達式中。例如:

 

 

var square=function(x){return x*x}

 

 

代碼解析及其使用方法

下面我們來解析prototype第一段代碼。好長的一段代碼,爲了便於理解,讓我們把這段代碼簡化一下——略去其中的部分細節:

 

 

var Prototype = {

 

 

  Version: '1.5.1',

 

 

  Browser: {

 

 

  

 

 

  },

 

 

  BrowserFeatures: {

 

 

  

 

 

  },

 

 

  ScriptFragment: '<script[^>]*>([/u0001-/uFFFF]*?)</script>',

 

 

  JSONFilter: /^///*-secure-/s*(.*)/s*/*///s*$/,

 

 

  emptyFunction: function() { },

 

 

  K: function(x) { return x }

 

 

}

 

 

這段代碼的意思就是創建了一個名字爲Prototype的對象。該對象有七個屬性:Version BrowserBrowserFeaturesScriptFragmentJSONFilteremptyFunctionK

 

 

Version

 

Version(版本)屬性的值爲“1.5.1”,表示當前版本是1.5.1

 

 

Browser

 

Browser屬性的值是一個對象。該對象是:

 

 

{

 

 

    IE:     !!(window.attachEvent && !window.opera),

 

 

    Opera:  !!window.opera,

 

 

    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,

 

 

    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1

 

 

  }

 

 

作用是嗅探當前使用的所使用的瀏覽器。讓我們來看看在prototypeAPI文檔(下文簡稱文檔)是怎樣描述該屬性的:

 

 

Detects the current browser and returns an object

 

 

Possible Tests: 

 

 

Prototype.Browser.IE

 

 

Prototype.Browser.Opera

 

 

Prototype.Browser.WebKit // Safari

 

 

Prototype.Browser.Gecko 

 

 

Examples

 

 

 

 

 

In case you are viewing in Mozilla: 

 

 

Prototype.Browser

 

 

//-> Object: IE=false Opera=false WebKit=false Gecko=true 

 

 

if(Prototype.Browser.Gecko) {

 

 

  alert("It's a Gecko!")

 

 

}

 

 

譯文:

 

 

作用:探測當前的瀏覽器並返回一個對象

 

 

允許的測試:

 

 

Prototype.Browser.IE

 

 

Prototype.Browser.Opera

 

 

Prototype.Browser.WebKit // Safari

 

 

Prototype.Browser.Gecko 

 

 

例子

 

 

萬一你正在觀察Mozilla瀏覽器

 

 

Prototype.Browser

 

 

//-> 對象: IE=false Opera=false WebKit=false Gecko=true

 

 

if(Prototype.Browser.Gecko) {

 

 

  alert("It's a Gecko!")

 

 

}

 

 

BrowserFeatures

 

BrowserFeatures(瀏覽器特徵)的值也是一個對象。內容如下:

 

 

{

 

 

    XPath: !!document.evaluate,

 

 

    ElementExtensions: !!window.HTMLElement,

 

 

    SpecificElementExtensions:

 

 

      (document.createElement('div').__proto__ !==

 

 

       document.createElement('form').__proto__)

 

 

  }

 

 

BrowserFeatures屬性的作用我也不太理解。在此引用文檔的說法。

 

 

原文:

 

 

Prototype also provides a (nascent) repository of browser feature information, which it then uses here and there in its source code. The idea is, first, to make Prototype source code more readable; and second, to centralize whatever scripting trickery might be necessary to detect the browser feature, in order to ease maintenance. 

The only currently available feature detection is browser support for DOM Level 3 XPath, accessible as a boolean at Prototype.BrowserFeatures.XPath.

譯文:

 

 

 Prototype也規定了一個(初始化的)屬性來描述瀏覽器的特徵信息,然後它在源代碼中被到處使用。這個思想是,第一,讓Prototype的源代碼更易讀;第二,讓無論怎樣的腳本欺騙必然被瀏覽器特徵所探測出來,目的是減輕維護。(譯者注:第二點好像翻譯得不太對哦!水平有限!)

 

 

唯一當前可用的特徵探測是瀏覽器支持DOM LEVEL 3 XPath,Prototype.BrowserFeatures.XPath.中返回一個布爾值

ScriptFragment

 

ScriptFragment屬性的作用是識別腳本的正則表達式,返回一個字符串。在文檔中沒有找到關於他的描述。內容如下:

 

 

ScriptFragment: '<script[^>]*>([/u0001-/uFFFF]*?)</script>'

 

 

JSONFilter

 

JSONFilterJSON過濾器)屬性的作用不詳,在文檔中沒有找到關於他的描述。內容如下:

 

 

JSONFilter: /^///*-secure-/s*(.*)/s*/*///s*$/

 

 

 

 

emptyFunction

 

emptyFunction(空函數)屬性是一個空函數。他不做任何事也不返回任何值。內容如下:

 

 

emptyFunction: function() { }

 

 

K

 

K屬性的作用是回傳參數。內容如下:

 

 

K:function(x) { return x }

 

 

Class

源代碼

下面是prototype框架源代碼的第二段代碼:

 

 

var Class = {

 

 

  create: function() {

 

 

    return function() {

 

 

      this.initialize.apply(this, arguments);

 

 

    }

 

 

  }

 

 

}

 

 

語法

什麼是函數?函數(function)是一個可執行的javascript代碼段,有javascript程序定義或由javascript實現預定義。雖然函數只被定義一次,但是javascript程序卻可以多次執行或調用它。javascript的函數帶有實際參數或者形式參數,用於指定這個函數執行計算要使用的一個或多個值,而且它還能返回一個值,以表示計算結果。javascript語言提供了許多預定義函數,如Math.sin(),它用於計算角的正弦值。

 

 

函數的實際參數:Arguments對象。在一個函數體內,標識符arguments具有特殊含義。它是調用對象的一個特殊屬性,用來引用Arguments對象。Arguments對象就像數組,可以按照數字獲取傳遞給函數的參數值。但它並非真正的Array對象。Arguments對象也定義了callee屬性。儘管定義javascript函數時有固定數目的命名參數,但當調用這個函數時,傳遞給他的參數數目卻可以是任意的。數組arguments[]允許完全地存取那些實際參數值,即使某些參數還沒有被命名。假定你定義了一個函數f,要傳遞給他一個實際參數x,如果你用兩個實際參數來調用這個函數,那麼在函數體內,用形式參數名x或者arguments[0]可以存取第一個實際參數。而第二個實際參數只能通過arguments[1]來存取。而且和所有數組一樣,arguments具有length屬性,用於說明他所含有的元素個數。因此,在函數f的主體內,如果調用時使用的是兩個實際參數,那麼arguments.length的值是2.在這裏,我們多次用到了“arguments數組”,這種說法是不準確的。請記住,arguments並非真正的數組,他是一個Arguments對象。

 

 

Arguments對象有一個非同尋常的特性。當函數具有命名了的參數時,Arguments對象的數組元素是存放函數參數的局部變量的同義詞。arguments[]數組和命名了的參數不過是引用同一變量的兩種不同方法。用參數名改變一個參數的值同時會改變通過arguments[]數組獲得的值。通過arguments[]數組改變參數的值同樣會改變用參數名獲得的參數值。

 

 

ECMAScript v3給所有函數定義了兩個方法call()apply()。使用這兩個方法可以像調用其他對象的方法一樣調用函數。call()apply()都是要調用的函數的對象,在函數體內這一參數是關鍵字this的值。call()的剩餘參數是傳遞給要調用的函數的值。例如,要把兩個數字傳遞給函數f(),並將他作爲對象o的方法調用,可以用如下代碼:

 

 

f.call(o,1,2);

 

 

apply()方法和call()方法相似,只不過要傳遞給函數的參數是由數組指定的。

 

 

javascript有對象和類的概念,但是沒有內建繼承的概念。我們可以通過構造函數來實現類似與內建繼承的功能。在構造函數中,設置爲this的屬性的任何東西隨後都可以作爲對象的一個成員來使用。構造函數有兩個特性:他有new運算符調用;傳遞給他的是一個對新創建的空對象的引用,將該引用作爲關鍵字this的值,而且他還要對新創建的對象進行適當的初始化。

 

 

代碼解析及其使用方法

Class是一個全局對象,他的唯一方法就是create,作用是返回一個函數,類似於ruby的類。prototype通過一個全局對象Class從形式上將函數和類區別開來。Class.create()僅僅是返回一個空類,而且它會默認爲這個類是具有initialize方法的,所以要使用這個類,至少需要有一個構造函數,這就需要使用到類的繼承。(作者注——這樣解釋可能有點難以理解哦,本人水平不高,只領悟到這一層次。)

 

 

示例:

 

 

var Animal = Class.create();

Animal.prototype = {

  initialize: function(name, sound) {

    this.name  = name;

    this.sound = sound;

  }, 

  speak: function() {

    alert(name + " says: " + sound + "!");

  }

}; 

var snake = new Animal("Ringneck""hissssssssss");

snake.speak();

// -> alerts "Ringneck says: hissssssssss!" 

var Dog = Class.create(); 

Dog.prototype = Object.extend(new Animal(), {

  initialize: function(name) {

    this.name  = name;

    this.sound = "woof";

  }  

}); 

var fido = new Dog("Fido");

fido.speak();

// -> alerts "Fido says: woof!"

Abstract

源代碼

var Abstract = new Object();

 

 

代碼解析及其使用方法

Abstract(抽象類)是個空類,沒有任何成員。

 

 

Object.extend

源代碼

Object.extend = function(destination, source) {

 

 

  for (var property in source) {

 

 

    destination[property] = source[property];

 

 

  }

 

 

  return destination;

 

 

}

 

 

Object.extend(Object, {

 

 

  inspect: function(object) {

 

 

    try {

 

 

      if (object === undefined) return 'undefined';

 

 

      if (object === null) return 'null';

 

 

      return object.inspect ? object.inspect() : object.toString();

 

 

    } catch (e) {

 

 

      if (e instanceof RangeError) return '...';

 

 

      throw e;

 

 

    }

 

 

  },

 

 

  toJSON: function(object) {

 

 

    var type = typeof object;

 

 

    switch(type) {

 

 

      case 'undefined':

 

 

      case 'function':

 

 

      case 'unknown': return;

 

 

      case 'boolean': return object.toString();

 

 

    }

 

 

    if (object === null) return 'null';

 

 

    if (object.toJSON) return object.toJSON();

 

 

    if (object.ownerDocument === document) return;

 

 

    var results = [];

 

 

    for (var property in object) {

 

 

      var value = Object.toJSON(object[property]);

 

 

      if (value !== undefined)

 

 

        results.push(property.toJSON() + ': ' + value);

 

 

    }

 

 

    return '{' + results.join(', ') + '}';

 

 

  },

 

 

  keys: function(object) {

 

 

    var keys = [];

 

 

    for (var property in object)

 

 

      keys.push(property);

 

 

    return keys;

 

 

  },

 

 

  values: function(object) {

 

 

    var values = [];

 

 

    for (var property in object)

 

 

      values.push(object[property]);

 

 

    return values;

 

 

  },

 

 

  clone: function(object) {

 

 

    return Object.extend({}, object);

 

 

  }

 

 

});

 

 

語法

所謂方法(method),其實就是通過對對象調用的javascript函數。我們可以將函數賦給任何變量,甚至賦給一個對象的任何屬性。如果有一個函數f和一個對象o,就可以使用下面的代碼定義一個名爲m的方法:

 

 

o.m=f

 

 

注意:我們這裏省略了f函數的園括號。如果寫成:

 

 

o.m=f();

 

 

那麼將執行f函數,並且用他的返回值來給o對象的m屬性賦值。

 

 

定義對象o的方法m()之後,就可以用下面的方式來調用他:

 

 

o.m();

 

 

每個對象都繼承原型對象的所有屬性。一個對象的原型是由創建並初始化該對象的構造函數定義的。javascript中所有的函數都有prototype屬性 ,他引用了一個對象。因爲原型對象和構造函數關聯在一起,所以類的每個成員都從原型對象繼承了相同的屬性。注意,繼承是在查詢一個屬性值時自動發生的。我們在聲明構造函數之後,才能引用原型,對象只繼承那些在調用構造函數之前已經添加到原型上的東西。例如:

 

 

function MyObject(name,size){

 

 

   this.name=name;

 

 

   this.size=size;

 

 

}

 

 

MyObject.prototype.tellSize=function(){

 

 

   alert(“size of”+this.name+” is ”+this.size);

 

 

}

 

 

var myObj=new MyObject(“tiddles”,”7.5”);

 

 

myObj.tellSize();

 

我們可以使用原型機制來擴展內建類。例如:

 

 

Array.prototype.indexOf=function(obj)

 

 

{

 

 

  var result=-1;

 

 

  for(var i=0;i<this.length;i++){

 

 

   if(this[i]==obj){

 

 

   result=I;

 

 

   break;

 

 

}

 

 

}

 

 

return result;

 

 

}

 

 

for/in語句。這個語句是有點特別的循環語句。他的語法如下:

 

 

for(variable in object) {

 

 

 statement

 

 

}

 

 

variable應該是一個變量名,聲明一個變量的var語句,數組的一個元素或者一個對象的一個屬性,object是一個對象名,或者是一個計算結果爲對象的表達式。statement是循環體。for/in循環的主題對object的每個屬性執行一次。在循環體執行之前,對象的一個屬性名會被作爲字符串賦給變量variable。在循環體內部,你可以使用這個變量和“[]”運算符來查詢該對象屬性的值。

 

 

try/catch/finally語句是javascript的異常處理機制。該語句的try從句只定義異常需要被處理的代碼塊。catch從句跟隨在try塊後,他是在try塊內的某個部分發生了異常時調用的語句塊。finally塊跟隨在catch從句後,存放清除代碼,無論try塊中發生了什麼,該代碼塊都會被執行。雖然catch塊和finally塊都是可選的,但是try塊中至少應該有一個catch塊或finally塊。trycatchfinally塊都以大括號開頭和結尾。這是必須的語法部分,即使從句只有一條語句,也不能省略大括號。

 

 

接下來的代碼說明了try/catch/finally語句的語法和目的。尤其要注意cath關鍵字後用括號括起來的標識符。該標識符就像函數的參數,他指定了一個僅存在於catch塊內部的局部變量。javascript將把要拋出的異常對象或值賦給這個變量:

 

 

try {

 

 

   //通常,該代碼從代碼塊的頂部運行到底部沒有任何問題,

 

 

   //但有時他會拋出異常,

 

 

   //既可以用throw語句直接拋出,也可以調用一個拋出異常的方法間接拋出,

 

 

}

 

 

catch(e){

 

 

  //當且僅當try塊拋出了異常,本塊中的語句纔會被執行

 

 

  //這些語句使用局部變量e引用拋出的error對象或其他值。

 

 

  //這個塊可以以某種方式處理異常,或者什麼都不做,

 

 

  //忽略異常,或者用throw語句再拋出一個異常。

 

 

}

 

 

finally{

 

 

   //無論try塊中發生了什麼,這個塊中的代碼都會被執行。

 

 

   //try是否終止,他們都會被執行:

 

 

   //1)正常地,在達到try塊底部後

 

 

   //2)由於breakcontinuereturn語句終止

 

 

   //3)拋出一個異常,由catch從句處理

 

 

   //4)拋出一個異常,沒有被捕捉,仍舊在傳播

 

 

}

 

 

條件運算符(?:)是javascript中唯一的三元運算符(帶有三個運算數)。他具有三個運算數,第一個位於?之前,第二個位於?:之間,第三個位於:之後,可以用如下方式來使用他:

 

 

 

 

  x>0?x*y:-x*y;

 

 

 

 

條件運算符的第一個運算數必須是一個布爾值(或者能夠被轉換爲布爾值),通常他是一個比較表達式的結果。第二個和第三個運算數可以是任何類型的值,條件運算符的返回值是由第一個運算數的布爾值決定的。如果這個運算數的值爲true,那條件表達式的值就是第二個運算數的值,否則,條件表達式的值是第三個運算數的值。雖然使用if語句可以得到同樣的結果,但是在許多情況下使用條件運算符更爲快捷。

 

 

toString方法。作用返回對象的字符串表示。語法:

 

 

objectname.toString([radix])

 

 

參數

 

 

objectname

 

 

  必選項。要得到字符串表示的對象。

 

 

radix

 

 

  可選項。指定將數字值轉換爲字符串時的進制。

 

 

toString方法是所有內建的javascript對象的成員。

 

 

instanceof運算符。作用是返回一個布爾值,指出對象是否是特定類的一個實例。語法:

 

 

   result=object instanceof class;

 

 

參數

 

 

resualt

 

 

  必選項。任意變量。

 

 

object

 

 

  必選項。任意對象表達式。

 

 

class

 

 

  必選項。任意已定義的對象類。

 

 

示例:

 

 

function objTest(obj){

 

 

   var i, t, s = "";   // 創建變量。

 

 

   t = new Array();   // 創建一個數組。

 

 

   t["Date"] = Date;   // 填充數組。

 

 

   t["Object"] = Object;

 

 

   t["Array"] = Array;

 

 

      for (i in t)

 

 

      {

 

 

         if (obj instanceof t[i])   // 檢查 obj 的類。

 

 

         {

 

 

            s += "obj is an instance of " + i + "/n";

 

 

         }

 

 

         else

 

 

         {

 

 

            s += "obj is not an instance of " + i + "/n";

 

 

         }

 

 

   }

 

 

   return(s);   // 返回字符串。

 

 

}

 

 

 

 

var obj = new Date();

 

 

response.write(objTest(obj));

 

 

name屬性

返回一個錯誤的名稱。

errorObj.name

參數

 

 

errorObj

必選項。Error 對象。

說明

 

 

name 屬性返回錯誤名稱或異常類型。發生運行時錯誤時,該錯誤的名稱屬性被設置爲下列內在的異常類型之一:

異常類型

 

 

意義

 

 

ConversionError

 

當試圖將一個對象轉換爲其不能轉換的某種類型時,產生本錯誤。

 

 

RangeError

 

當函數的某個給出的參數不在允許範圍時,發生本錯誤。例如,當試圖建立的 Array 對象的長度不是有效的正整數時就會發生本錯誤。

 

 

ReferenceError

 

當檢測到無效的引用時,發生本錯誤。例如,如果所想要使用的引用爲 null 時就會發生本錯誤。

 

 

RegExpError

 

當正則表達式產生編譯錯誤時,發生本錯誤。然而,只要該正則表達式經過了編譯,就不會產生本錯誤。例如,如果使用無效語法,或標誌不爲 igm,或者同一標誌出現多次的樣式聲明正則表達式時,就會發生本錯誤。

 

 

SyntaxError

 

當對錯誤語法的源文本進行解析時,發生本錯誤。例如,調用 eval 函數時其參數不是有效的程序文本,就會發生本錯誤。

 

 

TypeError

 

只要算子的實際類型與所期望的類型不符合,就會發生本錯誤。例如,如果進行函數調用的不是對象或者不支持該調用,發生本錯誤。

 

 

URIError

 

當檢測到非法的統一資源標識符 (URI) 時發生本錯誤。例如,在被編碼或解碼的字符串中發現非法字符,就會發生本錯誤。

 

 

 

 

以下示例將導致 TypeError 異常,並顯示該錯誤的名稱及其消息。

try {

 

  // 'null' 不是有效的對象
  null.doSomething();
}
catch(e){
  print(e.name + ": " + e.message);
  print(e.number + ": " + e.description);
}

所謂異常是一個信號,說明發生了某種異常狀況或錯誤。拋出(throw)一個異常,就是用信號通知發生了錯誤或異常狀況。捕捉(catch)一個異常,就是處理他,即採取必要或適當的動作從異常恢復。當發生運行時錯誤或程序明確地使用throw語句時就會拋出異常。使用try/catch/finally語句可以捕捉異常。

 

 

throw語句的語法如下:

 

 

  throw expression;

 

 

expression的值可以是任何類型的。但通常他是一個error對象或error子類的一個實例。

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章