Constructor, prototype, prototype chain, prototype chain inheritance in JS

Posted by bmyster on Tue, 14 May 2019 13:32:08 +0200

JavaScript is a prototype-based interpretive scripting language, which runs on the browser side. At the same time, JS is an object-oriented programming language. Everything in JS can be regarded as objects, strings, numbers, functions and so on.
First, what are the ways to create objects?
1. Create an object literal: var student = key: value,...}
2. Create objects through new Object():

var student = new Object();
  student.name = 'zhangsan',
  student.age = 18,
  student.gender = 'male',
  student.sayHi = function () {
    console.log("hi,my name is "+this.name);
  }

In both ways, when it comes to creating multiple objects of the same type, the code redundancy is too high and needs to be improved.
Improvement 1: Factory function:

function createStudent(name,age,gender){
	var student = new Object();
	  student.name = 'zhangsan',
	  student.age = 18,
	  student.gender = 'male',
	  student.sayHi = function () {
	    	console.log("hi,my name is "+this.name);
	  }
	  return student;
}

var s1 = createStudent('zhangsan', 18, 'male');
var s2 = createStudent('lisi', 19, 'male');

This encapsulation does solve the problem of code redundancy, but every call to function createStudent() creates a new function, sayHi(), which means that each object has its own version of sayHi(), and in fact, each object shares a function. To solve this problem, we introduce an important concept in object-oriented programming: constructor. The function name of the constructor is uppercase

Create objects by [constructor]:

function Student(name,age,gender){
	this.name = name;
	this.age = age;
	this.gender = gender;
	this.sayhi = function(){
		console.log("hi,my name is" + this.name)
	}

var s1 = new Student("zhansan",18,"male");

}

The difference between a constructor and a factory function:
(1) instead of creating objects in the constructor, attributes and methods are assigned to this object by using this keyword.
(2) There is no return statement in the constructor, and this attribute defaults to the return value of the constructor.
(3) Function names are capitalized Student;
(4) Create objects with the new operator and class name Student.

But there are still some problems with the constructor. Take the Student constructor above. Because each object is created by new Student, the function sayHi will be recreated every time an object is created. At this time, each object has a separate but identical method, which will inevitably result in *** memory waste **.* . Some people may think that, since it is the same, we will put it forward separately, write a function, each call is not OK? However, this will lead to an increase in global variables, which may lead to naming conflicts, confusion of code results, and maintenance difficulties. This problem can be solved very well by using *** prototype ***.

In JavaScript, each function has a prototype attribute that points to another object. All attributes and methods of this object are inherited by instances of constructors. Let's look at the prototype of the previous example.

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype.sayHi = function(){
    console.log("hi");
}
var s1 = new Student('zhangsan', 18, 'male');
s1.sayHi();//Print hi
var s2 = new Student('lisi', 18, 'male');
s2.sayHi();//Print hi
console.log(s1.sayHi == s2.sayHi); // The result is true

The prototype object of the constructor has a constructor attribute by default, pointing to the function where the prototype object resides.

function F() {}
console.log(F.prototype.constructor === F);//The result is ture

The instance object obtained by the constructor contains a pointer _proto_ to the prototype object of the constructor. _ Proto_ attribute was first introduced by Firefox Browser to access prototypes through instance objects, which was a non-standard attribute in the early days.

function F() {}
var a = new F();
console.log(a.__proto__ === F.prototype); //The result is true

Instance objects have direct access to prototype object members. All instances directly or indirectly inherit the members of the prototype object.
Summary: Each constructor has a prototype object. The prototype object contains a constructor attribute pointing to the function where the prototype object is located, and the instance contains an internal pointer _proto_ pointing to the prototype object.

Prototype Chain: All objects have prototypes, and the prototype is also the object, that is, the prototype also has prototypes, so if this goes on, it will constitute what we call the prototype chain.
Attribute Search Principle: Attribute Search Principle, that is, the search order of attributes, will follow the following principles when accessing members of an object:
(1) Start with the object instance itself and return if this property or method is found.
(2) If the object instance itself is not found, it will be found from its prototype, and if it is found, it will be returned.
(3) If the prototype of an object instance is not found, it is searched from the prototype of its prototype, and if it is found, it is returned.
(4) Keep searching along the prototype chain and return when found. If the end of the prototype chain has not been found, then undefined if the attribute is found, and xxx is not a function if the method is searched.

In the previous example, we used xxx.prototype. Then we added the attribute name or method name to write the prototype, but it was a bit cumbersome to write every attribute or method, so we could rewrite the whole prototype object with an object literal containing all the attributes and methods:

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype = {
    hobby:"study",
    sayHi:function(){
    console.log("hi");
    }
}
var s1 = new Student("wangwu",18,"male");
console.log(Student.prototype.constructor === Student);//The result is false.

But there is also a problem with this writing, that is, the prototype object loses the constructor member. So in order to keep the constructor members pointing correctly, the proposed wording is:

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype = {
    constructor: Student, //Manually point the constructor to the correct constructor
    hobby:"study",
    sayHi:function(){
    console.log("hi");
    }
}
var s1 = new Student("wangwu",18,"male");
console.log(Student.prototype.constructor === Student);//The result is true

Prototype Chain Inheritance: The main idea is to use prototypes to let one reference type inherit the attributes and methods of another reference type.

function Student(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
Student.prototype.sayHi = function(){
    console.log("hi");
}
var s1 = new Student("zhangsan",18,"male");
s1.sayHi(); //Print hi
var s2 = new Student("lisi",18,"male");
s1.sayHi(); //Print hi

In the example above, both the instantiated objects s1 and s2 inherit the sayhi method.

Object-oriented programming is a mainstream way of JavaScript programming, which can improve the readability of code, reduce code redundancy and reduce the difficulty of code design.

Topics: Attribute Programming Javascript Firefox