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:CodeClass, extend, and _ super are custom objects. We will explain them in the code analysis.
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, for the completion of this seriesArticleTarget-inheritance-is not found. The method is as concise and clear as jquery.
Code Analysis
For a beautiful call method, internal implementation is indeed much more complicated, but it is also worth it-a person's thinking brings countlessProgramHappy Smile-hey, it's a bit boring. 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. This 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!