Home > Article > Web Front-end > Introducing a simple JavaScript class framework_Basic knowledge
When writing the work-in-progress JavaScript book, I spent a considerable amount of time on the JavaScript inheritance system, and in the process studied various different solutions for simulating classic class inheritance. Among these technical solutions, the implementation of base2 and Prototype is the one I admire most.
From these solutions, a framework with its ideological connotation should be extracted. The framework must be simple, reusable, easy to understand, and independent of dependencies. Simplicity and usability are the key points. Here are usage examples:
var Person = Class. extend ( { init: function (isDancing ) { this. dancing = isDancing; }, dance: function ( ) { return this. dancing; } } ); var Ninja = Person.extend({ init: function(){ this._super( false ); }, dance: function(){ // Call the inherited version of dance() return this._super(); }, swingSword: function(){ return true; } }); var p = new Person(true); p.dance(); // => true var n = new Ninja(); n.dance(); // => false n.swingSword(); // => true // Should all be true p instanceof Person && p instanceof Class && n instanceof Ninja && n instanceof Person && n instanceof Class
There are a few things to note:
Quite happy with the results: made the class definition structured, kept single inheritance, and was able to call superclass methods.
Simple class creation and inheritance
The following is its implementation (easy to read and has comments), about 25 lines. Suggestions are welcome and appreciated.
/* Simple JavaScript Inheritance * By John Resig http://ejohn.org/ * MIT Licensed. */ // Inspired by base2 and Prototype ( function ( ) { var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The base Class implementation (does nothing) this.Class = function(){}; // Create a new Class that inherits from this class Class.extend = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.prototype.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; }; })();
Among them, "initializing/don't call init" and "creating _super method" are the most difficult. Next, I will give a brief introduction to this so that everyone can better understand its implementation mechanism.
Initialization
In order to illustrate the function prototype inheritance method, let’s first look at the traditional implementation process, which is to point the prototype attribute of the subclass to an instance of the parent class. As shown below:
function Person ( ) { } function Ninja ( ) { } Ninja. prototype = new Person ( ); // Allows for instanceof to work: (new Ninja()) instanceof Person
However, the challenging point here is that we only want to get the effect of ‘instatnceOf’, without the consequences of instantiating a Person and calling its constructor. To prevent this, set a bool parameter initializing in the code, whose value will be true only when the parent class is instantiated and configured to the prototype property of the child class. The purpose of this processing is to distinguish the difference between calling the constructor during real instantiation and design inheritance, and then call the init method during real instantiation:
if ( !initializing ) this.init.apply(this, arguments);
It is worth paying special attention to because in the init function, quite resource-intensive code may be run (such as connecting to the server, creating DOM elements, etc., no one can predict), so it is completely necessary to make a distinction.
Super Method
When using inheritance, the most common requirement is that the subclass can access the overridden methods of the superclass. In this implementation, the final solution is to provide a temporary method (._super) that points to the superclass method and can only be accessed in the subclass method.
var Person = Class. extend ( { init: function (isDancing ) { this. dancing = isDancing; } } ); var Ninja = Person.extend({ init: function(){ this._super( false ); } }); var p = new Person(true); p.dancing; // => true var n = new Ninja(); n.dancing; // => false
Implementing this functionality requires several steps. First, we use extend to merge the basic Person instance (class instance, whose construction process we mentioned above) with the literal object (function parameter of Person.extend()). During the merging process, a simple check was made: first check whether the attribute to be merged is a function, if so, then check whether the superclass attribute to be overwritten is also a function? If both checks are true, you need to prepare a _super method for this property.
Note that an anonymous closure (returning a function object) is created here to encapsulate the added super method. Based on the need to maintain the running environment, we should save the old this._super (whether it exists or not) to be reset after the function is run. This helps in cases where there is the same name (don't want to accidentally lose the object pointer) Unpredictable problems.
Then, create a new _super method that points only to the overridden method in the super class. Thank God, there is no need to make any changes to _super or change the scope, because the execution environment of the function will automatically change with the function calling object (the pointer this will point to the super class).
Finally, call the method of the literal object. This._super() may be used during method execution. After the method is executed, the attribute _super is reset to its original state, and then return exits the function.
There are many ways to achieve the same effect (I have seen binding super to itself before and then accessing it with arguments.callee), but I feel that this method best reflects the characteristics of usability and simplicity.
Among the many works based on JavaScript prototypes that I have completed, this is the only class inheritance implementation plan that I have published to share with you. I think that concise code (easy to learn, easy to inherit, less downloading) needs to be put forward for everyone to discuss. Therefore, for people who learn JavaScript class construction and inheritance, this implementation plan is a good start.