Explanation of JavaScript inheritance (5)

Source: Internet
Author: User

In this chapter, we will analyze John Resig's implementation of JavaScript Inheritance-Simple JavaScript Inheritance.
John Resig is well known as the founder of jQuery. It is the author of "Pro JavaScript Techniques", and Resig will launch a book "JavaScript Secrets" this fall.

Call Method

The call method is very elegant:
Note: The Class, extend, and _ super in the Code are all custom objects. We will explain them in the code analysis below.

Var Person = Class. extend ({// init is the constructor init: function (name) {this. name = name ;}, getName: function () {return this. name ;}}); // The Employee class inherits var Employee = Person from the Person class. extend ({// init is the constructor init: function (name, employeeID) {// call the constructor of the parent class in the constructor this. _ super (name); this. employeeID = employeeID;}, getEmployeeID: function () {return this. employeeID;}, getName: function () {// call the parent class method return "Employee name:" + this. _ super () ;}}); var zhang = new Employee ("ZhangSan", "1234"); console. log (zhang. getName (); // "Employee name: ZhangSan"
To be honest, there are really no disadvantages for accomplishing the objective-inheritance-of this series of articles. The method is as concise and clear as jQuery.

Code Analysis

For a beautiful call method, internal implementation is indeed a lot more complicated, but it is also worth it-a person's thinking brings a smile to countless programmers-Hey hey, it's a bit cool.
However, some of the Code does confuse me for a while:

        fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;         
I wrote an article about this issue in my blog a few days ago. If you are interested, I can go over it.
// Create a context for the Self-executed anonymous function to avoid introducing the global variable (function () {// initializing variable to indicate whether the class is currently being created, //-during the class creation stage, the prototype method init cannot be called. // we have elaborated on this issue in the third article in this series. // fnTest is a regular expression., the possible value is/\ B _super \ B/or /. */) //-to/xyz /. test (function () {xyz;}) is used to check whether the browser supports the test parameter as a function. //-however, I tested IE7.0, Chrome2.0, and FF3.5, true is returned for all tests. //-So I think it is also true to assign a value to fnTest in most cases: fnTest =/\ B _super \ B/; var initializing = false, fnTest =/xyz /. test (function () {xyz ;})? /\ B _super \ B /:/. * // The base class constructor // this is a window, so the entire code section opens a window-window to the outside world. class this. class = function () {}; // inherit the Class defined by the method. extend = function (prop) {// This place is very confusing, do you still remember what I mentioned in the second article of this series? //-what does this point, it depends on how this function is called. //-we already know that extend must be called as a method instead of a constructor. //-so here this is not an Object, but Function (that is, Class), then this. prototype is the prototype object of the parent class //-Note: _ super points to the prototype object of the parent class. We will encounter this variable var _ super = this multiple times in the code below. pro Totype; // inherit by pointing the subclass prototype to an instance object of the parent Class //-Note: this is the base Class Constructor (that is, Class) initializing = true; var prototype = new this (); initializing = false; // I think this code has been optimized by the author, so it is very hard to read, I will explain it later for (var name in prop) {prototype [name] = typeof prop [name] = "function" & typeof _ super [name] = "function" & fnTest. test (prop [name])? (Function (name, fn) {return function () {var tmp = this. _ super; this. _ super = _ super [name]; var ret = fn. apply (this, arguments); this. _ super = tmp; return ret ;};} (name, prop [name]): prop [name] ;}// this area can be seen, resig is a good disguise. // It is confusing to use a local variable with the same name to overwrite the global variable. // if you think of a hidden port, you can use another name, for example, function F () is used to replace function Class () //-Note: The Class here is not the base Class defined in the outermost layer. The constructor function Class () {// call the prototype method init if (! Initializing & this. init) this. init. apply (this, arguments);} // prototype of the subclass points to the instance of the parent Class (the key to inheritance) Class. prototype = prototype; // corrected the constructor pointing to the wrong Class. constructor = Class; // subclass automatically obtains the extend method, arguments. callee points to the currently executed function Class. extend = arguments. callee; return Class ;};})();
Next I will interpret the for-in loop and replace the self-executed anonymous method with a local function, which helps us to see the truth:
(Function () {var initializing = false, fnTest =/xyz/. test (function () {xyz ;})? /\ B _super \ B /:/. */; this. class = function () {}; Class. extend = function (prop) {var _ super = this. prototype; initializing = true; var prototype = new this (); initializing = false; // if the parent class and the subclass have a method of the same name, and this method (name) in the subclass) if _ super calls the parent class method //-then the function fn (name, fn) {return function () {// is redefined to protect the instance method _ super. // I personally think this place is unnecessary, because this. _ super will be redefined every time such a function is called. Var tmp = this. _ super; // when executing the subclass instance method name, add another instance method _ super, which points to the same name method of the parent class this. _ super = _ super [name]; // Method name of the execution subclass. Note that this is in the method body. _ super can call the method var ret = fn with the same name as the parent class. apply (this, arguments); this. _ super = tmp; // return the execution result return ret;} // copy all attributes in prop to the subclass prototype for (var name in prop) {// If a function with the same name exists in the prop and parent classes, and the _ super method is used in this function, this method is specially processed-fn // otherwise, this method prop [name] is directly assigned to the prototype if (typeof prop [name] = "function "&& Typeof _ super [name] === "function" & fnTest. test (prop [name]) {prototype [name] = fn (name, prop [name]);} else {prototype [name] = prop [name];} function Class () {if (! Initializing & this. init) {this. init. apply (this, arguments) ;}} Class. prototype = prototype; Class. constructor = Class; Class. extend = arguments. callee; return Class ;};})();

Do you think the implementation of Resig is similar to the jClass we implement step by step in Chapter 3. Before writing this series of articles, I have some knowledge about prototype, mootools, extjs, jQuery-Simple-Inheritance, Crockford-Classical-Inheritance, most of them have been used in actual projects. In chapter 3, the implementation of jClass also referred to the implementation of Resig. I would like to express my gratitude to Resig.
Next, we will transform jClass into the Class with the same behavior.

Our implementation

Transforming the jClass we implemented in Chapter 3 into the form written by John Resig is quite simple. You only need to modify the two or three lines:

(Function () {// whether it is currently in the stage of creating a class var initializing = false; jClass = function () {}; jClass. extend = function (prop) {// if the object that calls the current function (function here) is not a Class, it is the parent Class var baseClass = null; if (this! = JClass) {baseClass = this;} // The class (constructor) function F () created in this call {// if the class is currently in the stage of instantiation, call the init prototype function if (! Initializing) {// if the parent class exists, the baseprototype of the Instance Object points to the prototype of the parent class. // This provides a way to call the parent class method in the instance object if (baseClass) {this. _ superprototype = baseClass. prototype;} this. init. apply (this, arguments) ;}}// if this class needs to be extended from other classes if (baseClass) {initializing = true; F. prototype = new baseClass (); F. prototype. constructor = F; initializing = false;} // The extend function F is automatically attached to the newly created class. extend = arguments. callee; // override the Same name function of the parent class for (var name in prop) {If (prop. hasOwnProperty (name) {// if this class inherits from the parent class baseClass and the parent class prototype contains the same name function name if (baseClass & typeof (prop [name]) === "function" & typeof (F. prototype [name]) = "function" & amp;/\ B _super \ B /. test (prop [name]) {// redefine the function name-// first set this in the function context. _ super points to the function with the same name in the parent class prototype // then calls the function prop [name] and returns the function result. // Note: The self-executed function creates a context, this context returns another function, // This function can apply the variables in this context, this is the Closure (Closure ). // This is a common technique in JavaScript framework development. F. prototype [name] = (function (name, fn) {return function () {this. _ super = baseClass. prototype [name]; return fn. apply (this, arguments) ;};} (name, prop [name]);} else {F. prototype [name] = prop [name] ;}} return F ;}}) (); // transformed jClass var Person = jClass. extend ({init: function (name) {this. name = name ;}, getName: function (prefix) {return prefix + this. name ;}}); var Employee = Person. extend ({init: function (name, employeeID) {// call the method of the parent class this. _ super (name); this. employeeID = employeeID;}, getEmployeeIDName: function () {// note: we can also call other functions in the parent class var name = this. _ superprototype. getName. call (this, "Employee name:"); return name + ", Employee ID:" + this. employeeID;}, getName: function () {// call the parent class method return this. _ super ("Employee name:") ;}}); var zhang = new Employee ("ZhangSan", "1234"); console. log (zhang. getName (); // "Employee name: ZhangSan" console. log (zhang. getEmployeeIDName (); // "Employee name: ZhangSan, Employee ID: 1234"
Just cool!

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.