Analysis of JavaScript object-oriented

What kind of things?

Class: a class is an abstraction of a series of objects that have the same properties and behaviors. The "behavior" here is usually called the method of a class in a class based object-oriented language. In JavaScript, a function is also a "first-class citizen", which can be directly assigned to a variable or an object's attribute. Therefore, in the following discussion of this paper, "behavior" is also included in the category of "attribute".

JavaScript implementation of class

JavaScript specifies that every object can have a prototype ([[prototype]] internal attribute). (before implementing the ECMAScript 5.1 specification, objects other than Object.prototype must have a prototype.) Each object "shares" its prototype's properties: when accessing an object's properties, if the object itself does not have this property, JavaScript will continue to try to access its prototype's properties. In this way, you can make some objects have the same properties by specifying their prototypes. So we can think that in JavaScript, an object based on the same object is an object belonging to the same class.

1. How to specify the prototype of objects in JavaScript

So how are objects in JavaScript related to their prototypes? Or, how are prototypes of objects in JavaScript specified?

1.1 new operator

JavaScript has a new operator, which creates objects based on a function. The prototype of the object created with the new operator is the prototype property of the function (called the "constructor") after the new operator. For example:

var obj = {"key": 1};
function fun() {}
fun.prototype = obj;
var a = new fun();

At this time, the prototype of the fun object is the obj object.

1.2 Object.create method

The Object.create method creates objects directly with the given object as the prototype. A code example:

var a = {"aa": 1};
var b = Object.create(a);

At this time, the prototype of b object is a object.

1.3 Object.setPrototypeOf method

Both the new operator and the Object.create method specify the prototype of an object while creating it. The Object.setPrototypeOf method specifies the prototype of an object that has been created. Code example:

var a = {"aa": 1};
var b = Object.create(a);
// Now the prototype of b is a
var c = {"cc": 2};
Object.setPrototypeOf(b, c);
// Now the prototype of b becomes c
1.4 implicit designation

Numbers, Booleans, strings, arrays, and functions are also objects in JavaScript, and their prototypes are implicitly specified by javascript:

  1. The prototype of numbers (such as 1, 1.1, NaN, Infinity) is Number.prototype;
  2. The prototype of Boolean values (true and false) is Boolean.prototype;
  3. The prototype of string (such as "", "abc") is String.prototype;
  4. The prototype of function (such as function() {}, function (a) {return a + '1';}) is Function.prototype;
  5. Array (such as [], [1, '2']) is the prototype of Array.prototype;
  6. The prototype of an object (such as {}, {"a": 1}) defined directly with curly braces is Object.prototype.
2 code example of defining class in JavaScript

Here is an example of a piece of JavaScript code that defines a class. It defines a class called Person, its constructor takes the name of a string, and a method, introduceSelf, outputs its own name.

// ----====Class definition start====----
function Person(name) {
    this.name = name;
}
Person.prototype.introduceSelf = function () {
    console.log("My name is " + this.name);
};
// ----====End of class definition====----
// Next, instantiate an object of the Person class
var someone = new Person("Tom");
// At this time, the prototype of someone is Person.prototype
someone.introduceSelf(); // Output My name is Tom

If you convert to the class declaration syntax introduced by ECMAScript 6, the definition of the above Person class is equivalent to:

class Person {
    constructor(name) {
        this.name = name;
    }
    introduceSelf() {
        console.log("My name is " + this.name);
    }
}
Rethinking of "constructor"

In the above example, if we do not define the introduceSelf method through Person.prototype, instead, we specify an introduceSelf property for the object in the constructor:

function Person(name) {
    this.name = name;
    this.introduceSelf = function () {
        console.log("My name is " + this.name);
    };
}
var someone = new Person("Tom");
someone.introduceSelf(); // My name is Tom will also be output

Although in this method, the objects coming out of the Person constructor new also have the introduceSelf property, here the introduceSelf becomes a property of someone rather than a common property of the Person class:

function Person1(name) {
    this.name = name;
}
Person1.prototype.introduceSelf = function () {
    console.log("My name is " + this.name);
};
var a = new Person1("Tom");
var b = new Person1("Jerry");
console.log(a.introduceSelf === b.introduceSelf); // Output true
delete a.introduceSelf;
a.introduceSelf(); // My name is Tom will still be output, because introduceSelf is not a property of a itself and will not be delete d
b.introduceSelf = function () {
    console.log("I am a pig");
};
Person1.prototype.introduceSelf.call(b); // Output My name is Jerry
// Even if the introduceSelf attribute of b is overridden, we can still use 'Person1.prototype' to let b perform the behavior specified by the Person1 class.
function Person2(name) {
    this.name = name;
    this.introduceSelf = function () {
        console.log("My name is " + this.name);
    };
}
a = new Person2("Tom");
b = new Person2("Jerry");
console.log(a.introduceSelf === b.introduceSelf); // Output false
// The introduceSelf property of a and the introduceSelf property of b are different objects, which occupy different memory space.
// Therefore, this method will cause a waste of memory space.
delete a.introduceSelf;
a.introduceSelf(); // Can throw TypeError
b.introduceSelf = function () {
    console.log("I am a pig");
};
// At this time, the behavior of b is out of line with the requirements of the Person2 class. Object a and object b do not look like objects of the same class

But this method is not useless. For example, when we need to use closures to encapsulate the name attribute:

function Person(name) {
    this.introduceSelf = function () {
        console.log("My name is " + name);
    };
}
var someone = new Person("Tom");
someone.name = "Jerry";
someone.introduceSelf(); // Output My name is Tom
// The actual name attribute used by introduceSelf has been encapsulated and cannot be accessed outside the Person constructor
// name is equivalent to a private member property of the Person class

Class inheritance of JavaScript

Class inheritance actually only needs to implement:

  1. The object of the subclass has all the member properties defined by the parent class;
  2. Any constructor of a subclass must call the constructor of the parent at the beginning.

The way to achieve point 2 is more intuitive. And how to achieve point 1? In fact, we only need to make the prototype of the prototype property of the constructor of the subclass (prototype of the instance object of the subclass) be the prototype property of the constructor of the parent class (prototype of the instance object of the parent class). In short, we need to take the prototype of the instance of the parent class as the prototype of the prototype of the instance of the subclass. In this way, when accessing the properties of the instance object of the subclass, JavaScript will find the member properties specified by the subclass along the prototype chain, and then find the member properties specified by the parent class. Moreover, the subclass can overload the member properties of the parent class in the prototype property of the subclass constructor.

1 code example

Here is a code example to define a Chinese Person class to inherit the Person class defined above:

function ChinesePerson(name) {
    Person.apply(this, name); // Call the constructor of the parent class
}
ChinesePerson.prototype.greet = function (other) {
    console.log(other + "Hello");
};
Object.setPrototypeOf(ChinesePerson.prototype, Person.prototype); // Set Person.prototype to the prototype of ChinesePerson.prototype

var someone = new ChinesePerson("Zhang San");
someone.introduceSelf(); // Output "My name is Zhang San"
someone.greet("Li Si"); // Output "Hello, Li Si"

If the above code defining Chinese person class uses ECMAScript 6 class declaration syntax, it will become:

class ChinesePerson extends Person {
    constructor(name) {
        super(name);
    }

    greet(other) {
        console.log(other + "Hello");
    }
}
2 code example of overloading parent class member properties

Do you think in the code example above, it's awkward to output half English and half Chinese? Let's overload the introduceSelf method with the ChinesePerson class

ChinesePerson.prototype.introduceSelf = function () {
    console.log("My name is" + this.name);
};
var someone = new ChinesePerson("Zhang San");
someone.introduceSelf(); // Output "my name is Zhang San"

var other = new Person("Ba Wang");
other.introduceSelf(); // Output My name is Ba Wang
// The overload of ChinesePerson does not affect the instance object of the parent class

Tags: Javascript Attribute ECMAScript

Posted on Fri, 06 Dec 2019 12:26:04 -0500 by nyy2000