zoukankan      html  css  js  c++  java
  • prototype.js源码解读(一)

    希望对大家重新认识 Javascript 有所帮助。

    代码:

    /**
    * 定义一个全局对象, 属性 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...jsmthApply.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/a...adystate_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';
    }
    },

    from:http://forum.j2eemx.com/ajax-ajax-discussions/1026-prototype-javascript.html

  • 相关阅读:
    FJNU 1151 Fat Brother And Geometry(胖哥与几何)
    FJNU 1157 Fat Brother’s ruozhi magic(胖哥的弱智术)
    FJNU 1159 Fat Brother’s new way(胖哥的新姿势)
    HDU 3549 Flow Problem(最大流)
    HDU 1005 Number Sequence(数列)
    Tickets(基础DP)
    免费馅饼(基础DP)
    Super Jumping! Jumping! Jumping!(基础DP)
    Ignatius and the Princess IV(基础DP)
    Keywords Search(AC自动机)
  • 原文地址:https://www.cnblogs.com/backuper/p/1673610.html
Copyright © 2011-2022 走看看