This is the 15th day of my participation in the August Text Challenge.More challenges in August

JavaScript does not recognize the concepts of categories and instances, but implements object-oriented programming through prototype.

The difference between JavaScript’s prototype chain and Java’s Class is that it does not have the concept of “Class”. All objects are instances, and the so-called inheritance relationship is simply referring to the prototype of one object to another.

var Student = {
    name: 'Robot'.height: 1.2.run: function () {
        console.log(this.name + ' is running... '); }};var xiaoming = {
    name: 'Ming'
};

xiaoming.__proto__ = Student; // Redirect the prototype of Xiaoming to Student. It looks like Xiaoming is inherited from Student
Copy the code

The object.create () method can pass in a prototype Object and create a new Object based on that prototype, but the new Object has no properties.

Create an object

JavaScript sets a prototype for each object it creates, pointing to its prototype object.

When we access an Object’s property using obj.xxx, the JavaScript engine looks for the property on the current Object. If not, it looks for the property on its prototype Object. If not, it goes all the way back to the Object.prototype Object. I have to return undefined.

var arr = [1.2.3];
arr ----> Array.prototype ----> Object.prototype ----> null

function foo() {
    return 0;
}
foo ----> Function.prototype ----> Object.prototype ----> null
Copy the code

If the stereotype chain is long, accessing an object’s properties will be slower because it takes more time to find them, so be careful not to make the stereotype chain too long.

The constructor

In addition to creating an object directly with {}, JavaScript can also create an object using a constructor method. It is used by defining a constructor, calling it with the keyword new, and returning an object. If you don’t say new, this is just a normal function that returns undefined. However, if new is written, it becomes a constructor, and its bound this refers to the newly created object and returns this by default, that is, there is no need to return this at the end

var xiaoming = new Student('Ming');
xiaoming.name; // 'xiao Ming'
xiaoming.hello(); // Hello, Xiao Ming!

xiaoming ----> Student.prototype ----> Object.prototype ----> nullThe prototype for Xiaoming points to the prototype for the function Student. If you create xiaohong and Xiaojun, the prototypes for these objects are the same as for XiaomingCopy the code

The object created with new Student() also gets a constructor property from the prototype, which points to the function Student itself.

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true
Copy the code

Xiaoming and xiaohong do not have the prototype property, but this can be checked with the nonstandard __proto__.

If we create a lot of objects through new Student(), their hello functions actually only need to share the same function, which can save a lot of memory.

To create an object that shares a hello function, we simply move the hello function to the common prototype of Xiaoming and Xiaohong (Student. Prototype) according to the object property lookup principle.

function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '! ');
};
Copy the code

Forget to write new

What if a function is defined as a constructor to create an object, but forgot to write new when called?

In strict mode, this.name = name is an error because this is bound to undefined

In non-strict mode, this.name = name does not report an error, because this is bound to window, so it inadvertently creates the global variable name and returns undefined, which is even worse.

Constructors should start with a capital letter and normal functions should start with a lowercase letter, so syntax-checking tools such as JsLint will help you detect missing new

Encapsulate all new operations internally. A common programming pattern looks like this:

function Student(props) {
    this.name = props.name || 'anonymous'; // Default is' anonymous'
    this.grade = props.grade || 1; // The default is 1
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '! ');
};

function createStudent(props) {
    return new Student(props || {})
}
Copy the code

The createStudent() function has several great advantages. First, it does not require new to be called, and second, the arguments are very flexible and can be passed either at all or as follows:

var xiaoming = createStudent({
    name: 'Ming'
});

xiaoming.grade; / / 1
Copy the code

Prototype inheritance

In traditional class-based languages such as Java and C++, the essence of inheritance is to extend an existing Class and generate a new Subclass.

However, JavaScript, with its prototype inheritance, cannot directly extend a Class because there is no Class type.

Extend PrimaryStudent from Student to define PrimaryStudent:

function PrimaryStudent(props) {
    // Call the Student constructor to bind this:
    Student.call(this, props);
    this.grade = props.grade || 1;
}
Copy the code

Calling the Student constructor does not equal inheriting from Student. The prototype of the object created by PrimaryStudent is:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null
Copy the code

Implement the correct prototype chain with an intermediate object whose prototype points to Student.Prototype. Intermediate objects can be implemented with an empty function F.

// PrimaryStudent constructor:
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// empty function F:
function F() {}// Set F's prototype to student. prototype:
F.prototype = Student.prototype;

// Set PrimaryStudent's prototype to a new F object.
PrimaryStudent.prototype = new F();

// Fix the PrimaryStudent prototype constructor to PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// Continue defining methods on the PrimaryStudent prototype (that is, the new F() object) :
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

/ / create xiaoming:
var xiaoming = new PrimaryStudent({
    name: 'Ming'.grade: 2
});
xiaoming.name; // 'xiao Ming'
xiaoming.grade; / / 2

// Verify the prototype:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// Verify the inheritance relationship:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true
Copy the code

Function F is only used for bridging, we create only one instance of new F(), and we do not change the original prototype chain defined by Student

JavaScript archetypal inheritance is implemented by:

  1. Define a new constructor and use it internallycall()Call the constructor you want to “inherit” and bindthis;
  2. By means of intermediate functionsFImplementation of prototype chain inheritance, preferably through encapsulationinheritsFunction completion;
  3. Continue to define new methods on the prototype of the new constructor.

The class inheritance

The advantages of prototype-based inheritance are simplicity, but the disadvantages are that it is more difficult to understand than the traditional class-instance model. The biggest disadvantages are that the implementation of inheritance requires a lot of code and the correct implementation of the prototype chain.

The purpose of class is to make defining classes easier.

class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '! '); }}var xiaoming = new Student('Ming');
Copy the code

As a comparison, the class definition contains the constructor function and the function hello() defined on the prototype object (note the absence of the function keyword), Student.prototype.hello = function () {… } such scattered code.

Another big benefit of using class to define objects is that it makes inheritance easier. You don’t have to worry about the middle objects that a prototype inherits, the constructors of a prototype object, and so on, and you implement them directly through extends

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // Remember to call the parent constructor with super!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade); }}Copy the code

Note that the definition of PrimaryStudent is also implemented by the class keyword, while the extends means that the prototype chain object comes from Student. The constructors of subclasses may not be quite the same as the parent class, for example, PrimaryStudent needs both name and grade, and the constructor of the parent class needs to be called by super(name), otherwise the name property of the parent class will not be initialized properly.

What is the difference between the classes introduced in ES6 and the legacy JavaScript archetypal inheritance? In fact, there is no difference at all. The purpose of class is to let the JavaScript engine implement the prototype chain code that we would have written ourselves. In short, the benefit of using class is that it greatly simplifies the prototype chain code.

Tip draw prototype diagrams to understand prototype relationships

Reference yanhaijing.com/javascript/…

class A {}
class B extends A {}

const b = new B();
Copy the code

What’s the difference between the way I wrote it and the way I wrote it? How to fix it?

function A() {}
function B() {}

B.prototype = Object.create(A.prototype);

const b = new B();
Copy the code
// The result of the following two statements is, why
Function instanceof Object
Object instanceof Function
Copy the code