object-oriented

What is object orientation?

  • Object orientation is an idea that is often compared to process orientation

  • Process-oriented: focus on the verb, is to analyze the steps needed to solve the problem, and then write corresponding functions to achieve each step, call the function in turn.

  • Object oriented: object focuses on subject and predicate, is to disassemble the things that constitute the problem into various objects, and the purpose of disassembling the object is not to achieve a certain step, but to describe the behavior of the thing in the current problem.

I. Three characteristics of object orientation:

  1. Encapsulation: let the user not consider the internal implementation, only consider the use of functionality, the internal code is protected, only set aside some API for users to use;
  • Class: Encapsulates the properties and behavior of an object
  • Method: Encapsulate specific logical functions
  • Access encapsulation: Access decorator encapsulation is nothing more than encapsulation of its access permissions
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getInfo() {
        let { name, age } = this;
        return { name, age }
    }
    static test() {
        console.log('hello'); }}let lisi = new Person('zhangsan'.18);
lisi.test(); // Error test is not a method
lisi.getInfo();// {name: 'zhangsan', age: '18'}
Person.test(); // hello
Copy the code
  • The public properties that are pulled out of person are name,age, Public methods include getInfo and test. The difference between getInfo and test is that test uses the static modifier and is static. Test belongs only to the person class, whereas getInfo belongs to the instance. Static encapsulates access to the test method.
  1. Inheritance: Methods and attributes are inherited from the parent class for code use, and subclasses have their own attributes
  class Person{
      constructor(name) {
          this.name = name;
      }
      getName() {
          console.log(this.name);
      }
      static test() {
          console.log('hello'); }}class Qianduan extends Person {
     constructor(name) {
         super(name);
     }
     work() {
         console.log('Do front-end work'); }}let qianduan = newQianduan (" zhangsan "); Qianduan.getName(); Qianduan.work();// Qianduan.test(); // Qianduan.test is not a function
Copy the code
  • As you can see from the above example, static methods that inherit from their parent class inherit only the public properties and methods of their parent class.
  • The subclass inherits both the getName method and the work method
  1. The idea of polymorphism is to separate what you want to do from who will do it.
  • This literally means multiple states, allowing a pointer of a subclass type to be assigned to a pointer of a parent type;

  • The representation of polymorphism is overridden

    1. Rewrite, the subclass can inherit the method in the parent class, but do not need to rewrite the same method, but sometimes the subclass does not want to inherit the method of the parent class, but want to do some modification, which needs to use method rewrite, method rewrite is also known as method overwrite;
class Person {
    constructor(name) {
        this.name = name;
    }
    test() {
        console.log('hello');
    }
    getName() {
        console.log(this.name); }}class Qianduan extends Person {
    constructor(name) {
        super(name);
    }
    test() {
        console.log('Hello, my name isThe ${this.name}I do front-end work); }}const person = new Person('zhangsan');
const qianduan = newQianduan (" lisi "); person.test();// hello
qianduan.test(); // Hello, my name is Lisi, I do front-end work
person.getName();// zhangsan
qianduan.getName(); // lisi
Copy the code
  • What is an overload? An overload is a function or method that has the same name but has different argument lists. These arguments or methods are called overloaded functions or methods.
class Person {
    constructor(arg) {
        let obj = null;
        switch (typeof arg) {
            case 'string':
                obj= new StringPerson(arg);
                break;
            case 'object':
                obj = new ObjPerson();
                break;
            case"Number" : obj =new NumberPerson();
                break;
        }
        returnobj; }}class ObjPerson {
    constructor() {
        console.log('ObjPerson'); }}class StringPerson {
    constructor() {
        console.log('StringPerson'); }}class NumberPerson {
    constructor() {
        console.log('NumberPerson'); }}new Person({}); // ObjPerson
new Person('123456'); // StringPerson
new Person(987654); // NumberPerson

Copy the code

Take, for example, the process of playing chess.

In the idea of procedural programming

Opening -> White chess -> board display -> Check win/lose -> Black chess -> board display -> Check -> Win/lose -> Loop

It may be a series of function calls represented in code

init();

whitePlay(); // create a chessboard

repaint(); // board display

check();

blackPlay(); // Play chess again

repaint(); // board display

check();

Object orientation looks like this: checkerboards. Opening -> player. Chess -> board. Redisplay -> board. Check the winner -> players. Play chess -> board. Redisplay -> board. Check the outcome

It might look something like this in code

constcheckerBoard = new CheckerBoard(); // The CheckerBoard class internally registers CheckerBoard operations, such as initializing the board, checking for winners and losers, etc

const whitePlayer = newPlayer (" white ");// The Player class encapsulates various Player actions, such as wait, drop, and regret

const blackPlayer = newPlayer (" black "); whitePlayer.start();// The end of the start method, encapsulated internally or triggered by event publishingCheckerboard.repaint (), checkerboard.check () calls blackplayer.start ();Copy the code

You just call a new player, and then you call the start method, which means we only care about the behavior, we don’t need to know what’s going on inside.

And if you want to add some new features, like regret, like adding another player, object-oriented is very easy to expand.

In the example above, how do object-oriented features come out?

  • Encapsulation: The Player, CheckerBoard class, you don’t need to know what’s being implemented internally, you just need to think about the use of the exposed API
  • Inheritance: Both whitePlayer and blackPlayer inherit from Player, and both can directly use the various methods and properties of Player
  • Polymorphisms: Whiteplayer.start () and blackplayer.start () play chess in white and black colors, respectively

When is it appropriate to use object orientation

Create object js object oriented

  • Objects include methods, properties, and some built-in objects (Object, Array, Date, Function, RegExp)
1. Common mode
  • As in the chess example above, the color and start assignments are rewritten for each new object
const Player = new Object(a); Player.color ="white";
Player.start = function () {
console.log("White chess"); };

// or factory mode, neither of which recognizes the object type. For example, the Player type is just Object
function createObject() {
    const Player = new Object(a); Player.color ="white";
    Player.start = function () {
    console.log("White chess"); };
    return Player;
}
Copy the code
2. Constructor/instance
  • Properties and methods added through this always refer to the current object, so any properties and methods added through this are copied in memory at instantiation time, resulting in a waste of memory. But the advantage of this creation is that even if you change the properties or methods of one object, it does not affect other objects;
function Player(color) {
    this.color = color;
    this.start = function () {
    console.log(color + "Chess"); };
}
const whitePlayer = new Player("white");
const blackPlayer = new Player("black");
Copy the code
3. The prototype
  • The method of passing through prototype inheritance is not self-contained. We look up the prototype chain layer by layer. The advantage of this method is that we only create objects in memory once, and instantiated objects refer to the Prototype object.
function Player(color) {
    this.color = color;
}
Player.prototype.start = function () {
    console.log(color + "Chess"); 
};
const whitePlayer = new Player("white");
const blackPlayer = new Player("black");
Copy the code
4. Static attributes
  • Is a property square method bound to a constructor that needs to be accessed through the constructor
function Player(color) {
    this.color = color;
    if(! Player.total) { Player.total =0;
    }
    Player.total++;
}
let p1 = new Player("white");
console.log(Player.total); / / 1
let p2 = new Player("black");
console.log(Player.total); / / 2
Copy the code

Prototype and prototype chain

What are the benefits of adding attributes or methods in the prototype?
  • Without prototyping, each new object is created to create a new piece of memory. As the number of objects increases, performance deteriorates.
Person.prtotype.x = function() {};
Person.prtotype.y = function() {};
Person.prtotype.z = function() {};

// The above performance is very poor, can be used as an example
Person.prtotype = {
    x: function() {
        console.log('x');
    },
    y: function() {
        console.log('y');
    },
    z: funciton() {
        console.log('z'); }}Copy the code
How do I find the Player prototype object?

function Player(color) {
    this.color = color;
}
Player.prototype.start = function () {
    console.log(color + "Chess"); };
    const whitePlayer = new Player("white");
    const blackPlayer = new Player("black");
    console.log(blackPlayer.__proto__); // Player {} console.log(Object.getPrototypeOf(blackPlayer)); // Player {}, can be object.getProtoTypeof to get __proto__
    console.log(Player.prototype); // Player {} console.log(Player.__proto__); // [Function]
Copy the code

Look at the prototype flowchart:

What does the new keyword do

  1. A new object, whitePlayer, inherited from Player.prototype, is created

  2. Proto = player. prototype 3. Point this to the newly created object, whitePlayer

  3. Return a new object

    4.1 Return this if the constructor does not explicitly return a value

    4.2 If the constructor returns an explicit value of a primitive type, such as number,string, or Boolean, return this

    4.3 Return the object {a: 1} if the constructor has an explicit return value of an object type, such as {a: 1}.

Implement a new function

// 1. Create Object obj with new Object()
// 2. Take the first argument, which is the constructor we pass in. Additionally, since Shift modifies the array, arguments is removed from the first argument
// 3. Point obj's prototype to the constructor so that obj can access properties in the constructor prototype
Change the reference of the constructor this to the new object using apply, so that obj can access the properties in the constructor
// 5. Return obj

function objectFactory() {
    let obj = new Object(a);let Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype;
    let ret = Constructor.apply(obj, arguments);
    return typeof ret === "object" ? ret : obj;
}
Copy the code

What is the prototype chain?

  • We all know that when we read an instance property, if we can’t find it, we look for the property in the stereotype associated with the object, and if we can’t find it, we look for the stereotype, all the way to the top.
 function Player() {} Player. Prototype. Name = "lisi";var p1 = newPlayer(); P1. Name = "zhangsan";// select * from p1 where name = 'zhangsan';
 console.log(p1.name); // zhangsan
 
 delete p1.name;
 P1.__proto__ (prototype, Player. Prototype, name = "zhangsan"); // Select * from p1 where name = "name"; // Select * from p1 where name = "name";
console.log(p1.name); // zhangsan

Copy the code

If we don’t find the name attribute in player.prototype, we’ll look in Player.prototype.proto. That is {}

Object. Prototype. Name = 'root';function Player() {}
Player.prototype.name = "lisi";

var p1 = new Player();
p1.name = "zhansan";
// select * from p1 where name = 'zhansan'
console.log(p1.name); // zhansan
delete p1.name;
/ / remove the p1. The name, and then find the p1 found no name attribute, from p1 prototype p1. To find __proto__, namely Player. The prototype, and then found out the name, output "lisi"
console.log(p1.name); // lisi
delete Player.prototype.name;
console.log(p1.name);

Copy the code

The chain of objects connected by Proto and Prototype is the prototype chain

inheritance

One. Prototype chain inheritance

  • Each constructor has a prototype object, the prototype object contains a pointer to the constructor, and each instance contains a pointer to the prototype object. The essence of inheritance is copying, or rewriting the prototype object to replace a new type of instance.
function Parent() {
    this.name = 'ParentName';
    this.actions = ['sing'.'jump'.'rap'];
}

function Child() {}

Child.prototype = new Parent();
Child.prototype.constructor = Child;

const c1 = new Child();
c1.actions.push('basketball');
console.log(c1.actions); //[ 'sing', 'jump', 'rap', 'basketball' ]
const c2 = new Child();
console.log(c2.actions); // [ 'sing', 'jump', 'rap' ]

Copy the code

Borrow constructor inheritance

  • The superclass constructor is called inside the subtype constructor, which can be executed on the newly created object by using the call() and apply() methods.
function Parent(name, color) {
    this.name = name;
    this.color = color;
    this.actions = ['sing'.'jump'.'rap'];
    this.eat = function () {}}function Child() {
    Parent.apply(this.arguments);
}

const c1 = new Child('c1'.'red');
const c2 = new Child('c2'.'white');

console.log(c1.eat === c2.eat);
Copy the code

Disadvantages:

  • Only instance properties and methods of the parent class can be inherited, not stereotype properties/methods
  • Unable to reuse, each subclass has a copy of the parent class instance function, affecting performance

3. Combinatorial inheritance

  • An inheritance pattern that combines a stereotype chain with the techniques of borrowing constructors to take advantage of both.
// Prototype chain inheritance + constructor inheritance
// 1. The reference type is changed and shared by all instances
// 2. The parameter cannot be transmitted
// 3. It takes up too much memory

function Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.getName = function () {
    console.log(this.name + 'getName called');
}

function Child() {
    Parent.apply(this.arguments); // Call the constructor for the first time
}

Child.prototype = new Parent(); // Call the constructor a second time
Child.prototype.constructor = Child;

const c1 = new Child('c1'['eat']);
const c2 = new Child('c2'['run']);
Copy the code

Iv. Original type inheritance

  • Stereotypes allow you to create new objects based on existing objects without having to create custom types.
function CreateObj(o){
    function F(){}
    F.prototype = o;
    console.log(o.__proto__ === Object.prototype);
    console.log(F.prototype.constructor === Object); // true
    return new F();
}

var person = {
    name: 'xiaopao'.friend: ['daisy'.'kelly']}var person1 = CreateObj(person);

// var person2 = CreateObj(person);

person1.name = 'person1';
// console.log(person2.name); // xiaopao
person1.friend.push('taylor');
// console.log(person2.friend); // ["daisy", "kelly", "taylor"]
// console.log(person); // {name: "xiaopao", friend: Array(3)}
person1.friend = ['lulu'];
// console.log(person1.friend); // ["lulu"]
// console.log(person.friend); // ["daisy", "kelly", "taylor"]
/ / note: Person1. name='person1'; person2.name ='person1'; person1.name='person1'; You did not change the name value on the stereotype
// Because when we look for properties on objects, we always look for properties on instance objects first, and then look for properties on prototype objects. If there is a property of the same name on the instance object and the prototype object, the value on the instance object is always taken first

Copy the code

Disadvantages:

  • The prototype chain inherits multiple instances of reference type attribute pointing to the same, there is the possibility of tampering.
  • Unable to pass parameters

Parasitic inheritance

  • Create a function that simply encapsulates the inheritance process, enhances the object internally in some way, and then returns the object as if it really did all the work.
 var ob = {
    name: 'xiaopao'.friends: ['lulu'.'huahua']}function CreateObj(o){
    function F(){};  // create a constructor F
    F.prototype = o;
    return new F();
}

The CreateObj function has a new specification for ECMAScript5. Object. Create (ob) has the same effect
var ob1 = CreateObj(ob);
var ob2 = Object.create(ob);
console.log(ob1.name); // xiaopao
console.log(ob2.name); // xiaopao

function CreateChild(o){
    var newob = CreateObj(o); // Create an Object or use var newob = object.create (ob)
    newob.sayName = function(){ // Enhance objects
        console.log(this.name);
    }
    return newob; // Specify the object
}

var p1 = CreateChild(ob);
p1.sayName(); // xiaopao 
Copy the code

Disadvantages (same as the original type inheritance) :

  • The prototype chain inherits multiple instances of reference type attribute pointing to the same, there is the possibility of tampering.
  • Unable to pass parameters

Parasitic combinatorial inheritance

  • Remove instance attributes of the parent class in a parasitic manner, so that when a construct of the parent class is called twice, no two instance methods/attributes are initialized, avoiding the disadvantages of combinatorial inheritance
// Prototype chain inheritance + constructor inheritance
// 1. The reference type is changed and shared by all instances
// 2. The parameter cannot be transmitted
// 3. It takes up too much memory

function Parent(name, actions) {
    this.name = name;
    this.actions = actions;
}

Parent.prototype.getName = function () {
    console.log(this.name + 'getName called');
}

function Child() {
    Parent.apply(this.arguments);
}

// Child.prototype = Parent.prototype; // If you change child-prototype, parent-prototype will also be modified.
Child.prototype = Object.create(Parent.prototype);
// Child.prototype = new Parent();
// let TempFunction = function () {};
// TempFunction.prototype = Parent.prototype;
// Child.prototype = new TempFunction();

Child.prototype.constructor = Child;



// super() Child

const c1 = new Child('c1'['eat']);
const c2 = new Child('c2'['run']);
Copy the code

ES6 Class inheritance

class person {
    constructor(){
        this.kind="person"
     }
    eat(food){
        console.log(this.name+‘ ’+food);
    }
}

class student extends person{
     constructor(name){
         super(a);this.name=name; }}var martin = newStudent (" Martin ");console.log(martin.kind); //personMartin. Eat (" apple ");//martin apple
Copy the code

There are eight inheritance schemes commonly used in JavaScript