Home  >  Article  >  Web Front-end  >  Parsing John Resig Simple JavaScript Inheritance code_javascript tips

Parsing John Resig Simple JavaScript Inheritance code_javascript tips

WBOY
WBOYOriginal
2016-05-16 17:47:36996browse

Since the author will add his own understanding to the translation for his own learning and use, students who are good at English can read the following. If there are translation errors in the article, please leave a message. Communicate and correct. (:
====== ===============Enein translation==========================

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 used some very clever techniques to implement super Method.
You can also read the original text for detailed explanation. He also introduced it in his "Secrets of a JavaScript Ninja". In the book, the method may be a little different. It adds subClass to Object. method, instead of creating a global variable.
Original Script - John Resig Simple JavaScript Inheritance
Below is the pardon code, I removed some comments to make it look clearer.

Copy code The code is as follows:

(function(){
var initializing = false, fnTest = /xyz/.test( function(){xyz;}) ? /b_superb/ : /.*/;
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
Let’s analyze how it is implemented and what technologies are used.

Copy code The code is as follows:

(function(){ // ... }) ();

First we create a self-executing anonymous function to create a scope for the code.

Copy code The code is as follows:
 
var initializing = false

The meaning of this initializing variable is very straightforward, it is a boolean to check when the Class Function (described later) is called. Set initializing to true/false when creating the instance or just return an object pointing to the current prototype chain to achieve " The purpose of inheritance.

If we create an instance (initializing == false), Class has an init method, so init will be executed automatically. Or, if we just assign it to the prototype (initializing == true), nothing will happen, the init method will not be executed. This is done to avoid having to execute the init method every time the constructor is called. (var prototype = new this());.

Copy code The code is as follows:

fnTest = /xyz/.test(function(){ xyz;}) ? /b_superb/ : /.*/;

The purpose of this fnTest is to verify whether the "_super()" call is used in the class method. This technology is called "function decompilation (function decompilation)", also called "function serialisation (function serialization)", Function serialisation It occurs when a function is converted to a string. Many browsers now support the toString method.

To test Function serialisation, fnTest uses an anonymous function funciton(){xyz;} to set the content to "xyz", and after converting it to a string, use regular expressions to search for "xyz". It will return true (if the browser supports function serialisation) Because the function converts it to a string "xyz" is also part of the string. In this case fnTest will return "/b_superb/", in the other it will return "/.*/" if the browser does not Supporting function serialisation always returns true. (This refers to fnTest.test in the original code) Using fnTest regularization, and function serialization technology, we can easily determine whether "_super" is used in methods. If they are used, some special methods will be executed. Otherwise, it is normal. This special The method is to avoid the same method appearing in the parent class and child class at the same time. The parent class will be overwritten.

If the browser does not support Function serialisation, it will always return true, so additional operations will always be performed on _super, resulting in these new methods not being used in _super. This will have some small performance consumption. But it is guaranteed Execute normally in all browsers.

Copy the code The code is as follows:

this.Class = function(){};

Create an empty constructor and put it in the global variable. This will be the top-level constructor. It has no defined content, or a prototype object. Except for the extends method below. this refers to the window object. Use Class variable is a global object.

Copy code The code is as follows:
 
Class.extend = function( prop) { // ...}

Add the extends method and a simple prop (an object) parameter. It will return the prototype of the new constructor and the prototype of the parent object;

Copy code The 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 method where you need it. What is this variable called _super because super is a reserved word . Although it has not been applied yet.

Copy code The 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. The initializing was set to true before, so the init method will not be fired during the new Class. After the prototype variable is assigned, the initializing is set back to false for the next step. Works normally. (e.g when you want to create a real instance)

Copy the code The code is as follows:

for (var name in prop) { // ...}

Using a for loop, we iterate out the attributes and methods in prop. The attribute is passed in through the extends method. In addition to some special processing of _super, we assign the value to the prototype attribute.

Copy code The 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 iterate through each object in prop, if it satisfies (typeof prop[name] == "function") (typeof _super[name] == "function") (fnTest.test(prop[name]) == true)
We will add new methods to handle new methods and original methods bound to the parent class.
The code above may look a bit confusing. Let’s use a clearer way to view it. .

Copy code The 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-executing anonymous function, used when processing the name prop[name] in super. There is no such closure. The reference to this variable will be wrong when returning this function. (e.g it will always return to the end of the loop a)
Iterate through all, we will return a new function, this function handles native methods (via super) and new methods.

Copy code The 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 processing of super, we first need to store the existing _super attributes and some parameters of the class. Store them in a temporary tmp. This is to prevent existing methods in _super from being overwritten
After that We assign tmp to this._super so that it can work normally.
Next, we assign the _super[name] method to this._super of the current object, so that when fn is executed through apply, this ._Super () will point to the parent method. This
This of the parent method can also access the current object.
Finally, we will store the return value in the RET. Object.
There is a simple example below. Define a simple Foo and create an inherited object Bar:

Copy code Code 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, due to the existence of this._super in the qux method, the qux on the Bar prototype should actually be like this:

Copy code The 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 completing this step in the script, the constructor will be called

Copy the code The 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 the local Class.extend. This constructor returns the call to Class.extend (such as the previous Foo.extends). new This constructor will be executed after Foo() is instanced.
The constructor will automatically execute the init() method (if it exists). As mentioned above, this initializing variable controls whether init is executed.

Copy code The code is as follows:
 
Class.prototype = prototype;

The last prototype returns a mixed prototype object of the parent class from the constructor method of the parent class. (e.g var prototype = new this()), this result is passed through the for loop in the extend function.

Class.constructor = Class;
Because we have rewritten the entire prototype object, store this native constructor in this type, so that it can maintain the default shape in the constructor of an instance.

Copy code The code is as follows:
 
Class.extend = arguments.callee;

will assign itself, through arguments.callee, which in this case means "self". In fact, we can avoid using arguments.callee here, if we modify my native method (e.g Class.extend = function extend(prop) ) Then we can copy the code

by using . The code is as follows:
;.return Class;

The instance will then return, a prototype object, a constructor property, an extend method and a self-executable method init.!!!

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