Home  >  Article  >  Web Front-end  >  Ajax::prototype source code interpretation_javascript skills

Ajax::prototype source code interpretation_javascript skills

WBOY
WBOYOriginal
2016-05-16 19:20:18867browse
AJAX Journey (1): Entering the javascript palace from prototype_1.3.1 - a preliminary exploration of the class
I decided to give it the title ajax, after all, many people will use this keyword to search. Although I think this is just a hyped concept, I have to admit that calling ajax is much more convenient. So I won’t explain the meaning of ajax in detail.
The reason for writing this tutorial is very simple: after a period of learning ajax, I have some experience, and I am more and more aware of the power of ajax technology, so I decided to record it, and by the way, I also organized my own ideas. For the follow-up of this tutorial, please pay attention to http://www.x2design.net
In the past few years, in the eyes of ordinary people, JavaScript’s functions were still very narrow. All it could do was either some simple form validation or many Flashy web special effects. With the emergence of flash, everyone is no longer as keen on js special effects as before. It seems that js can do even less. But at this time, the concept of ajax emerged. Ajax applications, typically represented by gmail, attracted a lot of attention. Ajax suddenly became a very popular technology. When javascript, xml, and the dom model are combined, it can The things it does are often incredible, and some of its functions are even comparable to those of desktop programs.
Okay, no more nonsense, let’s start with a javascript development framework prototype_1.3.1 (hereinafter referred to as prototype). I originally wanted to introduce the advanced application of JavaScript first, but I was afraid that the level was not enough and the explanation was unorganized, so I combined it with prototype and mentioned the syntax of JS by the way.
The following are the first two pieces of code in the framework:
var Prototype = {
Version: '1.3.1',
emptyFunction: function() {}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
First, Let’s look at the difference between the following two syntaxes:
var o={};
var f=function(){};

The latter one is easy to understand, it is equivalent to function f (){};Define a function f. But the first one is not common: this is actually creating an object, and you can specify the members of the object in {}. For example, the Prototype above is an object with two members, the first is the version number, and the second is An empty method (function). This function of directly creating objects without defining a class may only be possible with js. The latter syntax actually has another function, which is to define a class f. If you use this in the function body, then the variables after this are members of the class.
Not only can this define class members, there is also a syntax:
function c(){
member1:value,
member2:function(){}
}

This is equivalent to:
function c(){
this.member1=value;
this.member2=function(){};
}

It should be noted that, When using the former method, you cannot add a comma at the end of the last member. I think this syntax should be related to the array.
In js, there is no difference between functions and classes. Both can be new. The function of new is to execute all the statements in the function body and then return an object. If there is this in the function, then the variable after this will be used as an object member; if not, then the function of new is just to return an empty object without any members. So when you use typeof to check the type of a so-called class, function will still be returned. There is basically no concept of type in js. All variables are declared using var, even for functions. A function is actually just a variable.
Many people may not understand that functions are variables. But try the following:
function fTest(){
var a=1;
alert(a);
}
alert(fTest);

You will find that what is displayed is the function body of the fTest function, so we can think that the so-called function is just a code string that the js engine can parse. The function name variable stores only this string. To be more precise, the function name is a pointer variable, which stores the location of the code string in memory. In this way, it is not difficult to understand that passing a function as a parameter can be returned as a value. This is a technology that will be used extensively in the future. Because classes are also functions, if you understand functions, you also understand classes.
Although there is no difference between functions and classes in js, the concept of class can facilitate our programming, so prototype creatively created a global object Class:
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}

Class is a global object, Its only method is create, which returns a function. The mechanism of function as a return value has been discussed before and will not be discussed here.The returned function includes a statement:
this.initialize.apply(this, arguments);

As mentioned before, when a new function is created, the code in the function will be executed and the object will finally be returned. So when a function is created using Class.create() and the returned function is new, this statement will be executed first. As you can see later, this is actually to call the constructor of the class.
That’s it, Class has become the type creation model for the entire prototype, and can well distinguish classes and functions in code. Class.create() just returns an empty class, and it will default to this class having an initialize method, so to use this class, you need at least one constructor, which requires the use of class inheritance. A class is just a function, so how does the function inherit? It seems incredible that javascript can do this, and prototype makes the implementation more elegant. As for how it is done, we will break it down next time.
AJAX Journey (2): In-depth study of classes in JavaScript - implementation and inheritance
Last time we talked about the definition of a class, prototype formally distinguishes functions and classes through a global object Class. Since it is a class, there are abstract classes, concrete classes, and inheritance of classes. At the same time, members of a class can have instance members and static members. Let’s take a look at how the prototype does this.
First look at the following code in the prototype:

var Abstract = new Object();
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
Object.prototype.extend = function(object) {
return Object.extend.apply (this, [this, object]);
}
The first one declares an object Abstract. Object is actually a function. It does not have any members, so it is an empty class, so Abstract does not have any members. . Let’s not talk about this for now. We will see later that this is the basis of abstract classes. First explain the following syntax:
function.member=function(){}
In this case, function has generally been defined. The function of this statement is to add a static member member to function. The content of member is after the equal sign. For example, the second code above, Object.extend=..., adds a static method extend to the Object class. ok, we know how to define static members for a class, then you must want to know how to define instance members. It is very simple, add prototype between the class name and the member name:

function.prototype.member= function(){}

prototype can not only be used like this, but also:
function.prototype={
member1:function(){……},
member2:"abc",
member3:function(){……}
}
This is how the definition of instance members is implemented. But what does prototype mean? In the first article, I said that it is directly enclosed in {} to represent an object. For example, Prototype and Class are global objects defined in this way. Looking at the following usage, prototype is followed by a {} structure. Is it also an object? Yes, that’s right, prototype is actually an object! In JavaScript, we can add members to an object arbitrarily, using the following syntax:
object.member=function(){...};
As long as it is defined in this way, an object can immediately have members. This method! JavaScript is so magical!
Okay, we now know that prototype is an object, and function is a function or class, then we can think of prototype as a static member retained internally by any class (function). Its function is to store all member pointers of this class, but these members are only prototypes and have not been initialized, which is also in line with the original meaning of prototype. You can extend members at any time through the prototype object. When a new class is created, the members of the prototype are initialized and then assigned to the instantiated object.
The third piece of code above, Object.prototype.extend=..., adds an instance method extend to Object. In the instance method, you can reference this pointer, which points to the object itself instantiated by this class. Of course, this object has a member extend.
Before continuing, let’s understand these two statements:
for(var p in object){}
method.apply(object,arguments);
The first sentence: List all the arguments of a variable Members, if it is a function, then it is all static members; if it is an object, it is all instance members, and the type of p is a string. Indicates the name of the member. Not only can you use variabel.member to reference a member, you can also use variabel["member"]. In turn, the same goes for assignment. This brings great convenience to enumerating the members of a variable.
The second statement: Apply method to object for execution, and the parameter is the array of arguments. Note: method is not a member of object. However, we can think that the execution of this statement means: object.method(arguments).This is a very important method that will be used frequently later, and you will gradually become familiar with it.
Let’s continue with extend. It is a very important method. You can see that it is both a static member and an instance member of the class Object. So what does it do? Let's take a look: it receives two parameters, destination and source. If destination and source are both classes, then its function is to copy all static members of class source to class destination. If destination and source are both objects, then All instance members are copied over. At this time, if there is already a member with the same name in destination, this member will be overwritten. That is to say, let destination have all members of source, and the function returns this destination. Let’s look at extend as an instance member of Object:
Object.prototype.extend = function(object) {
return Object.extend.apply(this, [this, object]);
}
Start I'm a little dizzy, but don't worry, you can still understand it. The apply syntax has just been explained. Its caller is a method, and Object.extend is a static method. It is applied to this, which is Object. Example, assuming it is obj, the following square brackets are an array, including two members, this and object. This array is actually the arguments parameter of the Object static member extend. Then this statement is equivalent to executing
obj.extend(this, object);
this is not explained, it represents itself. What is object? Parameters, well, they are parameters passed by the instance method extend, don't be confused. What about extend? obj does not define extend instance members, but through apply, it can use the static member extend of Object. Let’s look at the function body of extend:
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}
Because obj is an object, object is also an object, that is, destination and source are both is an object, so the function of the function is to make obj have all the members of object. And will return obj. It sounds a bit awkward, but the logic is very simple: let obj "inherit" from object! Very good, we have seen inheritance, but you will definitely ask, object inheritance. This is the first time I heard that when we talk about inheritance, we are talking about class inheritance. That's right, we haven't seen real class inheritance yet, but we are close at hand: doesn't a class just have a prototype, and prototype is an object!
Okay, thinking about this, the inheritance syntax of a class seems to be very simple:
b.prototype.extend(a.prototype);
Let b inherit a.
But the truth is not that simple: prototype stores the method prototype pointer, and the extend method is not initialized and cannot be used! To use extend, you must instantiate an object. Let’s take a look at how prototype is done:
b.prototype=(new a()).extend(b.prototype);
A very clever method! It fully explains that the function is actually a variable. First instantiate the a object, then call extend based on it, overwrite all members of b.prototype to the a object, and then assign the a object to b.prototype. Completed the work of b inheriting from a. In actual use, the general usage is:
b.prototype=(new a()).extend({});
Because b inherits from a, usually b is previously undefined. class, so class members can actually be defined in the following {}. Of course, you can also define it first and then inherit it, but it is different from the traditional concept.

AJAX Journey (3): Event Design Patterns in JavaScript
Today I will temporarily leave prototype 1.3.1 aside and share my experience in javscript event design. The technical basis for its implementation lies in the nature of the function, which is described in detail in the previous two articles (for more information, please pay attention to: http://www.x2design.net).
The built-in objects in JavaScript have event functions. For example, button has onclick event and input has onchange event. So how do we implement events in our custom classes? Very simple:
var myClass=Class.create();
myClass.prototype={
show:function(){
//statement
onshow();
},
onshow:function(){}
}
This code actually implements the onshow event, which is triggered when the myClass instance shows. You can bind a function to onshow to use the event function. In JavaScript, the built-in object event usage method is the same, and its internal implementation should also be based on this pattern. However, such an implementation has two outstanding problems:
1. Only one callback function can be bound. If you want to implement multiple bindings, you must write a lot of code yourself to encapsulate the function to be called back into a function.
2. Parameters cannot be passed. Because onshow can only be assigned to the function name, that is, the function body itself, and parameters cannot be passed in. In order to pass parameters, I once wrote an article: "Using shell packaging method to pass parameters to JavaScript triggers". It can be seen that a lot of writing is also required. code.
So, how to solve these problems? We will ignore the use of events of JavaScript's built-in objects for the time being, and let's consider how to avoid the above two problems in the classes we implement. Before implementing it, let's consider the following issue, which may help to understand the significance of implementing this function:
My page needs some initialization using javascript, but the initialization must be done after the page is loaded. Usually we put the code at the bottom of the html file. But at this time, before the page is loaded, the button click on the page needs to call a method that must be initialized. If no judgment is made, script errors will easily occur. Because it has not been initialized yet, a simple idea is to use a bool variable loaded to judge. The initial value is false, and after the initialization is completed, it is true. Then when the button is clicked and false is encountered, it simply returns. Although this implementation is simple, it may cause users to find that the click is invalid without knowing why. Therefore, the perfect approach should be to capture this method, bind it to the page loading completion event, and automatically call it when the page loading is completed.
Okay, now look at the implementation code of the event design pattern:
var myClass=Class.create();
myClass.prototype={
initialize:function(){
this.initEvent= new Object();
},
init:function(){
//Initialize the statement to be executed

//The following is to call the bound callback function
for( var p in this.initEvent){
//extend is a built-in method and cannot be used as a callback keyword
if(p=="extend")continue;
this.initEvent[p].apply(_object ,[]);
}
},
attachOnInit:function(_key,_object,_method,_arguments){
this.initEvent[_key]=createFunction(_object,_method,_arguments);
},
}
function createFunction(_object,_method,_arguments){
return function(){
_method.apply(_object,_arguments);
}
}
This code implements a class myClass, which has an init method and triggers the oninit event. If you want to bind an event during use, you can call the attachOnInit method. The meanings of the parameters are: _key, the unique identifier of the callback parameter function. If Repeat, the latter overrides the former; _object is the object of the callback function. If it is a function directly in the script, you can pass this pointer into it, that is, the document object; _method, the function to be called back. Note that this is a function name, not a string ;_arguments, the parameter array of the callback function. There is also a function called createFunction, which is used to wrap a function so that it has built-in parameters. This is a common implementation of the article on the shell packaging method. If you have read the first two articles in the ajax journey series, it should be easy to understand the above code. If you have any questions, please feel free to comment.
Usage example:
function myFunc(s){
alert(s);
}
var myObj=new myClass();
myClass.attach("key1",this ,myFunc,[123]);
myClass.init();

This binds the myFunc function to the init function of myObj, and dialog box 123 will pop up after execution.

Ajax::prototype Source Code Interpretation of prototype.js 1 [Reprinted]
/**
* Define a global object, the attribute Version will be replaced with the current version number when released
*/
var Prototype = {
Version: '@@VERSION@@'
}

/**
* Create a type, note that its attribute create is a method and returns a constructor.
* Generally used as follows:
* var X = Class.create(); Returns a type, similar to a Class instance in java.
* To use the X type, you need to continue to use new X() to obtain an instance, just like java's Class.newInstance() method.
*
* The returned constructor will execute the method named initialize, which is the name of the constructor method of the Ruby object.
* The initialize method has not been defined at this time. When creating a new type in subsequent code, a corresponding method with the same name will be created.
*
* If you must understand it from Java. You can understand it as using Class.create() to create a class that inherits the java.lang.Class class. Of course java does not allow this, because the Class class is final
*
*/
var Class = {
create: function() {
return function() {
this.initialize.apply (this, arguments);
} }
}
}

/**
* Create an object and think from the variable name. The original intention may be to define an abstract class. Creating new objects extends it.
* But judging from the application of the subsequent code, Abstract is more about keeping the namespace clear.
* In other words, we can add a new object definition to the Abstract object instance.
*
* To understand from java, it is to dynamically create an internal class for an object.
*/
var Abstract = new Object();

/**
* Get all properties and methods of the parameter object, a bit like multiple inheritance. But this inheritance is obtained dynamically.
* For example:
* var a = new ObjectA(), b = new ObjectB();
* var c = a.extend(b);
* At this time, object c also owns a and b object properties and methods. But unlike multiple inheritance, c instanceof ObjectB will return false.
*/
Object.prototype.extend = function(object) {
for (property in object) {
this[property] = object[property];
}
return this;
}

/**
* This method is very interesting. It encapsulates a javascript function object and returns a new function object. The body of the new function object is the same as the original object, but the bind() method parameter will be used as the object of the current object.
* That is to say, the this reference in the new function is changed to the object provided by the parameter.
* For example:
*
* > * Then, calling aaa.showValue will return "aaa", but calling aaa.showValue2 will return "bbb". *
* apply is a new method that only appeared after ie5.5 (Netscape seems to have supported it very early).
* For more information about this method, please refer to MSDN http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
* There is also a call method, which is similar to apply . You can study it together.
*/
Function. prototype.bind = function(object) {
var method = this;
return function() {
method.apply(object, arguments);
}
}

/**
* Same as bind, but this method is generally used for event processing of html control objects. So the event object needs to be passed
* Note that Function.call is used at this time. The difference between it and Function.apply seems to be just the definition of parameter form.
* Just like java's two overloaded methods. 
 */ 
Function.prototype.bindAsEventListener = function(object) { 
  var method = this; 
  return function(event) { 
    method.call(object, event || window.event); 
  } 


/**
* Convert RGB color values ​​in integer form to HEX form
*/ 
Number.prototype.toColorPart = function() { 
  var digits = this.toString(16); 
  if (this   return digits; 


/**
* Typical Ruby style function, calls the methods in the parameters one by one, and returns the return value of the first successfully executed method
*/ 
var Try = { 
  these: function() { 
    var returnValue; 

    for (var i = 0; i       var lambda = arguments[i]; 
      try { 
        returnValue = lambda(); 
        break; 
      } catch (e) {} 
    } 

    return returnValue; 
  } 


/*--------------------------------------------------------------------------*/ 

/**
* A well-designed scheduled executor
* First create a PeriodicalExecuter type by Class.create(),
* Then set the prototype using the syntax of object literals.
*
* What needs special explanation is the rgisterCallback method, which calls the function prototype method bind defined above and passes itself as a parameter.
* The reason for this is because setTimeout always takes the window object as the current object by default, that is, if the registerCallback method is defined as follows:
* registerCallback: function() {
* setTimeout(this. onTimerEvent, this.frequency * 1000);
* } }
* Then, this.onTimeoutEvent method fails to execute because it cannot access this.currentlyExecuting property.
* After using bind, this method can correctly find this, which is the current instance of 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) 的最简化调用。
* For example: $("aaa") will return the aaa object
* 2. Get the object array
* For example: $("aaa","bbb") will return an object including the id "aaa" and "bbb" are two arrays of input control objects.
*/
function $() {
var elements = new Array();

for (var i = 0; i var element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);

if (arguments.length == 1)
return elements;

elements.push(element);
}

return elements; .js 二[Reprint]
/**
* Define Ajax object, static method getTransport method returns an XMLHttp object
*/
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() {}
}

/**
* I thought the Ajax object at this time acted as a namespace.
* Ajax.Base is declared as a basic object type
* Note that Ajax.Base is not created using Class.create(). I think it is because the author does not want Ajax.Base to be instanced by the library user change.
* The author's declarations of other object types will inherit from it.
* Just like a private abstract class in java
*/
Ajax.Base = function() {};
Ajax.Base.prototype = {
/**
* The usage of extend (see definition in prototype.js) is really refreshing
* options first set the default attributes, and then extend the parameter object , then there is also an attribute with the same name in the parameter object, then the default attribute value will be overwritten.
* Think about it if I write such an implementation, it should be similar to the following:
setOptions: function(options) {
this.options.methed = options.methed? options.methed : 'post';
         …..                                                                                                                                                                         .  >
    } .extend(옵션 || {}); 
  } 



/**
* Ajax.Request는 XmlHttp를 캡슐화합니다
*/ 
Ajax.Request = Class.create(); 

/**
* 4가지 이벤트(상태)를 정의합니다. http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp를 참조하세요.
*/ 
Ajax.Request.Events = 
  ['초기화되지 않음', '로딩', '로드됨', '대화형', '완료']; 

/** 
 * 
 */ 
Ajax.Request.prototype = (new Ajax.Base()).extend({ 
  초기화: function(url, options) { 
this.transport = Ajax.getTransport(); 
    this.setOptions(options); 

    시도해 보세요 { 
      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'); 
      .transport.setRequestHeader('X-Prototype-Version', Prototype.Version); 

      if (this.options.method == 'post') { 
        this.transport.setRequestHeader('Connection' , '닫기'); 
        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; 
   /**
* 로딩 상태가 아닌 경우 콜백 함수 호출
*/ 
    if (readyState != 1) 
      this.respondToReadyState(this.transport.readyState); 
  }, 

  /** 
   * 回调函数정义재 this.options 属性中,比如: 
      var 옵션 = { 
         onLoaded : 함수(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 is used to bind an html Element and the return value of the XmlHttp call. Similar to buffalo's bind.
* If there is an insertion(from dom.js) object in options, insertion can provide more insertion control. 
 */ 
Ajax.Updater = Class.create(); 
Ajax.Updater.prototype = (new Ajax.Base()).extend({ 
  초기화: 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, 
        이 .request.transport.responseText); 
    } else { 
      this.container.innerHTML = this.request.transport.responseText; 
    } 

    if (this.onComplete) { 
      setTimeout((function() {this.onComplete(this.request)}).bind(this), 10); 
    } 
  } 
});
Ajax::prototype 源码解读 之 prototype.js 三[转载] 
/**
* 페이지 요소 개체의 도구 클래스에 대한 몇 가지 간단한 정적 메서드를 제공합니다.
*/ 
var Field = { 
  /**
* 매개변수 참조 객체의 값 지우기
*/ 
  지우기 : function() { 
    for (var i = 0; i       $(arguments[i]).value = ''; 
  }, 

  /**
* 매개변수 참조 객체에 포커스를 부여합니다
*/ 
  focus: function(element) { 
    $(element).focus(); 
  }, 

  /**
* 매개변수 참조 객체 값이 비어 있는지 확인합니다. 비어 있으면 false를 반환하고, 그렇지 않으면 true를 반환합니다.
*/ 
  현재: function() { 
    for (var i = 0; i       if ($(arguments[i]).value == '') false를 반환합니다. 
    true를 반환합니다. 
  }, 

  /**
* 선택한 매개변수 참조 객체를 만듭니다
*/ 
  선택: function(element) { 
    $(element).select(); 
  }, 

  /**
* 매개변수 참조 개체를 편집 가능하게 만듭니다
*/ 
  활성화: function(element) { 
    $(element).focus(); 
    $(element).select(); 
  } 


/*----------------------------------- -------------------------*/ 

/* *
* 양식 도구 클래스
*/ 
var Form = { 
  /**
* 양식 요소의 직렬화된 값을 QueryString 형식으로 결합합니다.
*/ 
  직렬화: function(form) { 
    var elements = Form.getElements($(form)); 
    var queryComponents = new Array(); 

    for (var i = 0; i       var queryComponent = Form.Element.serialize(elements[i]); 
      if (queryComponent) 
        queryComponents.push(queryComponent); 
    } 

    return queryComponents.join('&'); 
  }, 

  /**
* Get all element objects of the form
*/ 
  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         elements.push(tagElements[j]); 
    } 
    return elements; 
  }, 

  /**
* Put the elements of the specified form into an unavailable state
*/ 
  disable: function(form) { 
    var elements = Form.getElements(form); 
    for (var i = 0; i       var element = elements[i]; 
      element.blur(); 
      element.disable = 'true'; 
    } 
  }, 

  /**
* Make the first non-hidden element of the form that is in a usable state gain focus
*/ 
  focusFirstElement: function(form) { 
    form = $(form); 
    var elements = Form.getElements(form); 
    for (var i = 0; i       var element = elements[i]; 
      if (element.type != 'hidden' && !element.disabled) { 
        Field.activate(element); 
        break; 
      } 
    } 
  }, 

  /* 
   * 重置表单 
   */ 
  reset: function(form) { 
    $(form).reset(); 
  } 


/**
* Form element tool class
*/ 
Form.Element = { 
  /**
* Return the value of the form element first serialized and then URL encoded
*/ 
  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]);                    
  }, 

  /**
* Returns the serialized value of the form element
*/ 
  getValue: function(element) { 
    element = $(element); 
    var method = element.tagName.toLowerCase(); 
    var parameter = Form.Element.Serializers[method](element); 

    if (parameter) 
      return parameter[1]; 
  } 


/**
* The so-called serialization of prototype is actually combining the name and value of the form into an array
*/ 
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]; 
  }, 

  /**
* It seems that multiple selection boxes (select-multiple) are not supported either
*/ 
  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 may be used frequently, so I made a shortcut reference
*/ 
var $F = Form.Element.getValue; 

/*--------------------------------------------------------------------------*/ 

/** 
 * Abstract.TimedObserver 也没有用 Class.create() 来创建,和Ajax.Base 意图应该一样 
 * Abstract.TimedObserver 顾名思义,是套用Observer设计模式来跟踪指定表单元素, 
 * 当表单元素的值发生变化的时候,就执行回调函数 
 * 
 * 我想 Observer 与注册onchange事件相似,不同点在于 onchange 事件是在元素失去焦点的时候才激发。 
 * 同样的与 onpropertychange 事件也相似,不过它只关注表单元素的值的变化,而且提供timeout的控制。 
 * 
 * 除此之外,Observer 的好处大概就在与更面向对象,另外可以动态的更换回调函数,这就比注册事件要灵活一些。
* Observer should be capable of dynamic data verification, or linkage of multiple associated drop-down option lists, etc.
*
*/
Abstract.TimedObserver = function() {}

/**
* This design is the same as PeriodicalExecuter, the bind method is the core of the implementation
*/
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 and Form.Observer are actually the same
* Note that Form.Observer is not used to track the entire form. I think it is just to reduce writing (this is a design principle of 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);
}
});
Ajax::prototype Source Code Interpretation of prototype.js 4 [Reprinted]
/**
* Get the object array based on the name of the class attribute, supporting multiple classes
*
*/
document.getElementsByClassName = function(className) {
var children = document.getElementsByTagName('*') || document.all;
var elements = new Array();

for ( var i = 0; i var child = children[i];
var classNames = child.className.split(' ');
for (var j = 0 ; j if (classNames[j] == className) {
elements.push(child);
break;
}
}
}

return elements;
}

/*---------------------------- --------------------------------------------------*/

/**
* Element is like a java tool class, mainly used to hide/show/destroy objects and obtain simple attributes of objects. 
 * 
 */ 
var Element = { 
  toggle: function() { 
    for (var i = 0; i       var element = $(arguments[i]); 
      element.style.display = 
        (element.style.display == 'none' ? '' : 'none'); 
    } 
  }, 

  hide: function() { 
    for (var i = 0; i       var element = $(arguments[i]); 
      element.style.display = 'none'; 
    } 
  }, 

  show: function() { 
    for (var i = 0; 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; 
  } 


/**
* Made a symbolic link for Element.toggle, probably for compatibility reasons
*/ 
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 does not support the insertAdjacentHTML method, but you can use the following code instead
*/ 
      this.range = this.element.ownerDocument.createRange(); 
     /**
* If the initializeRange method is defined, it will be executed. This is equivalent to defining an abstract initializeRange method
*/ 
      if (this.initializeRange) this.initializeRange(); 
      this.fragment = this.range.createContextualFragment(this.content); 

     /**
* insertContent is also an abstract method, subclasses must implement it
*/ 
      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); 
  }, 

  /**
* Insert content in front of the specified node, at the same level as the specified node
*/ 
  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); 
  }, 

  /**
* Insert the content before the first child node of the specified node, so the content becomes the first child node of the node
*/ 
  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); 
  }, 

  /**
* Insert the content to the end of the specified node, so the content becomes the last child node of the node
*/ 
  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); 
  }, 

  /**
* Insert content after the specified node, at the same level as the specified node
*/ 
  insertContent: function() { 
    this.element.parentNode.insertBefore(this.fragment, 
      this.element.nextSibling); 
  } 
});

 

Ajax::prototype 源码解读 之 prototype.js 五[转载] 
prototype 还有两个源码文件 effects.js compat.js 就不贴出来了。两者并不常用,effects.js 看example 做花哨的效果还不错,不过代码中没有太多新鲜的东西。 

需要指出的就是 
compat.js 中 Funcation.prototype.apply 的实现有两个错误(应该是拼写错误), 我分别贴出来,大家比较一下就清楚了。
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn