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子类的一个实例。

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值