Since the author's translation will add his understanding to help him learn and use it, if a good English speaker can see the following translation errors in the article, please leave a message, exchange and correct it .(:
================================== Enein ==========
John Resig wrote an article about "inheritance" in JavaScript similar to other languages, inspired by base2 and PrototypeJS. he named the article "Simple JavaScript Inheritance ". he uses some clever techniques to implement the super method.
You can also read the original article for detailed instructions. He also introduced it in his "Secrets of a JavaScript Ninja. there may be some differences in the methods in the book. It adds the subClass Method to the Object, instead of creating a global variable.
Original Script-John Resig Simple JavaScript Inheritance
Below is the code for forgiveness. I removed some comments and used it to look clearer.
Copy codeThe Code is as follows:
(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;
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];
}
Function Class (){
If (! Initializing & this. init)
This. init. apply (this, arguments );
}
Class. prototype = prototype;
Class. constructor = Class;
Class. extend = arguments. callee;
Return Class;
};
})();
Breakdown of the Simple Inheritance script
Next we will analyze how it is implemented and what technologies are used.
Copy codeThe Code is as follows:
(Function (){//...})();
First, we create a self-executed anonymous function to create a scope for the code.
Copy codeThe Code is as follows:
Var initializing = false
This initializing variable is very straightforward. It is a boolean variable used to check when Class Function (described later) is called. set initializing to true/false when creating an instance, or return an object pointing to the current prototype chain for the purpose of "Inheriting.
If we create an instance (initializing = false), the Class has an init method, so the init will be automatically executed. Or, if we only allocate it to the prototype (initializing = true), nothing will happen and the init method will not be executed. This is done to avoid executing the init method every time you call the constructor. (var prototype = new this ());.
Copy codeThe Code is as follows:
FnTest =/xyz/. test (function () {xyz ;})? /\ B _super \ B /:/.*/;
This fnTest aims to verify whether "_ super ()" is used in the class method. this technology is called "function decompilation (function decompilation)" or "Function serialisation (function serialization)". function serialisation occurs when a Function is converted to a string. currently, many browsers support the toString method.
Test Function serialisation. fnTest uses an anonymous Function funciton () {xyz;} to set the content to "xyz". After converting it into a string, use the regular expression to search for "xyz. it returns true (if the browser supports function serialisation) because the function will be converted into a string, "xyz" is also part of the string. in this example, fnTest will return "/\ B _super \ B/", and the other will return "/. */"if the browser does not support function serialisation, true is always returned. (This refers to fnTest in the original code. test) using fnTest Regular Expressions and function serialization technology, we can easily determine whether "_ super" is used in the method. If they are used, some special methods are executed. otherwise normal. this special method is used to avoid the same method in both the parent class and the subclass. the parent class will be overwritten.
If the browser does not support Function serialisation, it will always return true, so it will always perform extra operations on _ super, so these new methods cannot be used in _ super. this will reduce the performance consumption. but it can ensure normal execution in all browsers.
Copy codeThe Code is as follows:
This. Class = function (){};
Create an empty constructor and place it in a global variable. this will be the top-level constructor. it does not define content, or a prototype object. except the following extends method. this indicates the window object. make the Class variable a global object.
Copy codeThe Code is as follows:
Class. extend = function (prop ){//...}
Add the extends method and a simple prop (an object) parameter. It returns the prototype of the new constructor + the prototype of the parent object;
Copy codeThe Code is as follows:
Var _ super = this. prototype;
Store the prototype object of the current object in _ super. this. prototype is the prototype of the extended object. It can access the parent-level method where you need it. This variable is called _ super because super is a reserved word. although it has not been applied yet.
Copy codeThe Code is as follows:
Initializing = true; var prototype = new this (); initializing = false;
The instance class object is stored in the prototype variable, but the init method is not executed. previously set initializing to true, so the fire init method will not be used in the new Class. after the prototype variable is assigned, initializing is set back to false, which can work normally for the next step. (e. g when you want to create a real instance)
Copy codeThe Code is as follows:
For (var name in prop ){//...}
Using a for loop, We iterate the attributes and methods in prop. this attribute is passed through the extends method. In addition to some special processing of _ super, we assign the value to the prototype attribute.
Copy codeThe Code is as follows:
Prototype [name] = typeof prop [name] = "function" & typeof _ super [name] = "function" & fnTest. test (prop [name])? (Function (name, fn) {return function () {// special handling for _ super };}) (name, prop [name]): prop [name];
When we traverse every object in prop, if (typeof prop [name] = "function") (typeof _ super [name] = "function") (fnTest. test (prop [name]) = true)
We will add a new method to handle the new methods and original methods bound to the parent class.
The code in the above method may seem a little confusing. Next, use a clear method to check the code.
Copy codeThe Code is as follows:
If (typeof prop [name] = "function" & typeof _ super [name] = "function" & fnTest. test (prop [name]) {prototype [name] = (function (name, fn) {return function () {// special handling for _ super };}) (name, prop [name]);} else {// just copy the property prototype [name] = prop [name];}
Another self-executed anonymous function is used to process name prop [name] in super. there is no such closure. when this function is returned, the reference of this variable will fail. (e. g it will always return the last loop)
After traversing all the functions, we will return a new function to process native methods (via super) and new methods.
Copy codeThe Code is as follows:
// Special handling for supervar tmp = this. _ super; this. _ super = _ super [name]; var ret = fn. apply (this, arguments); this. _ super = tmp; return ret;
For special super processing, we first need to store some parameters of the existing _ super attribute and class, and store them in the temporary tmp to prevent the existing methods in _ super from being overwritten.
After completion, we will assign tmp to this. _ super so that it can work normally.
Next, assign the _ super [name] method to this of the current object. _ super, so that this. _ super () will point to the parent class method, this
This in the parent method can also access the current object.
Finally, we store the returned value in ret and return this object after _ super is set back.
The following is a simple example to define a simple Foo and create an inherited object Bar:
Copy codeThe Code is as follows:
Var Foo = Class. extend ({qux: function () {return "Foo. qux ";}}); var Bar = Foo. extend ({qux: function () {return "Bar. qux, "+ this. _ super ();}});
When Foo. extends is executed, the qux on the Bar prototype should actually be like this because this. _ super exists in the qux method:
Copy codeThe Code is as follows:
Bar. prototype. qux = function () {var tmp = this. _ super; this. _ super = Foo. prototype. qux; var ret = (function () {return "Bar. qux, "+ this. _ super ();}). apply (this, arguments); this. _ super = tmp; return ret ;}
After this step is completed in the script, the constructor will be called
Copy codeThe Code is as follows:
Function Class () {if (! Initializing & this. init) this. init. apply (this, arguments );}
This Code calls Class to create a new constructor, which is different from the previously created this. class, as a local Class. extend. this constructor returns Class. extend call (for example, Foo. extends ). after the new Foo () instance is created, the constructor is executed.
The constructor will automatically execute the init () method (if any) as mentioned above. The initializing variable controls whether init is executed.
Copy codeThe Code is as follows:
Class. prototype = prototype;
Finally, this prototype returns a hybrid parent class prototype object from the constructor of the parent class. (e. g var prototype = new this (). The result is a for loop in the extend function.
Class. constructor = Class;
Because we have rewritten the entire prototype object and stored this native constructor In this type, so that it can be maintained by default in the constructor of an instance.
Copy codeThe Code is as follows:
Class. extend = arguments. callee;
Will be assigned to itself through arguments. callee: In this example, we can avoid using arguments. callee, If we modify my native method (e. g Class. after extend = function extend (prop), we can use
Copy codeThe Code is as follows:
Class. extend = extend;. return Class;
The instance will return a prototype object, a constructor property, an extend method, and an executable method init .!!!