SoFunction
Updated on 2025-02-28

Detailed explanation of JavaScript inheritance (II)

this

This represents the current object. If this is used within the global scope, it refers to the current page object window; if this is used in the function, this refers to what is called on the object on which this function is based on the runtime. We can also use two global methods apply and call to change the specific direction of this in the function.

Let’s first look at an example of using this within the global scope of action:

  <script type="text/javascript">
   (this === window); // true
   ( === ); // true
   (("021", 10)); // 10
  </script>
  

 

This in a function is determined at runtime, not when the function is defined, as follows:

  // Define a global function  function foo() {
   ();
  }
  // Define a global variable, equivalent to = "apple";  var fruit = "apple";
  // At this time, this in the function foo points to the window object  // This method of calling is completely equivalent to ();  foo(); // "apple"

  // Customize an object and point the property foo of this object to the global function foo  var pack = {
   fruit: "orange",
   foo: foo
  };
  // This in function foo points to the object  (); // "orange"
  

 

The global functions apply and call can be used to change the pointing of this in the function, as follows:

  // Define a global function  function foo() {
   ();
  }
  
  // Define a global variable  var fruit = "apple";
  // Customize an object  var pack = {
   fruit: "orange"
  };
  
  // Equivalent to ();  (window); // "apple"
  // This === pack in foo  (pack); // "orange"
  
Note: The functions of apply and call are the same, the only difference is that the parameter definitions of the two functions are different.

 

Because functions are also objects in JavaScript, we can see the following interesting examples:

  // Define a global function  function foo() {
   if (this === window) {
    ("this is window.");
   }
  }
  
  // Function foo is also an object, so you can define the attribute boo of foo as a function   = function() {
   if (this === foo) {
    ("this is foo.");
   } else if (this === window) {
    ("this is window.");
   }
  };
  // Equivalent to ();  foo(); // this is window.
  
  // You can see this in the function pointing to the object calling the function  (); // this is foo.
  
  // Use apply to change the pointing of this function  (window); // this is window.
  

 

prototype

We have used prototype to simulate classes and inherited implementations in Chapter 1. prototype is essentially a JavaScript object. And each function has a default prototype property.
If this function is used in the scenario where custom objects are created, we call this function a constructor. For example, the following simple scenario:

  // Constructor  function Person(name) {
    = name;
  }
  // Define the prototype of Person, the properties in the prototype can be referenced by custom objects   = {
   getName: function() {
    return ;
   }
  }
  var zhang = new Person("ZhangSan");
  (()); // "ZhangSan"
  
As an analogy, we consider the data types in JavaScript - string (String), number (Number), array (Array), object (Object), date (Date), etc. We have reason to believe that these types are implemented as constructors within JavaScript, such as:
  // Define the constructor of the array as a predefined type of JavaScript  function Array() {
   // ...
  }
  
  // Initialize an instance of the array  var arr1 = new Array(1, 56, 34, 12);
  // However, we prefer the following syntax definition:  var arr2 = [1, 56, 34, 12];
  
At the same time, many methods of operating arrays (such as concat, join, push) should also be defined in the prototype attribute.
Actually, all inherent data types in JavaScript have read-only prototype properties (this is understandable: because if these types of prototype properties are modified, which predefined methods disappear), but we can add our own extension methods to it.
  // Extend a method to get the minimum value to JavaScript native type Array   = function() {
   var min = this[0];
   for (var i = 1; i &lt; ; i++) {
    if (this[i] &lt; min) {
     min = this[i];
    }
   }
   return min;
  };
  
  // Call the min method on any Array instance  ([1, 56, 34, 12].min()); // 1
  

Note: There is a trap here. After adding an extension method to the Array prototype, this extension method will also be looped out when using for-in to loop the array.
The following code illustrates this (assuming that the min method has been extended to Array's prototype):
  var arr = [1, 56, 34, 12];
  var total = 0;
  for (var i in arr) {
   total += parseInt(arr[i], 10);
  }
  (total); // NaN
  
  
The solution is also very simple:
  var arr = [1, 56, 34, 12];
  var total = 0;
  for (var i in arr) {
   if ((i)) {
    total += parseInt(arr[i], 10);
   }
  }
  (total); // 103
  

 

constructor

The constructor always points to the constructor that creates the current object. For example, the following example:

  // Equivalent to var foo = new Array(1, 56, 34, 12);  var arr = [1, 56, 34, 12];
  ( === Array); // true
  // is equivalent to var foo = new Function();  var Foo = function() { };
  ( === Function); // true
  // Instantiate an obj object by the constructor  var obj = new Foo();
  ( === Foo); // true
  
  // Combine the above two codes and get the following conclusion  ( === Function); // true
  

 

But when the constructor encounters prototype, something interesting happens.
We know that each function has a default property prototype, and the constructor of this prototype points to this function by default. As shown in the following example:

  function Person(name) {
    = name;
  };
   = function() {
   return ;
  };
  var p = new Person("ZhangSan");
  
  ( === Person); // true
  ( === Person); // true
  // Merge the previous two lines of code to get the following results  ( === Person); // true
  
When we redefine the prototype of the function (note: the difference between the above example, it is not modified but overridden here), the constructor behavior is a bit strange, as shown in the following example:
  function Person(name) {
    = name;
  };
   = {
   getName: function() {
    return ;
   }
  };
  var p = new Person("ZhangSan");
  ( === Person); // false
  ( === Person); // false
  ( === Person); // false
  
Why?
It turns out that when it is covered, it is equivalent to performing the following code operation:
   = new Object({
   getName: function() {
    return ;
   }
  });
  
The constructor always points to the constructor that creates its own, so at this time === Object, that is:
  function Person(name) {
    = name;
  };
   = {
   getName: function() {
    return ;
   }
  };
  var p = new Person("ZhangSan");
  ( === Object); // true
  ( === Object); // true
  ( === Object); // true
  
How to fix this problem? The method is also very simple, just re-cover:
  function Person(name) {
    = name;
  };
   = new Object({
   getName: function() {
    return ;
   }
  });
   = Person;
  var p = new Person("ZhangSan");
  ( === Person); // true
  ( === Person); // true
  ( === Person); // true
  

 


In the next chapter, we will improve the implementation of the Person-Employee class mentioned in Chapter 1 and inheritance.