Home  >  Article  >  Web Front-end  >  Detailed explanation of JavaScript inheritance (4)_js object-oriented

Detailed explanation of JavaScript inheritance (4)_js object-oriented

WBOY
WBOYOriginal
2016-05-16 18:50:00738browse
Classical Inheritance in JavaScript.
Crockford is the most well-known authority in the JavaScript development community and is the author of JSON, JSLint, JSMin and ADSafe is the author of "JavaScript: The Good Parts". He is now a senior JavaScript architect at Yahoo and participates in the design and development of YUI. Here is an
article detailing Crockford’s life and writings. Of course Crockford is also someone we and other juniors admire.
Calling method

First, let us look at the calling method using Crockford-style inheritance:

Note: method, inherits, and uber in the code are all custom objects, which we will explain in detail in the subsequent code analysis.

    // 定义Person类
    function Person(name) {
      this.name = name;
    }
    // 定义Person的原型方法
    Person.method("getName", function() {
      return this.name;
    }); 
    
    // 定义Employee类
    function Employee(name, employeeID) {
      this.name = name;
      this.employeeID = employeeID;
    }
    // 指定Employee类从Person类继承
    Employee.inherits(Person);
    // 定义Employee的原型方法
    Employee.method("getEmployeeID", function() {
      return this.employeeID;
    });
    Employee.method("getName", function() {
      // 注意,可以在子类中调用父类的原型方法
      return "Employee name: " + this.uber("getName");
    });
    // 实例化子类
    var zhang = new Employee("ZhangSan", "1234");
    console.log(zhang.getName());  // "Employee name: ZhangSan"
    

There are a few shortcomings that have to be mentioned here:

    The code that a subclass inherits from a parent class must be done after both the subclass and the parent class are defined, and must be done before the prototype method of the subclass is defined.
  • Although the method body of the subclass can call the method of the parent class, the constructor of the subclass cannot call the constructor of the parent class.
  • The writing of the code is not elegant enough, such as the definition of prototype methods and the method of calling the parent class (not intuitive).

Of course, Crockford’s implementation also supports methods in subclasses to call parent class methods 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() {
      // 注意,uber的第一个参数是要调用父类的函数名称,后面的参数都是此函数的参数
      // 个人觉得这样方式不如这样调用来的直观: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 of all, the definition of the method function is very simple:

Pay special attention to the point of this here. When we see this, we should not just focus on the current function, but should think of how 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.
    Function.prototype.method = function(name, func) {
      // this指向当前函数,也即是typeof(this) === "function"
      this.prototype[name] = func;
      return this;
    };
    

The definition of the inherits function is a bit complicated:

Note that there is a small BUG in the inherits function, that is, the pointer of the constructor is not redefined, so the following error will occur:
    Function.method('inherits', function (parent) {
      // 关键是这一段:this.prototype = new parent(),这里实现了原型的引用
      var d = {}, p = (this.prototype = new parent());
      
      // 只为子类的原型增加uber方法,这里的Closure是为了在调用uber函数时知道当前类的父类的原型(也即是变量 - v)
      this.method('uber', function uber(name) {
        // 这里考虑到如果name是存在于Object.prototype中的函数名的情况
        // 比如 "toString" in {} === true
        if (!(name in d)) {
          // 通过d[name]计数,不理解具体的含义
          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 {
          // 个人觉得这段代码有点繁琐,既然uber的含义就是父类的函数,那么f直接指向v[name]就可以了
          f = p[name];
          if (f == this[name]) {
            f = v[name];
          }
        }
        d[name] += 1;
        // 执行父类中的函数name,但是函数中this指向当前对象
        // 同时注意使用Array.prototype.slice.apply的方式对arguments进行截断(因为arguments不是标准的数组,没有slice方法)
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d[name] -= 1;
        return r;
      });
      return this;
    });
    
    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

Based on the previous analysis, I personally feel that the method function is not necessary, but it is easy to confuse the line of sight. The inherits method can do some slimming down (because Crockford may consider more situations. The original article introduces several ways to use inherits, and we only focus on one of them), and corrects the pointing error of the constructor.

Calling method:
    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));
      };
    };
    
    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 actually said this:

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 object-oriented programming in JavaScript and claims that JavaScript should follow the prototypal and functional patterns ) for programming.
But for me personally, it would be much more convenient if there is 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 strongly advocate an object-oriented JavaScript. The Cappuccino project even invented an Objective-J language to implement object-oriented JavaScript.
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