SoFunction
Updated on 2025-02-28

Detailed explanation of JavaScript inheritance (IV)

Classical Inheritance in JavaScript
Crockford is the most well-known authority in the JavaScript development community.JSONJSLintJSMinandADSafeThe father is the author of "JavaScript: The Good Parts".
Now is a senior JavaScript architect at Yahoo, participating in the design and development of YUI. There is an article herearticleA detailed introduction to Crockford's life and works.
Of course, Crockford is also the object of worship among the younger generations.

Call method

First, let's take a look at the call method using Crockford inheritance:
Note: The methods, inherits, and uber in the code are all customized objects, which we will explain in detail in the subsequent code analysis.

    // Define Person class    function Person(name) {
       = name;
    }
    // Define the prototype method of Person    ("getName", function() {
      return ;
    }); 
    
    // Define Employee class    function Employee(name, employeeID) {
       = name;
       = employeeID;
    }
    // Specify that Employee class inherits from Person class    (Person);
    // Define the prototype method of Employee    ("getEmployeeID", function() {
      return ;
    });
    ("getName", function() {
      // Note that the prototype method of the parent class can be called in the subclass      return "Employee name: " + ("getName");
    });
    // Instantiate subclasses    var zhang = new Employee("ZhangSan", "1234");
    (());  // "Employee name: ZhangSan"
    

 

There are several flaws that must be mentioned here:

  • The code inherited by a subclass from the parent class must be performed after both the subclass and the parent class are defined, and must be performed before the subclass prototype method is defined.
  • Although the parent class method can be called in the subclass method body, the subclass constructor cannot call the parent class constructor.
  • The code is not written elegantly enough, such as the definition of prototype methods and the method that calls the parent class (not intuitive).

 

Of course, the implementation of Crockford also supports methods in subclasses to call parent class methods with parameters, as shown in the following example:

    function Person(name) {
       = name;
    }
    ("getName", function(prefix) {
      return prefix + ;
    });

    function Employee(name, employeeID) {
       = name;
       = employeeID;
    }
    (Person);
    ("getName", function() {
      // Note that the first parameter of uber is to call the function name of the parent class, and the following parameters are all parameters of this function      // I personally think this method is not as intuitive as this call: ("Employee name: ")      return ("getName", "Employee name: ");
    });
    var zhang = new Employee("ZhangSan", "1234");
    (());  // "Employee name: ZhangSan"
    

 

Code Analysis

First of all, the definition of the method function is very simple:

     = function(name, func) {
      // this points to the current function, that is, typeof(this) === "function"      [name] = func;
      return this;
    };
    
Pay special attention to the direction of this here. When we see this, we cannot just focus on the current function, but should think of the way the current function is called. For example, we will not call the method in this example through new, so this in the method points to the current function.

 

The inherits function definition is a bit complicated:

    ('inherits', function (parent) {
      // The key is this paragraph: = new parent(), here the reference to the prototype is implemented      var d = {}, p = ( = new parent());
      
      // Add uber method only for the prototype of the subclass. Closure here is to know the prototype of the parent class of the current class when calling the uber function (that is, variable - v)      ('uber', function uber(name) {
        // Consider here if name is the function name that exists in it        // For example "toString" in {} === true        if (!(name in d)) {
          // Counting by d[name], do not understand the specific meaning          d[name] = 0;
        }    
        var f, r, t = d[name], v = ;
        if (t) {
          while (t) {
            v = ;
            t -= 1;
          }
          f = v[name];
        } else {
          // I personally think this code is a bit cumbersome. Since the meaning of uber is a function of the parent class, then f can point directly to v[name]          f = p[name];
          if (f == this[name]) {
            f = v[name];
          }
        }
        d[name] += 1;
        // Execute the function name in the parent class, but this in the function points to the current object        // At the same time, pay attention to the use of arguments to truncate arguments (because arguments are not standard arrays, there is no slice method)        r = (this, (arguments, [1]));
        d[name] -= 1;
        return r;
      });
      return this;
    });
    
Note that there is a small bug in the inherits function, that is, the constructor's pointer is not redefined, so the following error will occur:
    var zhang = new Employee("ZhangSan", "1234");
    (());  // "Employee name: ZhangSan"
    ( === Employee);  // false
    ( === Person);   // true
    

 

Improvement suggestions

According to the previous analysis, I personally think that the method function is not very necessary, but it is easy to confuse the situation. The inherits method can do some weight loss (because Crockford may consider more situations. The original text introduces several ways to use inherits, and we only focus on one of them), and corrects the pointing error of the constructor.

     = function(parent) {
       = new parent();
       = this;
       = function(name) {
        f = [name];
        return (this, (arguments, 1));
      };
    };
    
Call method:
    function Person(name) {
       = name;
    }
     = function(prefix) {
      return prefix + ;
    };
    function Employee(name, employeeID) {
       = name;
       = employeeID;
    }
    (Person);
     = function() {
      return ("getName", "Employee name: ");
    };
    var zhang = new Employee("ZhangSan", "1234");
    (());  // "Employee name: ZhangSan"
    ( === Employee);  // true
    

 

Something interesting

At the end of the article, Crockford actually released such 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 disapproves of implementing object-oriented programming in JavaScript, and claims that JavaScript should be programmed according to the prototype and function patterns (the prototype and functional patterns).
But personally, it would be much more convenient if there is an object-oriented mechanism in complex scenarios.
But who can guarantee that even projects like jQuery UI do not use inheritance, while on the other hand, Extjs and Qooxdoo strongly advocate an object-oriented JavaScript. Even the Cappuccino project invented an Objective-J language to practice object-oriented JavaScript.