prototype源码中文说明

/**   * 定义一个全局对象, 属性 Version 在发布的时候会替换为当前版本号   */  var Prototype = {   Version: '@@VERSION@@'  }  /**   * 创建一种类型,注意其属性 create 是一个方法,返回一个构造函数。   * 一般使用如下   * var X = Class.create(); 返回一个类型,类似于 java 的一个Class实例。   * 要使用 X 类型,需继续用 new X()来获取一个实例,如同 java 的 Class.newInstance()方法。   *   * 返回的构造函数会执行名为 initialize 的方法, initialize 是 Ruby 对象的构造器方法名字。   * 此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。   *   * 如果一定要从java上去理解。你可以理解为用Class.create()创建一个继承java.lang.Class类的类。当然java不允许这样做,因为Class类是final的   *   */  var Class = {   create: function() {   return function() {   this.initialize.apply(this, arguments);   }   }  }  /**   * 创建一个对象,从变量名来思考,本意也许是定义一个抽象类,以后创建新对象都 extend 它。   * 但从其后代码的应用来看, Abstract 更多是为了保持命名空间清晰的考虑。   * 也就是说,我们可以给 Abstract 这个对象实例添加新的对象定义。   *   * 从java去理解,就是动态给一个对象创建内部类。   */  var Abstract = new Object();  /**   * 获取参数对象的所有属性和方法,有点象多重继承。但是这种继承是动态获得的。   * 如:   * var a = new ObjectA(), b = new ObjectB();   * var c = a.extend(b);   * 此时 c 对象同时拥有 a 和 b 对象的属性和方法。但是与多重继承不同的是,c instanceof ObjectB 将返回false。   */  Object.prototype.extend = function(object) {   for (property in object) {   this[property] = object[property];   }   return this;  }  /**   * 这个方法很有趣,它封装一个javascript函数对象,返回一个新函数对象,新函数对象的主体和原对象相同,但是bind()方法参数将被用作当前对象的对象。   * 也就是说新函数中的 this 引用被改变为参数提供的对象。   * 比如:   * <input type="text" id="aaa" value="aaa">   * <input type="text" id="bbb" value="bbb">   * .................   * <script>   * var aaa = document.getElementById("aaa");   * var bbb = document.getElementById("bbb");   * aaa.showValue = function() {alert(this.value);}   * aaa.showValue2 = aaa.showValue.bind(bbb);   * </script>   * 那么,调用aaa.showValue 将返回"aaa", 但调用aaa.showValue2 将返回"bbb"。   *   * apply 是ie5.5后才出现的新方法(Netscape好像很早就支持了)。   * 该方法更多的资料参考MSDN http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp   * 还有一个 call 方法,应用起来和 apply 类似。可以一起研究下。   */  Function.prototype.bind = function(object) {   var method = this;   return function() {   method.apply(object, arguments);   }  }  /**   * 和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象   * 注意这时候,用到了 Function.call。它与 Function.apply 的不同好像仅仅是对参数形式的定义。   * 如同 java 两个过载的方法。   */  Function.prototype.bindAsEventListener = function(object) {   var method = this;   return function(event) {   method.call(object, event || window.event);   }  }  /**   * 将整数形式RGB颜色值转换为HEX形式   */  Number.prototype.toColorPart = function() {   var digits = this.toString(16);   if (this < 16) return '0' + digits;   return digits;  }  /**   * 典型 Ruby 风格的函数,将参数中的方法逐个调用,返回第一个成功执行的方法的返回值   */  var Try = {   these: function() {   var returnValue;   for (var i = 0; i < arguments.length; i++) {   var lambda = arguments[i];   try {   returnValue = lambda();   break;   } catch (e) {}   }   return returnValue;   }  }  /*--------------------------------------------------------------------------*/  /**   * 一个设计精巧的定时执行器   * 首先由 Class.create() 创建一个 PeriodicalExecuter 类型,   * 然后用对象直接量的语法形式设置原型。   *   * 需要特别说明的是 rgisterCallback 方法,它调用上面定义的函数原型方法bind, 并传递自己为参数。   * 之所以这样做,是因为 setTimeout 默认总以 window 对象为当前对象,也就是说,如果 registerCallback 方法定义如下的话:   * registerCallback: function() {   * setTimeout(this.onTimerEvent, this.frequency * 1000);   * }   * 那么,this.onTimeoutEvent 方法执行失败,因为它无法访问 this.currentlyExecuting 属性。   * 而使用了bind以后,该方法才能正确的找到this,也就是PeriodicalExecuter的当前实例。   */  var PeriodicalExecuter = Class.create();  PeriodicalExecuter.prototype = {   initialize: function(callback, frequency) {   this.callback = callback;   this.frequency = frequency;   this.currentlyExecuting = false;   this.registerCallback();   },   registerCallback: function() {   setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);   },   onTimerEvent: function() {   if (!this.currentlyExecuting) {   try {   this.currentlyExecuting = true;   this.callback();   } finally {   this.currentlyExecuting = false;   }   }   this.registerCallback();   }  }  /*--------------------------------------------------------------------------*/  /**   * 这个函数就 Ruby 了。我觉得它的作用主要有两个   * 1. 大概是 document.getElementById(id) 的最简化调用。   * 比如:$("aaa") 将返回上 aaa 对象   * 2. 得到对象数组   * 比如: $("aaa","bbb") 返回一个包括id为"aaa"和"bbb"两个input控件对象的数组。   */  function $() {   var elements = new Array();   for (var i = 0; i < arguments.length; i++) {   var element = arguments[i];   if (typeof element == 'string')   element = document.getElementById(element);   if (arguments.length == 1)   return element;   elements.push(element);   }   return elements;  }  /**   * 定义 Ajax 对象, 静态方法 getTransport 方法返回一个 XMLHttp 对象   */  var Ajax = {   getTransport: function() {   return Try.these(   function() {return new ActiveXObject('Msxml2.XMLHTTP')},   function() {return new ActiveXObject('Microsoft.XMLHTTP')},   function() {return new XMLHttpRequest()}   ) || false;   },   emptyFunction: function() {}  }  /**   * 我以为此时的Ajax对象起到命名空间的作用。   * Ajax.Base 声明为一个基础对象类型   * 注意 Ajax.Base 并没有使用 Class.create() 的方式来创建,我想是因为作者并不希望 Ajax.Base 被库使用者实例化。   * 作者在其他对象类型的声明中,将会继承于它。   * 就好像 java 中的私有抽象类   */  Ajax.Base = function() {};  Ajax.Base.prototype = {   /**   * extend (见prototype.js中的定义) 的用法真是让人耳目一新   * options 首先设置默认属性,然后再 extend 参数对象,那么参数对象中也有同名的属性,那么就覆盖默认属性值。   * 想想如果我写这样的实现,应该类似如下:   setOptions: function(options) {   this.options.methed = options.methed? options.methed : 'post';   ..........   }   我想很多时候,java 限制了 js 的创意。   */   setOptions: function(options) {   this.options = {   method: 'post',   asynchronous: true,   parameters: ''   }.extend(options || {});   }  }  /**   * Ajax.Request 封装 XmlHttp   */  Ajax.Request = Class.create();  /**   * 定义四种事件(状态), 参考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp   */  Ajax.Request.Events =   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];  /**   *   */  Ajax.Request.prototype = (new Ajax.Base()).extend({   initialize: function(url, options) {   this.transport = Ajax.getTransport();   this.setOptions(options);   try {   if (this.options.method == 'get')   url += '?' + this.options.parameters + '&_=';   /**   * 此处好像强制使用了异步方式,而不是依照 this.options.asynchronous 的值   */   this.transport.open(this.options.method, url, true);   /**   * 这里提供了 XmlHttp 传输过程中每个步骤的回调函数   */   if (this.options.asynchronous) {   this.transport.onreadystatechange = this.onStateChange.bind(this);   setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);   }   this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest');   this.transport.setRequestHeader('X-Prototype-Version', Prototype.Version);   if (this.options.method == 'post') {   this.transport.setRequestHeader('Connection', 'close');   this.transport.setRequestHeader('Content-type',   'application/x-www-form-urlencoded');   }   this.transport.send(this.options.method == 'post' ?   this.options.parameters + '&_=' : null);   } catch (e) {   }   },   onStateChange: function() {   var readyState = this.transport.readyState;   /**   * 如果不是 Loading 状态,就调用回调函数   */   if (readyState != 1)   this.respondToReadyState(this.transport.readyState);   },   /**   * 回调函数定义在 this.options 属性中,比如:   var option = {   onLoaded : function(req) {...};   ......   }   new Ajax.Request(url, option);   */   respondToReadyState: function(readyState) {   var event = Ajax.Request.Events[readyState];   (this.options['on' + event] || Ajax.emptyFunction)(this.transport);   }  });  /**   * Ajax.Updater 用于绑定一个html元素与 XmlHttp调用的返回值。类似与 buffalo 的 bind。   * 如果 options 中有 insertion(from dom.js) 对象的话, insertion 能提供更多的插入控制。   */  Ajax.Updater = Class.create();  Ajax.Updater.prototype = (new Ajax.Base()).extend({   initialize: function(container, url, options) {   this.container = $(container);   this.setOptions(options);   if (this.options.asynchronous) {   this.onComplete = this.options.onComplete;   this.options.onComplete = this.updateContent.bind(this);   }   this.request = new Ajax.Request(url, this.options);   if (!this.options.asynchronous)   this.updateContent();   },   updateContent: function() {   if (this.options.insertion) {   new this.options.insertion(this.container,   this.request.transport.responseText);   } else {   this.container.innerHTML = this.request.transport.responseText;   }   if (this.onComplete) {   setTimeout((function() {this.onComplete(this.request)}).bind(this), 10);   }   }  });  /**   * 针对 页面元素对象 的工具类,提供一些简单静态方法   */  var Field = {   /**   * 清除参数引用对象的值   */   clear: function() {   for (var i = 0; i < arguments.length; i++)   $(arguments[i]).value = '';   },   /**   * 使参数引用对象获取焦点   */   focus: function(element) {   $(element).focus();   },   /**   * 判断参数引用对象值是否为空,如为空,返回false, 反之true   */   present: function() {   for (var i = 0; i < arguments.length; i++)   if ($(arguments[i]).value == '') return false;   return true;   },   /**   * 使选中参数引用对象   */   select: function(element) {   $(element).select();   },   /**   * 使参数引用对象处于可编辑状态   */   activate: function(element) {   $(element).focus();   $(element).select();   }  }  /*--------------------------------------------------------------------------*/  /**   * 表单工具类   */  var Form = {   /**   * 将表单元素序列化后的值组合成 QueryString 的形式   */   serialize: function(form) {   var elements = Form.getElements($(form));   var queryComponents = new Array();   for (var i = 0; i < elements.length; i++) {   var queryComponent = Form.Element.serialize(elements[i]);   if (queryComponent)   queryComponents.push(queryComponent);   }   return queryComponents.join('&');   },   /**   * 得到表单的所有元素对象   */   getElements: function(form) {   form = $(form);   var elements = new Array();   for (tagName in Form.Element.Serializers) {   var tagElements = form.getElementsByTagName(tagName);   for (var j = 0; j < tagElements.length; j++)   elements.push(tagElements[j]);   }   return elements;   },   /**   * 将指定表单的元素置于不可用状态   */   disable: function(form) {   var elements = Form.getElements(form);   for (var i = 0; i < elements.length; i++) {   var element = elements[i];   element.blur();   element.disable = 'true';   }   },   /**   * 使表单的第一个非 hidden 类型而且处于可用状态的元素获得焦点   */   focusFirstElement: function(form) {   form = $(form);   var elements = Form.getElements(form);   for (var i = 0; i < elements.length; i++) {   var element = elements[i];   if (element.type != 'hidden' && !element.disabled) {   Field.activate(element);   break;   }   }   },   /*   * 重置表单   */   reset: function(form) {   $(form).reset();   }  }  /**   * 表单元素工具类   */  Form.Element = {   /**   * 返回表单元素的值先序列化再进行 URL 编码后的值   */   serialize: function(element) {   element = $(element);   var method = element.tagName.toLowerCase();   var parameter = Form.Element.Serializers[method](element);   if (parameter)   return encodeURIComponent(parameter[0]) + '=' +   encodeURIComponent(parameter[1]);   },   /**   * 返回表单元素序列化后的值   */   getValue: function(element) {   element = $(element);   var method = element.tagName.toLowerCase();   var parameter = Form.Element.Serializers[method](element);   if (parameter)   return parameter[1];   }  }  /**   * prototype 的所谓序列化其实就是将表单的名字和值组合成一个数组   */  Form.Element.Serializers = {   input: function(element) {   switch (element.type.toLowerCase()) {   case 'hidden':   case 'password':   case 'text':   return Form.Element.Serializers.textarea(element);   case 'checkbox':   case 'radio':   return Form.Element.Serializers.inputSelector(element);   }   return false;   },   inputSelector: function(element) {   if (element.checked)   return [element.name, element.value];   },   textarea: function(element) {   return [element.name, element.value];   },   /**   * 看样子,也不支持多选框(select-multiple)   */   select: function(element) {   var index = element.selectedIndex;   var value = element.options[index].value || element.options[index].text;   return [element.name, (index >= 0) ? value : ''];   }  }  /*--------------------------------------------------------------------------*/  /**   * Form.Element.getValue 也许会经常用到,所以做了一个快捷引用   */  var $F = Form.Element.getValue;  /*--------------------------------------------------------------------------*/  /**   * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样   * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素,   * 当表单元素的值发生变化的时候,就执行回调函数   *   * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。   * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。   *   * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。   * Observer 应该可以胜任动态数据校验,或者多个关联下拉选项列表的连动等等   *   */  Abstract.TimedObserver = function() {}  /**   * 这个设计和 PeriodicalExecuter 一样,bind 方法是实现的核心   */  Abstract.TimedObserver.prototype = {   initialize: function(element, frequency, callback) {   this.frequency = frequency;   this.element = $(element);   this.callback = callback;   this.lastValue = this.getValue();   this.registerCallback();   },   registerCallback: function() {   setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);   },   onTimerEvent: function() {   var value = this.getValue();   if (this.lastValue != value) {   this.callback(this.element, value);   this.lastValue = value;   }   this.registerCallback();   }  }  /**   * Form.Element.Observer 和 Form.Observer 其实是一样的   * 注意 Form.Observer 并不是用来跟踪整个表单的,我想大概只是为了减少书写(这是Ruby的一个设计原则)   */  Form.Element.Observer = Class.create();  Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({   getValue: function() {   return Form.Element.getValue(this.element);   }  });  Form.Observer = Class.create();  Form.Observer.prototype = (new Abstract.TimedObserver()).extend({   getValue: function() {   return Form.serialize(this.element);   }  });  /**   * 根据 class attribute 的名字得到对象数组,支持 multiple class   *   */  document.getElementsByClassName = function(className) {   var children = document.getElementsByTagName('*') || document.all;   var elements = new Array();   for (var i = 0; i < children.length; i++) {   var child = children[i];   var classNames = child.className.split(' ');   for (var j = 0; j < classNames.length; j++) {   if (classNames[j] == className) {   elements.push(child);   break;   }   }   }   return elements;  }  /*--------------------------------------------------------------------------*/  /**   * Element 就象一个 java 的工具类,主要用来 隐藏/显示/销除 对象,以及获取对象的简单属性。   *   */  var Element = {   toggle: function() {   for (var i = 0; i < arguments.length; i++) {   var element = $(arguments[i]);   element.style.display =   (element.style.display == 'none' ? '' : 'none');   }   },   hide: function() {   for (var i = 0; i < arguments.length; i++) {   var element = $(arguments[i]);   element.style.display = 'none';   }   },   show: function() {   for (var i = 0; i < arguments.length; i++) {   var element = $(arguments[i]);   element.style.display = '';   }   },   remove: function(element) {   element = $(element);   element.parentNode.removeChild(element);   },   getHeight: function(element) {   element = $(element);   return element.offsetHeight;   }  }  /**   * 为 Element.toggle 做了一个符号连接,大概是兼容性的考虑   */  var Toggle = new Object();  Toggle.display = Element.toggle;  /*--------------------------------------------------------------------------*/  /**   * 动态插入内容的实现,MS的Jscript实现中对象有一个 insertAdjacentHTML 方法(http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/insertadjacenthtml.asp)   * 这里算是一个对象形式的封装。   */  Abstract.Insertion = function(adjacency) {   this.adjacency = adjacency;  }  Abstract.Insertion.prototype = {   initialize: function(element, content) {   this.element = $(element);   this.content = content;   if (this.adjacency && this.element.insertAdjacentHTML) {   this.element.insertAdjacentHTML(this.adjacency, this.content);   } else {   /**   * gecko 不支持 insertAdjacentHTML 方法,但可以用如下代码代替   */   this.range = this.element.ownerDocument.createRange();   /**   * 如果定义了 initializeRange 方法,则实行,这里相当与定义了一个抽象的 initializeRange 方法   */   if (this.initializeRange) this.initializeRange();   this.fragment = this.range.createContextualFragment(this.content);   /**   * insertContent 也是一个抽象方法,子类必须实现   */   this.insertContent();   }   }  }  /**   * prototype 加深了我的体会,就是写js 如何去遵循 Don't Repeat Yourself (DRY) 原则   * 上文中 Abstract.Insertion 算是一个抽象类,定义了名为 initializeRange 的一个抽象方法   * var Insertion = new Object() 建立一个命名空间   * Insertion.Before|Top|Bottom|After 就象是四个java中的四个静态内部类,而它们分别继承于Abstract.Insertion,并实现了initializeRange方法。   */  var Insertion = new Object();  Insertion.Before = Class.create();  Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({   initializeRange: function() {   this.range.setStartBefore(this.element);   },   /**   * 将内容插入到指定节点的前面, 与指定节点同级   */   insertContent: function() {   this.element.parentNode.insertBefore(this.fragment, this.element);   }  });  Insertion.Top = Class.create();  Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({   initializeRange: function() {   this.range.selectNodeContents(this.element);   this.range.collapse(true);   },   /**   * 将内容插入到指定节点的第一个子节点前,于是内容变为该节点的第一个子节点   */   insertContent: function() {   this.element.insertBefore(this.fragment, this.element.firstChild);   }  });  Insertion.Bottom = Class.create();  Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({   initializeRange: function() {   this.range.selectNodeContents(this.element);   this.range.collapse(this.element);   },   /**   * 将内容插入到指定节点的最后,于是内容变为该节点的最后一个子节点   */   insertContent: function() {   this.element.appendChild(this.fragment);   }  });  Insertion.After = Class.create();  Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({   initializeRange: function() {   this.range.setStartAfter(this.element);   },   /**   * 将内容插入到指定节点的后面, 与指定节点同级   */   insertContent: function() {   this.element.parentNode.insertBefore(this.fragment,   this.element.nextSibling);   }  });  /* 这是包含错误的原版本  if (!Function.prototype.apply) {  // Based on code from http://www.youngpup.net/  Function.prototype.apply = function(object, parameters) {  var parameterStrings = new Array();  if (!object) object = window;  if (!parameters) parameters = new Array();  for (var i = 0; i < parameters.length; i++)  parameterStrings[i] = 'x[' + i + ']'; //Error 1  object.__apply__ = this;  var result = eval('obj.__apply__(' + //Error 2  parameterStrings[i].join(', ') + ')');  object.__apply__ = null;  return result;  }  }  */  if (!Function.prototype.apply) {   Function.prototype.apply = function(object, parameters) {   var parameterStrings = new Array();   if (!object) object = window;   if (!parameters) parameters = new Array();   for (var i = 0; i < parameters.length; i++)   parameterStrings[i] = 'parameters[' + i + ']';   object.__apply__ = this;   var result = eval('object.__apply__(' + parameterStrings.join(', ') + ')');   object.__apply__ = null;   return result;   }  }  Effect 的一个子类

Effect.Blink = Class.create();  Effect.Blink.prototype = {   initialize: function(element, frequency) {   this.element = $(element);   this.frequency = frequency?frequency:1000;   this.element.effect_blink = this;   this.blink();   },   blink: function() {   if (this.timer) clearTimeout(this.timer);   try {   this.element.style.visibility = this.element.style.visibility == 'hidden'?'visible':'hidden';   } catch (e) {}   this.timer = setTimeout(this.blink.bind(this), this.frequency);   }  };

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值