wsjls-zw:16, object-oriented and prototype and prototype chains

Keywords: Javascript Front-end

Object-oriented

What is object-oriented programming?

Object-oriented is a programming idea that is often compared with process-oriented.

The process-oriented focus is on verbs, which analyze the steps needed to solve a problem, write functions to implement each step, and call functions in turn.

Object-oriented focuses on the subject and predicate, breaking down the things that make up the problem into individual objects, and the purpose of breaking down objects is not to achieve a certain step, but to describe the various behaviors of this thing in the current problem.

What are the characteristics of object-oriented?

Encapsulation: Let the user of the object not consider the internal implementation, only consider the use of functions to protect the internal code, leaving only some api interfaces for the user to use

Inheritance: For code reuse, methods and attributes are inherited from the parent class, and subclasses have their own attributes

Polymorphism: Different objects act on the same operation to produce different effects. Polymorphic thinking actually separates what you want to do from who does it

Examples of the process of chess are as follows:

<Process Oriented>This is the following:Start->White Chess->Board Display->Check Win or Lose->Black Chess->Board Display->Check Win or Lose->Cycle

Code representation of a possible sequence of function calls

init();

whitePlay(); //Inside for a Chess-playing operation

repaint(); //board display

check();

blackPlay(); //Do a single chess play again

repaint(); //board display

check();

<Object Oriented>This is the following:Checkerboard.Opening->Player.Chess->Checkerboard.Re-display->Checkerboard.Check wins->Players.Checkerboard->Checkerboard.Re-display->Checkerboard.Check wins and losses

Code may indicate this

const checkerBoard = new CheckerBoard(); // CheckerBoard class closes the operations of the board internally, such as initializing the board, checking the winning-losing relationship, etc.

Const whitePlayer = new Player ('white'); the // Player class encapsulates the actions of various players, such as waiting, losing, repentance

const blackPlayer = new Player('black');

whitePlayer.start(); // start method end, internally encapsulated or triggered calls to checkerBoard.repaint(), checkerBoard.check() via event Publishing

blackPlayer.start();

You just need to call a new player and then the start method, which means we just need to focus on the behavior and not know what's going on inside.

And if you want to add some new features, such as Regret Chess, such as another player, object-oriented is very good to expand.

In the example above, how does the object-oriented feature work?

Encapsulation: Player, CheckerBoard class, use without knowing what's implemented internally, just consider exposed api usage

Inheritance: Both whitePlayer and blackPlayer inherit from Player and can use Player's methods and properties directly

Polymorphism: The colors of whitePlayer.start() and blackPlayer.start() are white and black, respectively.

When is object-oriented appropriate

It can be seen that in the face of more complex problems, or when there are more participants, object-oriented programming ideas can simplify the problem well, and can be better extended and maintained.

In the face of simpler problems, the difference between object-oriented and process-oriented is not obvious, and can be invoked step by step.

Object-oriented in Js

What does an object contain

Method, Property

Some built-in objects

Object Array Date Function RegExp

create object

1. Common way

Each new object rewrites the assignments of color and start

const Player = new Object();

Player.color = "white";

Player.start = function () {

  console.log("white Play chess");

};

Or factory mode, neither of which recognizes object types, such as Player's type being Object only

function createObject() {

  const Player = new Object();

  Player.color = "white";

  Player.start = function () {

    console.log("white Play chess");

  };

  return Player;

}

2. Constructor/Instance

Attributes and methods added through this always point to the current object, so when instantiating, the attributes and methods added through this copy in memory, which results in [memory waste].

But the advantage of creating this is that even if you change the properties or methods of one object, it doesn't affect other objects (because each object is a copy)

function Player(color) {

  this.color = color;

  this.start = function () {

    console.log(color + "Play chess");

  };

}

const whitePlayer = new Player("white");

const blackPlayer = new Player("black");

Tips.How do you see if a function has been created in memory many times?

For example, in the constructor, we can see whitePlayer.start == blackPlayer.start//output false

3. Prototype

The method of inheritance through a prototype is not its own. We look one level at the prototype chain, so the benefit of creating it is that it is only created once in memory, and the instantiated objects point to the prototype object.

function Player(color) {

  this.color = color;

}

Player.prototype.start = function () {

  console.log(color + "Play chess");

};

const whitePlayer = new Player("white");

const blackPlayer = new Player("black");

4. Static Properties

Is an attribute method bound to a constructor and needs to be accessed through the constructor

For example, we want to see an example of how many players were created in total

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

Can be cached, can be a public object on the parent

Prototype Prototype Chain

What are the benefits of adding attributes or methods to a prototype?

Without prototyping, every time a new object is generated, a new storage space is opened up in memory, and when there are more objects, performance becomes poor.

But through

Player.prototype.xx = function () {};

Player.prototype.xx = function () {};

Adding attributes or methods to a prototype object in this way can be cumbersome. So we can write like this

Player.prototype = {

  start: function () {

    console.log("Play chess");

  },

  revert: function () {

    console.log("Regret Chess");

  },

};

How do I find Player's prototype object?

function Player(color) {

  this.color = color;

}

Player.prototype.start = function () {

  console.log(color + "Play chess");

};

const whitePlayer = new Player("white");

const blackPlayer = new Player("black");

console.log(blackPlayer.__proto__); // Player {}

console.log(Object.getPrototypeOf(blackPlayer)); // Player {}, can get u through Object.getPrototypeOf proto_u

console.log(Player.prototype); // Player {}

console.log(Player.__proto__); // [Function]

  Prototype flowchart

What the new keyword does

1. A new object whitePlayer inherited from Player.prototype was created

2. whitePlayer. u Proto_u Point to Player.prototype, whitePlayer. u Proto_u = Player.prototype

3. Point this to the newly created object whitePlayer

4. Return a new object

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

    4.2 Return this if the constructor has an explicit return value and is a base type, such as number,string,boolean

    4.3 If the constructor has an explicit return value, which is the object type, such as {a:1}, then it returns this object {a:1}

Handwriting a new function

// 1. A new object obj is created as new Object()

// 2. Take out the first parameter, which is the constructor we want to pass in. Moreover, because shift modifies the original array, arguments are stripped of the first parameter

// 3. Point obj's prototype to the constructor so that OBJ can access the properties in the constructor's prototype

// 4. Use apply to change the direction of the constructor this to the newly created object so that obj can access the properties in the constructor

// 5. Return to obj

function objectFactory() {

  let obj = new Object();

  let Constructor = [].shift.call(arguments); // Pseudo-array These two rows can be generated with Object.create, let Constructor = Object.create (passed in constructor.prototype);

  obj.__proto__ = Constructor.prototype;

  let ret = Constructor.apply(obj, arguments);

  return typeof ret === "object" ? ret : obj;

}

Note Object.create(null) is cleanest when creating objects

What is the prototype chain

When reading the properties of an instance, if it cannot be found, it will look for the properties in the prototype associated with the object, and if not, it will look for the prototype of the prototype until it reaches the top level.

For instance

function Player() {}

Player.prototype.name = "Kevin";

var p1 = new Player();

p1.name = "Daisy";

// Find the name attribute in the p1 object, because name is added above, so the output "Daisy"

console.log(p1.name); // Daisy

delete p1.name;

// Delete p1.name, then look for P1 and find that there is no name attribute, it will be from p1's prototype p1. u Proto_u Find it, Player.prototype, then find the name and output "Kevin"

console.log(p1.name); // Kevin

So if we can't find the name attribute in Player.prototype, we'll go to Player.prototype. u Proto_u To find, that is {}.

Object.prototype.name = "root";

function Player() {}

Player.prototype.name = "Kevin";

var p1 = new Player();

p1.name = "Daisy";

// Find the name attribute in the p1 object, because name is added above, so the output "Daisy"

console.log(p1.name); // Daisy

delete p1.name;

// Delete p1.name, then look for P1 and find that there is no name attribute, it will be from p1's prototype p1. u Proto_u Find it, Player.prototype, then find the name and output "Kevin"

console.log(p1.name); // Kevin

delete Player.prototype.name;

console.log(p1.name);

Such a passage through u proto_u The chain of objects to which prototype is connected is the prototype chain

inherit

Prototype Chain Inheritance

Realization

function Parent() {

  this.name = "parentName";

}

Parent.prototype.getName = function () {

  console.log(this.name);

};

function Child() {}



// Parent's instance contains both instance and prototype attribute methods, so assign new Parent() to Child.prototype.

// If only Child.prototype = Parent.prototype, then Child can only call getName, not.name.

// When Child.prototype = new Parent(), if new Child() gets an instance object child, then

// child.__proto__ === Child.prototype;

// Child.prototype.__proto__ === Parent.prototype

// This means that when accessing the properties of the child object, if it cannot be found on the child, it will go to Child.prototype and if it cannot be found, it will go to Parent.prototype to achieve inheritance.

Child.prototype = new Parent();

// Because the constructor attribute is included in the prototype and the prototype is reassigned above, it causes the Child constructor to point to [Function: Parent], which sometimes causes problems when using child1.constructor to determine the type

// To ensure the correct type, we need to point Child.prototype.constructor at his original constructor, Child

Child.prototype.constructor = Child;



var child1 = new Child();

child1.getName(); // parentName

Implicit problems

1. If an attribute is of reference type, once an instance modifies the attribute, all instances will be affected

function Parent() {

  this.actions = ["eat", "run"];

}

function Child() {}

Child.prototype = new Parent();

Child.prototype.constructor = Child;

const child1 = new Child();

const child2 = new Child();

child1.actions.pop();

console.log(child1.actions); // ['eat']

console.log(child2.actions); // ['eat']

2. Child instances cannot be passed along when they are created

Constructor Inheritance

Question 1 above, how to solve it?

Can you find a way to add attribute methods on Parent to Child? Instead of having prototype objects on them, prevent them from being shared by all instances.

###Implementation

Question 1. We can use call to copy the operation on Parent once

function Parent() {

  this.actions = ["eat", "run"];

  this.name = "parentName";

}

function Child() {

  Parent.call(this);

}

const child1 = new Child();

const child2 = new Child();

child1.actions.pop();

console.log(child1.actions); // ['eat']

console.log(child1.actions); // ['eat', 'run']

Targeting Question 2. How should we pass on the information?

function Parent(name, actions) {

  this.actions = actions;

  this.name = name;

}

function Child(id, name, actions) {

  Parent.call(this, name, actions); // If you want to pass multiple parameters directly, you can Parent.apply(this, Array.from(arguments).slice(1));

  this.id = id;

}

const child1 = new Child(1, "c1", ["eat"]);

const child2 = new Child(2, "c2", ["sing", "jump", "rap"]);

console.log(child1.name); // { actions: [ 'eat' ], name: 'c1', id: 1 }

console.log(child2.name); // { actions: [ 'sing', 'jump', 'rap' ], name: 'c2', id: 2 }

Implicit problems

Attributes or methods can only be defined in a constructor if they want to be inherited. If the method is defined within the constructor, it will be created once for each instance, taking up an extra block of memory.

function Parent(name, actions) {

  this.actions = actions;

  this.name = name;

  this.eat = function () {

    console.log(`${name} - eat`);

  };

}

function Child(id) {

  Parent.apply(this, Array.prototype.slice.call(arguments, 1));

  this.id = id;

}

const child1 = new Child(1, "c1", ["eat"]);

const child2 = new Child(2, "c2", ["sing", "jump", "rap"]);

console.log(child1.eat === child2.eat); // false

Combinatorial Inheritance

We implement basic inheritance through prototype chain inheritance, where methods exist on the prototype and subclasses can be called directly. However, attributes of reference types are shared by all instances and cannot be passed.

With constructor inheritance, we solved the two problems above: using call to repeat the creation of attributes and methods within a sub-constructor, and it can be passed.

However, a problem with constructors is that the method of repeated creation within the constructor causes an excessive memory footprint.

Prototype chain inheritance solves the problem of duplicate creation, so we combine the two approaches, called combinatorial inheritance

Realization

function Parent(name, actions) {

  this.name = name;

  this.actions = actions;

}

Parent.prototype.eat = function () {

  console.log(`${this.name} - eat`);

};

function Child(id) { // (id, ...args) Parent.apply(this, args);

  Parent.apply(this, Array.from(arguments).slice(1)); // For the first time

  this.id = id;

}

Child.prototype = new Parent(); // The second time

Child.prototype.constructor = Child;

const child1 = new Child(1, "c1", ["hahahahahhah"]);

const child2 = new Child(2, "c2", ["xixixixixixx"]);

child1.eat(); // c1 - eat

child2.eat(); // c2 - eat

console.log(child1.eat === child2.eat); // true

Implicit problems

The constructor was called twice and repeated

1. Parent.apply(this, Array.from(arguments).slice(1));

2. Child.prototype = new Parent();

Parasitic Combinatorial Inheritance

The constructor has been called twice above. Think about which step we can simplify?

We could consider giving Child.prototype indirect access to Parent.prototype

Realization

function Parent(name, actions) {

  this.name = name;

  this.actions = actions;

}

Parent.prototype.eat = function () {

  console.log(`${this.name} - eat`);

};

function Child(id) {

  Parent.apply(this, Array.from(arguments).slice(1));

  this.id = id;

}

// Simulate the effect of Object.create

// If Object.create is used directly, it can be written as Child.prototype = Object.create(Parent.prototype);

let TempFunction = function () {};

TempFunction.prototype = Parent.prototype;

Child.prototype = new TempFunction();



Child.prototype.constructor = Child;

const child1 = new Child(1, "c1", ["hahahahahhah"]);

const child2 = new Child(2, "c2", ["xixixixixixx"]);

Perhaps some students will ask why Child.prototype has to be accessed to Parent.prototype through a bridge?

Can't direct Child.prototype = Parent.prototype?

A: No!!

Let's have a look

function Parent(name, actions) {

  this.name = name;

  this.actions = actions;

}

Parent.prototype.eat = function () {

  console.log(`${this.name} - eat`);

};

function Child(id) {

  Parent.apply(this, Array.from(arguments).slice(1));

  this.id = id;

}

Child.prototype = Parent.prototype;



Child.prototype.constructor = Child;

console.log(Parent.prototype); // Child { eat: [Function], childEat: [Function] }

Child.prototype.childEat = function () {

  console.log(`childEat - ${this.name}`);

};

const child1 = new Child(1, "c1", ["hahahahahhah"]);

console.log(Parent.prototype); // Child { eat: [Function], childEat: [Function] }

You can see that when you add a new attribute or method to Child.prototype, the Parent.prototype also changes, which is not what we want to see.

ES6 Inheritance

class Parent {

    constructor() {

        this.name = 'aaa';

    }

    getName() {

        console.log('getname');

    }

}

class Child extends Parent {

    constructor() {

        super();

    }

}

const p1 = new Child();

p1.getName();


Learning makes people progress, will repeated learning make them progress?

Posted by blueman378 on Thu, 02 Dec 2021 09:03:54 -0800