JavaScript inheritance Detailed (vi) _JAVASCRIPT skills

Source: Internet
Author: User
Tags anonymous extend function definition inheritance iterable wrapper

In this chapter, we will analyze the implementation of JavaScript inheritance in Prototypejs.
Prototypejs is the earliest JavaScript class library, can be said to be the originator of JavaScript class library. The first JavaScript class library I contacted a few years ago is this one, so Prototypejs has a broad mass base.

However, the implementation of the Prototypejs in the previous year is quite simple, the source code on a few lines, we look at.

Implementation of inheritance in early Prototypejs
Source:

var Class = {
      //Class.create returns only another function that will invoke the prototype method initialize Create:function () {return function
        () {
          this.initialize.apply (this, arguments);
        }
      }
    ;
    
    Object is extended
    object.extend = function (destination, source) {for
      (var property in source) {
        destination[ Property] = Source[property];
      }
      return destination;
    };

Call Mode:

var person = class.create ();
    Person.prototype = {
      initialize:function (name) {
        this.name = name;
      },
      getname:function (prefix) { return
        prefix + this.name;
      }
    ;

    var Employee = Class.create ();
    Employee.prototype = object.extend (new person (), {
      initialize:function (name, EmployeeID) {
        this.name = name;
        This.employeeid = EmployeeID
      },
      getname:function () {return
        "Employee name:" + this.name;
      }
    });


    var Zhang = new Employee ("Zhangsan", "1234");
    Console.log (Zhang.getname ());  "Employee Name:zhangsan"

It's a very primitive feeling, right? There is no way to invoke the parent class function in the subclass function.

Implementation of inheritance after Prototypejs 1.6
First look down the way:

Create a new class var person
    = Class.create through Class.create ({
      //Initialize is constructor
      initialize:function (name) {
        this.name = name;
      ,
      getname:function (prefix) {return
        prefix + this.name;
      }
    });
    
    Class.create's first argument is the parent class
    var Employee = class.create (person, {
      //) referencing the parent class function by setting the first argument of the subclass function to $super
      //More creative, but internal implementations should be more complex, at least with a closure to set the context of $super this point to the current object
      initialize:function ($super, name, EmployeeID) {
        $super (name);
        This.employeeid = EmployeeID
      },
      getname:function ($super) {return
        $super ("Employee name:");
      }
    });


    var Zhang = new Employee ("Zhangsan", "1234");
    Console.log (Zhang.getname ());  "Employee Name:zhangsan"

Here we will prototypejs 1.6.0.3 inheritance implementation alone, those who do not want to refer to the entire prototype library and just want to use the prototype-style inheritance of friends, you can directly copy the following code to save as JS file on the line.

var Prototype = {emptyfunction:function () {}};
        var Class = {create:function () {var parent = null, properties = $A (arguments);

        if (Object.isfunction (Properties[0])) parent = Properties.shift ();
        Function Klass () {this.initialize.apply (this, arguments);
        } object.extend (Klass, Class.methods);
        Klass.superclass = parent;

        Klass.subclasses = [];
          if (parent) {var subclass = function () {};
          Subclass.prototype = Parent.prototype;
          Klass.prototype = new Subclass;
        Parent.subclasses.push (Klass);

        for (var i = 0; i < properties.length i++) Klass.addmethods (properties[i));

        if (!klass.prototype.initialize) klass.prototype.initialize = prototype.emptyfunction;

        Klass.prototype.constructor = Klass;
      Return Klass;

    }
    }; Class.methods = {addmethods:function (source) {var ancestor = This.superclass && this.superclass.prototype;

        var properties = Object.keys (source); if (!

        Object.keys ({tostring:true}). Length) Properties.push ("ToString", "valueof"); for (var i = 0, length = properties.length i < length; i++) {var property = Properties[i], value = source[p
          Roperty]; if (ancestor && object.isfunction (value) && value.argumentnames (). A () = "$super") {var m
            Ethod = value;
            Value = (function (m) {return function () {return ancestor[m].apply (this, arguments)};

            ) (property). Wrap (method);
            Value.valueof = Method.valueOf.bind (method);
          Value.tostring = Method.toString.bind (method);
        } This.prototype[property] = value;
      return to this;

    }
    }; Object.extend = function (destination, source) {for (var property in source) destination[pRoperty] = Source[property];
    return destination;

    };
      function $A (iterable) {if (!iterable) return [];
      if (Iterable.toarray) return Iterable.toarray (); var length = Iterable.length | |
      0, results = new Array (length);
      while (length--) results[length] = Iterable[length];
    return results;
        } object.extend (Object, {keys:function (object) {var keys = [];
        For (var property in object) Keys.push;
      return keys;
      }, Isfunction:function (object) {return typeof object = = "function";
      }, Isundefined:function (object) {return typeof object = = "undefined";

    }
    }); Object.extend (Function.prototype, {argumentnames:function () {var names = this.tostring (). Match (/^[\s\
        unction[^ (]*\ ([^\)]*) \) [1].replace (/\s+/g, ']. Split (', '); return names.length = = 1 &&!names[0]?
      []: Names;
    }, Bind:function () {    if (Arguments.length < 2 && object.isundefined (arguments[0)) return to this;
        var __method = this, args = $A (arguments), object = Args.shift ();
        return function () {return __method.apply (object, Args.concat ($A (arguments)));
        }, Wrap:function (wrapper) {var __method = this;
        return function () {return wrapper.apply (this, [__method.bind (This)].concat ($A (arguments)));

    }
      }
    });
      Object.extend (Array.prototype, {first:function () {return this[0]; }
    });

First, we need to explain the definition of some of the methods in the Prototypejs.

Argumentnames: Gets an array of arguments for the function

function init ($super, name, EmployeeID) {
        console.log (Init.argumentnames (). Join (","));//"$super, Name, EmployeeID "
        }

Bind: The context of the binding function this is to a new object (typically the first parameter of a function)

var name = "Window";
        var p = {
          name: "Lisi",
          getname:function () {return
            this.name;
          }
        };

        Console.log (P.getname ());  "Lisi"
        console.log (p.getname.bind (window));//"Window"

Wrap: The first parameter that takes the current calling function as the wrapper wrapper function

var name = "Window";
        var p = {
          name: "Lisi",
          getname:function () {return
            this.name;
          }
        };

        function Wrapper (ORIGINALFN) {return
          "Hello:" + ORIGINALFN ();
        }

        Console.log (P.getname ());  "Lisi"
        console.log (P.getname.bind (window) ());//"Window"
        console.log (P.getname.wrap (wrapper) ());//" Hello:window "
        Console.log (P.getname.wrap (wrapper). Bind (P) ());//" Hello:lisi "

It's a bit of a detour, right. Note here that the wrap and bind calls return all functions, and it is easy to see the essence of this principle.

After a certain understanding of these functions, we will then resolve the core content of Prototypejs inheritance.
Here are two important definitions, one is class.extend, the other is Class.Methods.addMethods.

var class = {Create:function () {///If the first argument is a function, then as the parent var parent = null, properties = $A (arguments
        );

        if (Object.isfunction (Properties[0])) parent = Properties.shift ();
        The definition function of a subclass constructor Klass () {this.initialize.apply (this, arguments);
        ///Add a prototype method to the subclass Class.Methods.addMethods Object.extend (Klass, Class.methods);
        Not only does the current class hold a reference to the parent class, but it also records references to all subclasses Klass.superclass = parent;

        Klass.subclasses = []; if (parent) {//Core code-implements the inheritance of the prototype if it exists, this provides a new way to create a class without invoking the constructor of the parent class/-Use an intermediate transition class, which we used to make
          Use the global initializing variable to achieve the same purpose,//-but the code is a bit more elegant.
          var subclass = function () {};
          Subclass.prototype = Parent.prototype;
          Klass.prototype = new Subclass;
        Parent.subclasses.push (Klass);
          }//Core code-if the subclass has the same method as the parent class, then the special handling will be detailed in later for (var i = 0; i < properties.length i++)Klass.addmethods (Properties[i]);
        
        if (!klass.prototype.initialize) klass.prototype.initialize = prototype.emptyfunction;

        Fixed constructor pointing error klass.prototype.constructor = Klass;
      Return Klass; }
    };

Again to see what Addmethods has done:

Class.methods = {addmethods:function (source) {//If the parent class exists, ancestor a prototype object that points to the parent class var ancestor = This.su
        Perclass && This.superclass.prototype;
        var properties = Object.keys (source); Firefox and Chrome return 1,IE8 return 0, so this place is specially handled if (!

        Object.keys ({tostring:true}). Length) Properties.push ("ToString", "valueof"); Loop subclass prototype defines all the attributes, for those functions that have the same name as the parent class, redefine for (var i = 0, length = properties.length i < length; i++) {//
          property is the attribute name, value is the property body (may be a function, or it may be the object) var properties = Properties[i], value = Source[property]; If the parent class exists and the current current property is a function, and the first parameter of this function is $super if (ancestor && object.isfunction (value) && Value.argu
            Mentnames (). A () = = "$super") {var method = value; The following three lines of code are the essence, presumably://-First to create a self executed anonymous function to return another function, which is used to execute the parent class with the same name function//-(because this is in the loop, we have repeatedly indicated that the function in the loop refers to the local change
Problem of quantity)//-second, the first argument (i.e. corresponding to the formal parameter $super) as method of this executed anonymous function.            However, I think the author of this place is a bit obsessed, there is no need to be so complicated, I will analyze this code in detail.
            Value = (function (m) {return function () {return ancestor[m].apply (this, arguments)};

            ) (property). Wrap (method);
            Value.valueof = Method.valueOf.bind (method); Since we have changed the function body, we redefine the ToString method of the function//so that when the user invokes the function's ToString method, the original function definition body is returned value.tostring = Method.tos
          Tring.bind (method);
        } This.prototype[property] = value;
      return to this; }
    };

I've had "obsession" in the code above, and it's not an insult to the author, it's just that the author uses a bit too much of an important rule in JavaScript (by creating a scope from an anonymous function that executes).

Value = (function (m) {return
      function () {return ancestor[m].apply (this, arguments)};
    }) (property). Wrap (method);

In fact, this code is the same as the following effect:

value = ancestor[property].wrap(method);

We'll see the wrap function unfold more clearly:

Value = (function (FN, wrapper) {
      var __method = fn;
      return function () {return
        wrapper.apply (this, [__method.bind (This)].concat ($A (arguments)))
    (Ancestor[property], method);

As you can see, we actually created the scope for the function Ancestor[property of the parent class through the anonymous function that was executed. The original author is the scope created for the property. The final effect of the two is consistent.

Our re-implementation of Prototypejs inheritance
Analysis of so many, in fact, it is not difficult, on so many concepts, a big deal to change the form of expression.
Here we will use the first few chapters of our own implementation of the Jclass to achieve the Prototypejs form of inheritance.

Note: This is our own implementation of a similar Prototypejs inheritance code, can be directly copied down using//This method is to borrow the PROTOTYPEJS definition function argumentnames (FN) {
      var names = fn.tostring (). Match (/^[\s\ (]*function[^]*\ ([^\)]*)/) [1].replace (/\s+/g, ']). Split (', '); return names.length = = 1 &&!names[0]?
    []: Names; 
        function Jclass (BaseClass, prop) {//Accept only one argument-jclass (prop) if (typeof (BaseClass) = = "Object") {
        Prop = BaseClass;
      BaseClass = null; //The class created by this call (constructor) function F () {//If the parent class exists, the baseprototype of the instance object points to the prototype of the parent class//This provides a way to invoke the parent class in the instance object
        The way if (baseclass) {this.baseprototype = Baseclass.prototype;
      } this.initialize.apply (this, arguments);
        //If this class needs to be extended from other classes if (BaseClass) {var middleclass = function () {};
        Middleclass.prototype = Baseclass.prototype;
        F.prototype = new Middleclass ();
      F.prototype.constructor = F; //Overwrite the parent class with the same name function foR (Var name in prop) {if Prop.hasownproperty (name) {//If this class inherits from the parent class BaseClass and a function name exists in the parent class prototype if (BaseClass && typeof (prop[name)) = = = "function" && argumentnames (Prop[name]) [ 0] = = = "$super") {//Redefine subclass's prototype method Prop[name]//-There are a lot of JavaScript skills, and if you have difficulty reading, you can refer to my previous Javascrip
            T Tips and Tricks series articles//-for example, $super encapsulates the invocation of the parent class method, but the context pointer at the time of invocation is to point to the instance object of the current subclass-the first argument to be $super as a method call
                F.prototype[name] = (function (name, FN) {return function () {var. = this;
                $super = function () {return baseclass.prototype[name].apply, arguments);
                };
              Return fn.apply (This, Array.prototype.concat.apply ($super, arguments));
            };
            
          }) (name, Prop[name]);
          else {F.prototype[name] = Prop[name]; }} return F;
    }; 

The invocation method is consistent with the way Prototypejs is invoked:

 var person = jclass ({
      initialize:function (name) {
        this.name = name;
      },
      getname:function () {
        return this.name;
      }
    });

    var Employee = Jclass (person, {
      initialize:function ($super, name, EmployeeID) {
        $super (name);
        This.employeeid = EmployeeID;
      },
      getemployeeid:function () {return
        This.employeeid;
      },
      Getname:function ($super) {return
        "Employee name:" + $super ();
      }
    });


    var Zhang = new Employee ("Zhangsan", "1234");
    Console.log (Zhang.getname ());  "Employee Name:zhangsan"

Through this chapter of study, we have strengthened our confidence, like the Prototypejs form of inheritance we can easily handle.
In the next few chapters, we will gradually analyze Mootools,extjs and other JavaScript class libraries in the implementation of inheritance, please look forward to.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.