JS object-oriented and prototype__ proto__ ,constructor

Time:2020-11-24

1、 Object oriented and inheritance in Java

  1. In the following code, we define a dog class. In the class, we define a property and two methods. A constructor is used to initialize the dog’s age, and a public method say is used to print.
public class Puppy{
    int puppyAge;
    
    public Puppy(age){
      puppyAge = age;
    }
    
    public void say() {
      System.out.println (Wang Wang Wang Wang); 
    }
}

  1. This is a generic class. When we need an instance of a two-year-old puppy, it is written like this. This instance also has the method of the parent class.
Puppy myPuppy = new Puppy(2);
muPuppy.say (); // Wang Wang Wang

  1. The implementation of the above classes and instances are all based on Java syntax. However, compared with the relatively perfect Java syntax, the early JS did not have the class keyword (below, JS without class keyword refers to JS before ES6, mainly to help you understand the concept). In order to support object-oriented, JS uses a more tortuous way, as follows.

2、 Object oriented and inheritance in JS

  1. No class, use function instead: early JS didn’t have the class keyword, so what to do? Yes, it is replaced by a function, which can not only perform ordinary functions, but also be used as a class. Chestnuts are as follows.
function Puppy() {}

  1. The above code implements a function. Now we can generate an example of the above function.

The constructor itself is a function, and there is no difference with ordinary functions, but in order to standardize, it is usually capitalized. The difference between a constructor and a normal function is that the function that uses new to generate an instance is a constructor, and a normal function is called directly.

let myPuppy = new Puppy()

  1. The function itself is a constructor: Although we have an example of a dog, unlike Java syntax, we can define a constructor in a class to set the age of the dog. Don’t panic. In fact, the function used as the class itself is the constructor, and it is the default constructor. Next, we rewrite the above code to let the constructor receive the function to initialize the dog’s age.
//Constructor: can receive parameters to initialize property values
function Puppy(age) {
  this.puppyAge = age
}

//You can pass the age parameter when instantiating
let myPuppy = new Puppy(2)

  1. This in the constructor points to the instantiated object: this point in the constructor should be noted: in the function used as a class, this always points to the instantiated object, that is, mypuppy. The purpose of this design is to allow users to set properties for instance objects through the constructor, and print them out at this time myPuppy.puppyAge It’s two.
console.log(myPuppy.puppyAge)   // 2

  1. Defining instance method on Prototype: in the above four points, we implement the definition and instantiation of constructors. Java syntax can directly define public methods in the class to let the instance dog bark. What should JS do? The solution given by JS is to add aprototypeProperty, the method mounted on it, will be given to the instance object when instantiated.
//Add a method to the prototype of the constructor
Puppy.prototype.say = function() {
  console.log (Wang Wang Wang Wang)
}

//The instance object calls the corresponding method
myPuppy.say () // Wang Wang Wang

  1. Proto is used to search instance methods: some of the above students may have questions about the method in the constructorprototypeHow can the instance object mypuppy find the say method? Let’s print mypuppy.

(1) When you access a property that is not on an object, such as myPuppy.say The object will go__proto__Find.__proto__Is equal to that of the parent classprototype, myPuppy.__proto__YesPuppy.prototype

(2) If you access a property in thePuppy.prototypeIt doesn’t exist, and it will go onPuppy.prototype.__proto__You can find it at this timeObject.prototypeYes,Object.prototypeIf you look up again, there will be no, that is, nullPrototype chain

  1. constructor

(1) Each instance has a constructor property that points to the object itself.

(2) prototype.constructor Is a reserved property on prototype, which points to the class function itself and indicates the constructor of the current class.

(3) Since prototype.constructor Is a pointer to the constructor. Can we modify the constructor through it? Let’s have a try. Let’s modify this function first, and then create a new instance to see the effect

function Puppy(age) {
  this.puppyAge = age;
}

Puppy.prototype.constructor = function myConstructor(age) {
  this.puppyAge2 = age + 1;
}

const myPuppy2 = new Puppy(2);
console.log(myPuppy2.puppyAge);    // 2

The above example shows that we modify itprototype.constructorIt just changed the pointer, not the actual constructor.

(4) We have already made it clearprototype__proto__constructorThe relationship between the following draw a more intuitive picture

  1. Static methodWe know that many object-oriented objects have the concept of static methods. For example, Java can define a method as a static method by adding a static keyword. It’s easier to define a static method in JS, just take it as an attribute of a class function.
//Define static method statcifnanc on constructor
Puppy.statciFunc = function() {
  console.log ('I am a static method, this can't get the instance object')
}      

//Call directly through the class name
Puppy.statciFunc();

  1. inheritHow can object-oriented have no inheritance? According to the knowledge mentioned above, we can actually write an inheritance ourselves. The so-called inheritance is not that the subclass can inherit the properties and methods of the parent class? In other words, the subclass can find the parent classprototypeThe simplest way is to subclass prototypes__proto__Just point to the parent prototype.

(1) The following inheritance method only allows child to access the parent prototype chain, but does not execute the parent’s constructor

function Parent() {}
function Child() {}

Child.prototype.__proto__ = Parent.prototype;

const obj = new Child();
console.log(obj instanceof Child );   // true
console.log(obj instanceof Parent );   // true

(2) In order to solve the above problems, we can not simply modifyChild.prototype.__proto__You also need to use new to execute the constructor of parent.

function Parent() {
  this.parentAge = 50;
}
function Child() {}

Child.prototype.__proto__ = new Parent();

const obj = new Child();
console.log(obj.parentAge);    // 50

(3) There will be one more method__proto__Level, can be changed to modifyChild.prototypeTo solve the problem, pay attention to theChild.prototype.constructorReset it back.

function Parent() {
  this.parentAge = 50;
}
function Child() {}

Child.prototype = new Parent();
//Pay attention to reset the constructor
Child.prototype.constructor = Child;

const obj = new Child();
console.log(obj.parentAge);   // 50

  1. Implement a new: combined with the above, we know that new is actually the generation of an object. This object can access the prototype of the class. If we know the principle, we can implement a new ourselves.
function myNew(func, ...args) {
    //Create a new empty object
  const obj = {};     
  //Execute constructor
  func.call(obj, ...args);  
  //Set up prototype chain
  obj.__proto__ = func.prototype;    

  return obj;
}

function Puppy(age) {
  this.puppyAge = age;
}

Puppy.prototype.say = function() {
  console.log (Wang Wang Wang Wang);
}

const myPuppy3 = myNew(Puppy, 2);

console.log(myPuppy3.puppyAge);  // 2
console.log (mypuppy3. Say()); // Wang Wang Wang

  1. Implement an instanceof yourselfIf we know the principle, we also know what instanceof does. Isn’t instanceof just checking whether an object is an instance of a class? In other words, check whether there is a prototype of this class in the prototype chain of an object. Knowing this, we can implement one ourselves
function myInstanceof(targetObj, targetClass) {
  //Parameter check
  if(!targetObj || !targetClass || !targetObj.__proto__ || !targetClass.prototype){
    return false;
  }

  let current = targetObj;

  While (current) {// keep looking for the prototype chain
    if(current.__proto__ === targetClass.prototype) {
      Return true; // returns true if found
    }

    current = current.__proto__;
  }

  Return false; // if not found, return false
}

//With our previous inheritance experiment
function Parent() {}
function Child() {}

Child.prototype.__proto__ = Parent.prototype;

const obj = new Child();
console.log(myInstanceof(obj, Child) );   // true
console.log(myInstanceof(obj, Parent) );   // true
console.log(myInstanceof({}, Parent) );   // false

3、 Class of ES6

The class of ES6 is the syntax sugar of the function class mentioned above. For example, we use the class of ES6 to write our puppet

class Puppy {
  //Constructor
  constructor(age) {            
    this.puppyAge = age;
  }

  //Example method
  say() {
    console.log (Wang Wang Wang Wang)
  }

  //Static method
  static statciFunc() {
    console.log ('I am a static method, this cannot get the instance object');
  }
}

const myPuppy = new Puppy(2);
console.log(myPuppy.puppyAge);    // 2
console.log ( myPuppy.say ()); // Wang Wang Wang
console.log ( Puppy.statciFunc ()); // I am a static method, this cannot get the instance object

Using class can make our code look more like standard object-oriented. Constructors, instance methods, and static methods are clearly identified. But in essence, it only changes a way of writing, so it can be regarded as a kind of syntax sugar. If you look at the compiled code of Babel, you will find that it actually compiles the class into the function class in front of us, and the extensions keyword is also implemented in the way of prototype inheritance.

4、 Summary

  1. Functions in JS can be used as functions or as classes

  2. When instantiating a function used as a class, you need to use new

  3. In order for a function to have the function of a class, functions haveprototypeProperty.

  4. In order to make the instantiated object accessibleprototypeProperties and methods on, instance object’s__proto__That points to the classprototype。 thereforeprototypeIs a property of a function, not an object. Objects have__proto__, is used to findprototypeYes.

  5. prototype.constructorIt points to the constructor, which is the class function itself. Changing this pointer does not change the constructor.

  6. The object itself does notconstructorProperty, you are accessing the prototype chainprototype.constructor

  7. The function itself is also an object and also has__proto__, which points to the prototype of JS built-in object function Function.prototype 。 So you can call func.call , func.apply These methods are actually called by you Function.prototype.call And Function.prototype.apply 。

  8. prototypeIt’s also an object, so he has it__proto__, pointing to his parent’s prototype.__proto__andprototypeThis chain direction of JS constitutes the prototype chain of JS. The final direction of the prototype chain is the prototype of the object. The prototype chain above the object is nullObject.prototype.__proto__ === null

  9. In addition, some friends in the comment area mentioned:Function.__proto__ === Function.prototype 。 This is because the prototypes of all functions in JS are Function.prototype That is, all functions are instances of functions. Function itself can also be used as a function — function (), so it is also an instance of function. Similarly, there are object, array and so on. They can also be used as functions: object(), array(). So their own prototypes are Function.prototype , i.eObject.__proto__ === null Function.prototype。 In other words, these built-in objects that can be new are actually a class, just like our puppet class.

  10. The class of ES6 is actually a kind of syntax sugar of function class. It can be written more clearly, but the principle is the same.

Source reference:Easy to understand the object-oriented JS, by the way understand prototype and__ proto__
JS series 2: in depth constructor, prototypeproto, [[prototype]] and prototype chain