JavaScript inheritance (4) and javascript inheritance
In this chapter, we will analyze Douglas Crockford's implementation of JavaScript Inheritance-Classical Inheritance in JavaScript.
Crockford is The most well-known authority in The JavaScript development community. He is The father of JSON, JSLint, JSMin, and ADSafe and is The author of JavaScript: The Good Parts.
Now, I am a senior JavaScript architect of Yahoo and participate in YUI design and development. Here is an article detailing Crockford's life and works.
Of course, Crockford is also the target of Small-class worship.
Call Method
First, let's take a look at the call methods inherited by Crockford:
Note: The methods, inherits, and uber in the Code are all custom objects. We will explain them in the code analysis below.
// Define the Person class function Person (name) {this. name = name;} // defines the prototype of Person. method ("getName", function () {return this. name ;}); // defines the Employee class function Employee (name, employeeID) {this. name = name; this. employeeID = employeeID;} // specify that the Employee class inherits from the Person class. inherits (Person); // defines the prototype method of Employee. method ("getEmployeeID", function () {return this. employeeID;}); Employee. method ("getName", function () {// note that you can call the parent class's prototype method return "Employee name:" + this. uber ("getName") ;}); // instantiate the subclass var zhang = new Employee ("ZhangSan", "1234"); console. log (zhang. getName (); // "Employee name: ZhangSan"
There are several hard injuries that you have to mention:
- Code inherited from the parent class of the subclass must be defined after both the subclass and parent class are defined, and must be performed before the subclass prototype method is defined.
- Although the methods of the parent class can be called in the subclass method body, the constructor of the subclass cannot call the constructor of the parent class.
- Code writing is not elegant enough, such as the definition of the prototype method and the method for calling the parent class (not intuitive ).
Of course, Crockford's implementation also supports the method in the subclass to call the parent class method with parameters, as shown in the following example:
Function Person (name) {this. name = name;} Person. method ("getName", function (prefix) {return prefix + this. name ;}); function Employee (name, employeeID) {this. name = name; this. employeeID = employeeID;} Employee. inherits (Person); Employee. method ("getName", function () {// note that the first parameter of uber is to call the function name of the parent class, the following parameters are the parameters of this function. // I personally think this method is not as intuitive as calling this method: this. uber ("Employee name:") return this. uber ("getName", "Employee name:") ;}); var zhang = new Employee ("ZhangSan", "1234"); console. log (zhang. getName (); // "Employee name: ZhangSan"
Code Analysis
First, the definition of the method function is very simple:
<Pre name = "code" class = "javascript"> Function. prototype. method = function (name, func) {// this points to the current function, that is, typeof (this) = "function" this. prototype [name] = func; return this ;};
Pay special attention to the point of this. When we see this, we should not only focus on the current function, but should think of the method of calling the current function. For example, we will not call the method in this example through the new method, so this in method points to the current function.
The definition of the inherits function is a bit complicated:
Function. method ('explores', function (parent) {// The key is this section: this. prototype = new parent (). The reference var d ={}, p = (this. prototype = new parent (); // Add the uber method only for the prototype of the subclass, the Closure here is to know the prototype of the parent class of the current class (that is, the variable-v) when calling the uber function this. method ('uber ', function uber (name) {// here we consider that if name exists in Object. name of the function in prototype // For example, "toString" in {}== true if (! (Name in d) {// count by d [name], do not understand the specific meaning d [name] = 0;} var f, r, t = d [name], v = parent. prototype; if (t) {while (t) {v = v. constructor. prototype; t-= 1;} f = v [name];} else {// I personally think this code is a bit cumbersome. Since uber is a function of the parent class, then f points directly to v [name] and f = p [name]; if (f = this [name]) {f = v [name] ;}} d [name] + = 1; // execute the function name in the parent class, but this points to the current object in the function // use Array at the same time. prototype. slice. apply (because arguments is not a standard array and there is no slice method) r = f. apply (this, Array. prototype. slice. apply (arguments, [1]); d [name]-= 1; return r ;}); return this ;});
Note that there is also a small BUG in the inherits function, that is, the point of the constructor is not redefined, so the following error occurs:
var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan" console.log(zhang.constructor === Employee); // false console.log(zhang.constructor === Person); // true
Suggestions for improvement
According to the previous analysis, I personally think that the method function is not necessary, but it is easy to confuse the line of sight. The inherits method can be used for slimming (because Crockford may consider more situations, the original article describes several ways to use inherits, but we only focus on one of them ), corrected the constructor pointing error.
Function.prototype.inherits = function(parent) { this.prototype = new parent(); this.prototype.constructor = this; this.prototype.uber = function(name) { f = parent.prototype[name]; return f.apply(this, Array.prototype.slice.call(arguments, 1)); }; };
Call method:
function Person(name) { this.name = name; } Person.prototype.getName = function(prefix) { return prefix + this.name; }; function Employee(name, employeeID) { this.name = name; this.employeeID = employeeID; } Employee.inherits(Person); Employee.prototype.getName = function() { return this.uber("getName", "Employee name: "); }; var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan" console.log(zhang.constructor === Employee); // true
Interesting
At the end of the article, Crockford gave out the following words:
I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. the super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.
It can be seen that Crockford does not approve of implementing Object-Oriented Programming in JavaScript, and claims that JavaScript should be programmed according to the prototypal and functional patterns.
However, for me, it is much more convenient to have an object-oriented mechanism in complex scenarios.
But who can guarantee it? Even projects like jQuery UI do not use inheritance. On the other hand, projects like Extjs and Qooxdoo advocate an object-oriented JavaScript. Even the Cappuccino project has released an Objective-J language to practice object-oriented JavaScript.