JS is an object-oriented programming language. The object-oriented in JS focuses on prototype and prototype chain knowledge.
Prototype is the foundation of javascript object-oriented system implementation. Creating objects in javascript is not based on 'classes', but through constructors and prototype chains.
JS is based on prototype chain inheritance.
The concept of class is introduced in ES6 as a template for objects. You can define a class by using the class keyword. The JavaScript class introduced in ES6 is essentially the existing prototype based inheritance syntax sugar of JavaScript.
1. Constructor
function Student(name,number) { this.name = name this.number = number } var xialuo = new Student("Charlotte",23)
The things behind the new constructor are:
- Create an empty object and point this to the empty object;
- Point the implicit prototype of this object (this) to the constructor. Prototype (the prototype object of the constructor);
- Execute the constructor. apply(this);
- return this object.
Interview questions
How does returning an object in the constructor affect the result
function Super (a) { this.a = a; return 123; } Super.prototype.sayHello = function() { alert('hello world'); } function Super_ (a) { this.a = a; return {a: 2}; } Super_.prototype.sayHello = function() { alert('hello world'); } const super1 = new Super(3) const super2 = new Super_(3) console.log(super1) // Super { a: 3 } console.log(super2) // { a: 2 }
In the constructor, the return basic type does not affect the value of the constructor, while the return object type returns the object instead of the constructor.
function Foo () { getName = function () { console.log(1) return this } } Foo.prototype.getName = function () { console.log(3) } Foo.getName = function () { console.log(2) } var getName = function () { console.log(4) } function getName () { console.log(5) } getName() // 4 Foo.getName() // 2 new Foo().getName() // 3 // Foo().getName // Cannot read property 'getName' of undefined
2. Understand prototype and prototype chain
2.1 prototype
Each constructor has a prototype attribute, which points to the prototype object of the constructor, and a constructor attribute in the prototype object refers to the constructor; Each instance object has one__ proto__ Property. When we use the constructor to create an instance, the__ proto__ Property will point to the prototype object of the corresponding constructor.
function Student(name,number) { this.name = name this.number = number } Student.prototype.sayHi = function () { console.log(`full name:${this.name},Student number:${this.number}`) } var xialuo = new Student("Charlotte",23) console.log(xialuo.name) //Charlotte console.log(xialuo.number) //23 xialuo.sayHi(); //Name: Charlotte, student number: 23
Prototype drawing:
xialuo comes out through Student new, so xialuo__ proto__ prototype pointing to Student
xialuo.proto === Student.prototype
Find rules:
When obtaining the instance property xialuo.name or executing the instance method xialuo.sayHi(), first look for its own properties and methods. If it cannot be found, it will automatically pass through the instance__ proto__ Go to the prototype(Student.prototype) of the corresponding class to find it.
ES6 introduces class writing:
class Student { constructor(name, number) { this.name = name this.number = number } sayHi() { console.log(`full name:${this.name},Student number:${this.number}`) } } const xialuo = new Student("Charlotte",23) console.log(xialuo.name) //Charlotte console.log(xialuo.number) //23 xialuo.sayHi(); //Name: Charlotte, student number: 23 xialuo.hasOwnProperty("name") //true xialuo.hasOwnProperty("sayHi") //false
2.2 prototype chain
Each object created by the constructor has an implicit reference (proto) linked to the "prototype" attribute value of the constructor, that is, the prototype object of the constructor. The prototype object may have a non null implicit reference linked to its own prototype, and so on. This is called the prototype chain.
When trying to access the properties / methods of a JavaScript instance, it first searches the instance itself; When it is found that the instance has no corresponding attribute / method defined, it will turn to search the prototype object of the instance; If the prototype object cannot be searched, it will search the prototype object of the prototype object. This search track is called the prototype chain.
function People(name) { this.name = name; } People.prototype.eat = function () { console.log(`${this.name} eat something`) } //ES5 parasitic composite implementation inheritance function Student(name,number) { // First create the instance object this of the subclass, and then add the method of the parent class to this (Parent.apply(this)). People.call(this,name) // Borrow the constructor and call the parent constructor for the first time this.number = number } Student.prototype = Object.create(People.prototype) // Use the Object.create built-in function to create a new object by explicitly specifying the prototype. Student.prototype.constructor = Student; Student.prototype.sayHi = function () { console.log(`full name:${this.name},Student number:${this.number}`) } const xialuo = new Student("Charlotte",23) console.log(xialuo.name) //Charlotte console.log(xialuo.number) //23 xialuo.sayHi(); //Name: Charlotte, student number: 23
Prototype chain diagram:
xialuo comes out through Student new, so xialuo__ proto__ Point to the prototype of the student. Student inherits from People. The internal principle is that the prototype of Student.prototype points to People.prototype, so Student.prototype__ proto__ Point to People.prototype
Inheritance of ES6:
The essence of inheritance in ES5 is to first create the instance object this of the subclass, and then add the attributes of the parent class to this (Parent.apply(this)).
The inheritance mechanism of ES6 is completely different. The essence is to add the properties and methods of the parent instance object to this (so you must call the super method first), and then modify this with the constructor of the subclass. So, in ES6, class is used to implement inheritance. Subclass must call super method in constructor method, otherwise, it will report wrong when new instance is created. This is because the subclass does not have its own this object, but inherits the this object of the parent class and processes it. If you do not call the super method, the subclass will not get the this object.
class People { constructor(name) { this.name = name } eat() { console.log(`${this.name} eat something`) } } // This class inherits all properties and methods of the People class through the extends keyword. However, since no code is deployed, the two classes are exactly the same, which is equivalent to copying a People class. class Student extends People { constructor (name,number) { super(name) // Call the constructor(name) of the parent class // super represents the constructor of the parent class when called as a function. // super() is equivalent to People.prototype.constructor.call(this) this.number = number } sayHi() { console.log(super.eat() + ',' + this.number) // When super is used as an object, in the normal method, it points to the prototype object of the parent class; In a static method, point to the parent class// ES6 specifies that super will bind this of the subclass when calling the method of the parent class through super. } } const xialuo = new Student("Charlotte",23) console.log(xialuo.name) //Charlotte console.log(xialuo.number) //23 xialuo.sayHi(); //Name: Charlotte, student number: 23
2. Method of judging inheritance relationship
- Principle of A instanceof B: through A__ proto__ Look up in the prototype chain of A to see if B.prototype can be found. This method B must be A constructor;
console.log(xialuo instanceof Student) //true console.log(xialuo instanceof People) //true console.log(xialuo instanceof Object) //true let arr = [1,2,3] console.log(arr instanceof Array) //true
- B.prototype isPrototypeOf(A): look up in the prototype chain of A to see if B.prototype can be found. This method does not need to access the constructor;
- Object.getPrototypeOf(A) === B.prototype ;
Object.getPrototypeOf(A): get the prototype chain of a and see if Foo.prototype can be found; - A._proto _=== B.prototype.
3. Attribute shielding
If the attribute name foo appears both in instance object A and in the prototype chain of A, masking occurs.
The foo attribute in instance object a will mask all foo attributes on the upper level of the prototype chain, because A.foo always selects the lowest foo attribute in the prototype chain.