JS basic prototype and inheritance

Posted by MFKR on Sun, 20 Feb 2022 04:37:51 +0100

If you are Xiaobai, this set of information can help you become a big bull. If you have rich development experience, this set of information can help you break through the bottleneck
2022web full set of video tutorial front-end architecture H5 vue node applet Video + data + code + interview questions.

Reading catalog

Prototype basis

Prototype object

Each object has a prototype prototype object, and the object created through the function will also have this prototype object.
A prototype is a pointer to an object.

  1. The prototype can be understood as the father of the object, and the object inherits properties from the prototype object
  2. A prototype is an object. It has nothing special except that it is the parent of an object
  3. The prototype of all functions is the instance of Object by default, so the reason why toString / toValues / isPrototypeOf and other methods can be used.
  4. Use prototype objects to share properties or methods for multiple objects
  5. If the object itself does not have properties or methods, it will be looked up on the prototype
  6. Using the prototype can solve the problem of memory occupation caused by copying multiple functions when creating objects by building functions
  7. The prototype contains the constructor attribute, which points to the constructor
  8. Object contains__ proto__ Point to his prototype object

Use the concat method of the array prototype object to complete the connection operation

The following example uses the concat method of the array prototype object to complete the connection operation.

let hd = ["a"];
console.log(hd.concat("b"));
console.log(hd);

By default, all objects created have prototypes.

let hd = { name: "wgchen" };
console.log(hd);

The following prototypes of x and y are meta Object objects, that is, the root Object in JS

let x = {};
let y = {};
console.log(Object.getPrototypeOf(x) == Object.getPrototypeOf(y)); //true
// Object. The getprototypeof () method returns the prototype of the specified object (that is, the value of the internal property). [[Prototype]]

Create a minimalist object (pure data dictionary object) without a prototype (the prototype is null)

We can also create a minimalist object (pure data dictionary object) without a prototype (the prototype is null)

// The hasOwnProperty() method returns a Boolean value indicating whether the object has a specified property (that is, whether there is a specified key) in its own properties.

let hd = { name: 3 };
console.log(hd.hasOwnProperty("name")); // true

let xj = Object.create(null, {
  name: {
    value: "wgchen"
  }
});

console.log(xj.hasOwnProperty("name"));
// Uncaught TypeError: xj.hasOwnProperty is not a function

//Object.keys is a static method, not a prototype method, so it can be used
console.log(Object.keys(xj));


console.log(xj);

The function has multiple prototypes, and the prototype is used for instance objects__ proto__ Use for function objects

function User() {}

User.__proto__.view = function() {
  console.log("User function view method");
};

User.view();

User.prototype.show = function() {
  console.log("wgchen");
};

let hd = new User();
hd.show();

console.log(User.prototype == hd.__proto__);

Prototype relationship analysis, and examples of method inheritance

let hd = new Object();
hd.name = "wgchen";

Object.prototype.show = function() {
  console.log("blog");
};
hd.show();

function User() {}
let xj = new User();
xj.show();

User.show();

Use constructors to create prototypes of objects

  1. Constructors have prototypes
  2. When creating an object, the constructor assigns the prototype to the object

function User() {}
let xj = new User();
console.log(xj.__proto__ == User.prototype); // true

The following use of arrays will produce multi-level inheritance, that is, prototype chain

let hd = [];
console.log(hd);
console.log(hd.__proto__ == Array.prototype);

let str = "";
console.log(str.__proto__ == String.prototype);

Next, use setPrototypeOf and getPrototypeOf to get and set the prototype

let hd = {};
let parent = { name: "parent" };
Object.setPrototypeOf(hd, parent);

console.log(hd);
console.log(Object.getPrototypeOf(hd));


A prototype representation of an object created using a custom constructor

function User() {}
let hd = new User();
console.log(hd);

The constructor exists in the prototype and is used to point to the reference of the build function.

function hd() {
  this.show = function() {
    return "show method";
  };
}

const obj = new hd(); 
console.log(obj instanceof hd); //true

const obj2 = new obj.constructor();
console.dir(obj2.show()); //show method

Create an object using the object's constructor

function User(name, age) {
  this.name = name;
  this.age = age;
}

function createByObject(obj, ...args) {
  const constructor = Object.getPrototypeOf(obj).constructor;
  return new constructor(...args);
}

let hd = new User("wgchen");
let xj = createByObject(hd, "willem", 12);

console.log(xj);

Prototype chain

The steps to implement inheritance are to inherit the properties and methods of another reference type through the prototype of the reference type.


Use object Setprototypeof can set the prototype of the object. In the following example, the inheritance relationship is obj > HD > CMS.

Object.getPrototypeOf is used to get the prototype of an object.

let obj = {
  name: "wgchen"
};

let hd = {
  web: "blog"
};

let cms = {
  soft: "willem"
};

//Let obj inherit hd, that is, set the prototype of obj to hd
Object.setPrototypeOf(obj, hd);
Object.setPrototypeOf(hd, cms);

console.log(obj.web); // blog
console.log(Object.getPrototypeOf(hd) == cms); //true

Prototype testing

instanceof detects whether the prototype attribute of the constructor appears on the prototype chain of an instance object.

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

const c = new C();
B.prototype = c;

const b = new B();

A.prototype = b;
const a = new A();

console.dir(a instanceof A); //true
console.dir(a instanceof B); //true
console.dir(a instanceof C); //true
console.dir(b instanceof C); //true
console.dir(c instanceof B); //false

Use isPrototypeOf to detect whether one object is in the prototype chain of another object

const a = {};
const b = {};
const c = {};

Object.setPrototypeOf(a, b);
Object.setPrototypeOf(b, c);

console.log(b.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(b)); //true

Property traversal

Use in to detect whether there is a property on the prototype chain, and use hasOwnProperty to detect only the current object.

let a = { url: "blog" };
let b = { name: "wgchen" };
Object.setPrototypeOf(a, b);

console.log("name" in a); // true
console.log(a.hasOwnProperty("name")); // false
console.log(a.hasOwnProperty("url")); // true

When using for/in traversal, the attributes on the prototype will be traversed at the same time, as shown in the following example

let hd = { name: "wgchen" };
let xj = Object.create(hd, {
  url: {
    value: "blog",
    /*
    The enumerable attribute is one of four properties:
    Whether the object attribute can pass through the for in loop, or object Keys() read
    */
    enumerable: true
  }
});

for (const key in xj) {
  console.log(key);
}


The hasOwnProperty method determines whether an object has properties, rather than looking for prototypes.
So if you just want to traverse object properties, use the following code

let hd = { name: "wgchen" };
let xj = Object.create(hd, {
  url: {
    value: "blog",
    enumerable: true
  }
});

for (const key in xj) {
  if (xj.hasOwnProperty(key)) {
    console.log(key); // url
  }
}

Borrow prototype*

Using call or apply, you can borrow other prototype methods to complete functions.

The following xj objects cannot use the max method, but you can borrow the prototype method of hd objects.

let hd = {
  data: [1, 2, 3, 4, 5]
};

Object.setPrototypeOf(hd, {
  max: function() {
    return this.data.sort((a, b) => b - a)[0];
  }
});
console.log(hd.max()); // 5

let xj = {
  lessons: { js: 100, php: 78, node: 78, linux: 125 },
  get data() {
    return Object.values(this.lessons);
  }
};
console.log(hd.__proto__.max.apply(xj));// 125

In the above example, if the method can pass parameters, you can not define the get method in the xj object.

let hd = {
  data: [1, 2, 3, 4, 5]
};

Object.setPrototypeOf(hd, {
  max: function(data) {
    return data.sort((a, b) => b - a)[0];
  }
});
console.log(hd.max(hd.data)); // 5

let xj = {
  lessons: { js: 100, php: 78, node: 78, linux: 125 }
};
console.log(hd.__proto__.max.call(xj, Object.values(xj.lessons))); // 125

Because of math Max is the way to get the maximum value, so the code can be optimized again

let hd = {
  data: [1, 2, 3, 4, 5]
};
console.log(Math.max.apply(null, Object.values(hd.data))); // 5

let xj = {
  lessons: { js: 100, php: 78, node: 78, linux: 125 }
};
console.log(Math.max.apply(xj, Object.values(xj.lessons))); // 125

The following is the button to get and set the class attribute, but the DOM node cannot directly use the filter and other methods of the array, but it can be operated by borrowing the prototype method of the array.

<body>
  <button message="wgchen" class="red">wgchen but</button>
  <button message="blog">blog but</button>
</body>
<script>
  let btns = document.querySelectorAll("button");
  btns = Array.prototype.filter.call(btns, item => {
    return item.hasAttribute("class");
  });
</script>

this is not affected by prototype inheritance

this refers to the object used when calling the property.

let hd = {
  name: "wgchen"
};

let ycc = {
  name: "willem",
  show() {
    return this.name;
  }
};

hd.__proto__ = ycc;
console.log(hd.show()); // wgchen

Prototype summary

prototype

Functions are also objects and prototypes. Functions have a prototype attribute pointing to their prototypes

The prototype set for the constructor refers to the prototype given to the object when the constructor is used to create the object

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

User.prototype = {
  show() {
    return this.name;
  }
};

let xj = new User("wgchen");
console.log(xj.show()); // wgchen

The default prototype of the function refers to the object containing a property constructor, which points to the current constructor

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

let xj = new User("wgchen"); // User {name: 'wgchen'}

console.log(xj);
console.log(User.prototype.constructor == User); //true
console.log(xj.__proto__ == User.prototype); //true

let lisi = new xj.constructor("Li Si");
console.log(lisi.__proto__ == xj.__proto__); //true

Saving reference types in the prototype will cause objects to share properties, so generally only methods will be defined in the prototype.

function User() {}

User.prototype = {
  lessons: ["JS", "VUE"]
};

const lisi = new User();
const wangwu = new User();

lisi.lessons.push("CSS");

console.log(lisi.lessons); //["JS", "VUE", "CSS"]
console.log(wangwu.lessons); //["JS", "VUE", "CSS"]

Adding a method to an Object prototype Object will affect all functions

<body>
  <button onclick="this.hide()">wgchen</button>
</body>
<script>
  Object.prototype.hide = function() {
    this.style.display = "none";
  };
</script>

After understanding the prototype, you can add methods to the system object, such as adding a truncation function to the string.

Prototypes of system objects cannot be assigned directly

String.prototype.truncate = function (len = 5) {
	return this.length <= len ? this : this.substr(0, len) + '...';
}
console.log('People are not machines. They are tired sometimes'.truncate(3)); //People are not

Object.create

Use object Create when creating a new object, use the existing object as the prototype object of the new object.

Use object Create set object prototype

let user = {
  show() {
    return this.name;
  }
};

let hd = Object.create(user);
hd.name = "wgchen";
console.log(hd.show()); // wgchen

When setting, use the second parameter to set the properties of the new object

let user = {
  show() {
    return this.name;
  }
};
let hd = Object.create(user, {
  name: {
    value: "wgchen"
  }
});
console.log(hd); // {name: 'wgchen'}

__proto__

Exists on the instantiated object__ proto__ The prototype is recorded, so the properties or methods of the prototype can be accessed through the object.

  • __ proto__ It is not an object attribute. It is understood as the get/set implementation of prototype. It is a non-standard definition.
  • __ proto__ Internally use get/set control value, so only objects or null s are allowed
  • It is recommended to use object Setprototypeof and object Getprottoeypof substitution__ proto__

Next, modify the of the object__ proto__ It won't succeed because_ proto__ Internally use get/set control value, so only objects or null s are allowed

let xj = {};
xj.__proto__ = "willem";
console.log(xj);


Defined below__ proto__ Will succeed, because this is a minimalist object, there is no prototype object, so it will not affect__ proto__ Assignment.

let hd = Object.create(null);
hd.__proto__ = "wgchen";
console.log(hd);

Next, by changing the object's__ proto__ Prototype object to realize inheritance. Inheritance can realize multiple layers,

let hd = {
  name: "wgchen"
};

let ycc = {
  show() {
    return this.name;
  }
};

let xj = {
  handle() {
    return `user: ${this.name}`;
  }
};

ycc.__proto__ = xj;
hd.__proto__ = ycc;

console.log(hd.show());
console.log(hd.handle());
console.log(hd);


In constructor__ proto__ use

function User(name, age) {
  this.name = name;
  this.age = age;
}

User.prototype.show = function () {
	return `full name:${this.name},Age:${this.age}`;
};

let lisi = new User('Li Si', 12);
let xiaoming = new User('Xiao Ming', 32);

console.log(lisi.__proto__ == User.prototype); //true


console.log(lisi);
console.log(xiaoming);


Can use__ proto__ Or object Setprototypeof sets the prototype of the object,
Use object Getprottoeypof gets the object prototype.

function Person() {
  this.getName = function() {
    return this.name;
  };
}

function User(name, age) {
  this.name = name;
  this.age = age;
}

let lisi = new User("Li Si", 12);
Object.setPrototypeOf(lisi, new Person());
console.log(lisi.getName()); //Li Si

Setting the properties of an object only modifies the properties of the object and does not modify the properties of the prototype. Using hasOwnProperty to judge whether the object itself contains properties does not detect the prototype.

function User() {}
const lisi = new User();
const wangwu = new User();

lisi.name = "Xiao Ming";
console.log(lisi.name); // Xiao Ming
console.log(lisi.hasOwnProperty("name")); // true

//After modifying prototype properties
lisi.__proto__.name = "Zhang San";
console.log(wangwu.name); // Zhang San

//After deleting object properties
delete lisi.name;
console.log(lisi.hasOwnProperty("name")); // false
console.log(lisi.name); // Zhang San

Using in will detect the prototype and object, while hasOwnProperty only detects the object, so you can judge whether the property is in the prototype after combination.

function User() {
}

User.prototype.name = "wgchen";

const lisi = new User();

//In will be detected in the prototype
console.log("name" in lisi); // true

//hasOwnProperty detects object properties
console.log(lisi.hasOwnProperty("name")); // false

Use suggestions

Through the previous introduction, we know that prototypes can be set up in many ways. The following is the chronological order

  1. Prototype property of prototype constructor
  2. Object.create specifies the prototype when the object is created
  3. __ proto__ Declare custom non-standard attributes and set prototypes. Before solving, use object Create defines the prototype without providing an acquisition method
  4. Object.setPrototypeOf set object prototype

These methods can manage prototypes. Generally speaking, in my personal situation, I use prototype to change the constructor prototype and object Setprototypeof and object Getprototypeof gets or sets the prototype.

Constructor

Prototype properties

When the constructor is new, it assigns the prototype of the constructor to the new object.
If there are properties in the object, the object properties will be used and the lookup method on the prototype will no longer be used.

The constructor produces only one prototype object

function hd() {
  this.show = function() {
    return "show in object";
  };
}

hd.prototype.show = function() {
  return "show in prototype";
};

const obj = new hd();
console.log(obj.show()); // show in object

The prototype reference of the object and the prototype object of the constructor are determined when the object is created. When the prototype object of the constructor changes, it will affect the subsequent instance object.

function hd() {}
hd.prototype.name = "wgcms";

const obj1 = new hd();
console.log(obj1.name); //wgcms

hd.prototype = {
  name: "willem"
};
const obj2 = new hd();
console.dir(obj2.name); //willem

constructor

The prototype of the constructor contains the attribute constructor pointing to the constructor, which is illustrated in the following code

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

let hd = new User("wgchen");
let xj = new hd.constructor("willem");
console.log(xj); // User {name: 'willem'}

The following code directly sets the prototype of the constructor, which will cause the loss of the constructor

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

User.prototype = {
  show: function() {}
};

let hd = new User("wgchen");
let xj = new hd.constructor("willem");

console.log(xj); // String {'willem'}


The correct approach is to ensure that the constructor in the prototype points to the constructor

function User(name) {
  this.name = name;
}
User.prototype = {
  constructor: User,
  show: function() {}
};

let hd = new User("wgchen");
let xj = new hd.constructor("willem");
console.log(xj);

Use optimization

Using the constructor will cause the problems of function duplication, memory occupation and function sharing.

function User(name) {
  this.name = name;
  this.get = function() {
    return this.name;
  };
}

let lisi = new User("Xiao Ming");
let wangwu = new User("Wang Wu");

console.log(lisi.get == wangwu.get); //false

Defining methods through prototypes does not produce function copies

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

User.prototype.get = function() {
  return "wgchen" + this.name;
};

let lisi = new User("Xiao Ming");
let wangwu = new User("Wang Wu");

console.log(lisi.get == wangwu.get); //true

//Modifying the prototype method will affect all object calls because the method is common
lisi.__proto__.get = function() {
  return "willem" + this.name;
};

console.log(lisi.get()); // willem Xiaoming
console.log(wangwu.get()); // willem Wang Wu

Demonstrates using prototypes to share properties for multiple instances

function User(name, age) {
  this.name = name;
  this.age = age;
  this.show = () => {
  	return `you are here ${this.site}Name of:${this.name},Age:${this.age}`;
  }
}

User.prototype.site = 'wgchen';
let lisi = new User('Li Si', 12); 
let xiaoming = new User('Xiao Ming', 32);

console.log(lisi.show()); //Your name in wgchen: Li Si, age: 12
console.log(xiaoming.show()); //Your name in wgchen: Xiao Ming, age: 32

The method is defined as object sharing on the prototype to solve the memory occupation problem of creating object function replication through constructor

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

User.prototype.get = function () {
    return 'wgchen' + this.name;
}

let lisi = new User('Xiao Ming');
let wangwu = new User('Wang Wu');
console.log(lisi.get == wangwu.get); //true

//Modifying the prototype method will affect all object calls because the method is common
lisi.__proto__.get = function () {
    return 'willem' + this.name;
}

console.log(lisi.get()); // willem Xiaoming
console.log(lisi.get()); // willem Xiaoming
console.log(wangwu.get()); // willem Wang Wu

Use object Assign sets the prototype method to reuse at one time. Later, this function will be used to implement the Mixin mode

function User(name, age) {
  this.name = name;
  this.age = age;
}

Object.assign(User.prototype, {
  getName() {
      return this.name;
  },
  getAge() {
      return this.age;
  }
});

let lisi = new User('Li Si', 12);
let xiaoming = new User('Xiao Ming', 32);

console.log(lisi.getName()); //Li Si
console.log(lisi.__proto__)

Experience inheritance

The following changes the instance object whose prototype is User for Stu, and lisi is the instance object created through the constructor Stu

  1. When lisi executes the getName method, it will look up the prototype from itself, which is the prototype chain feature

  2. Of course, if you add getName to the object, you won't continue to trace the prototype chain

    "use strict";

    function User() {}

    User.prototype.getName = function() {
    return this.name;
    };

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

    Stu.prototype = new User();
    const lisi = new Stu("Li Si");

    console.log(lisi.proto);
    console.log(lisi.getName());

Inheritance and polymorphism

When the properties are not used in the object, JS will get them from the prototype, which is the implementation of inheritance in JavaScript.

Inheritance implementation

Next, use object Create creates an object as the prototype object of Admin and Member to realize inheritance.

function User() {}
User.prototype.getUserName = function() {};

function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};

function Member() {}

Member.prototype = Object.create(User.prototype);
Member.prototype.email = function() {};

console.log(new Admin());
console.log(new Member());


You cannot operate in the following way because it will change the User's prototype method. This is not inheritance, it is changing the prototype

...
function User() {}
User.prototype.getUserName = function() {};

function Admin() {}
Admin.prototype = User.prototype;
Admin.prototype.role = function() {};
...

Constructor

There are several ways to create objects through constructors

function Admin() {}
console.log(Admin == Admin.prototype.constructor); //true

let hd = new Admin.prototype.constructor();
console.log(hd);

let xj = new Admin();
console.log(xj);


Because sometimes the constructor is obtained according to the obtained object and then a new object is created, it is necessary to ensure that the constructor exists, but if admin. Is directly set The prototype attribute will cause the constructor to be lost, so you need to set the constructor value again.

function User() {}
function Admin() {}

Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};

let xj = new Admin();
console.log(xj.constructor); //Missing constructor, return User constructor

Admin.prototype.constructor = Admin;

let hd = new Admin();
console.log(hd.constructor); //Return Admin constructor correctly

//Now you can create a new object by getting the constructor from the object
console.log(new hd.constructor());

Use object Defineproperty definition to prohibit traversal of constructor properties

function User() {}

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

Admin.prototype = Object.create(User.prototype);

Object.defineProperty(Admin.prototype, "constructor", {
  value: Admin,
  enumerable: false //Prohibit traversal
});

let hd = new Admin("wgchen");

for (const key in hd) {
  console.log(key); // name
}

Completely rewrite to build function prototype, which is only valid for later application objects

function User() {}

const lisi = new User();

User.prototype = {
  show() {
    return "prototype show";
  }
};

const wangwu = new User();
console.log(wangwu.show()); // prototype show

console.log(lisi.show()); // lisi.show is not a function

Method rewrite

The following shows the skill that subclasses need to override parent methods.

function Person() {}

Person.prototype.getName = function() {
  console.log("parent method");
};

function User(name) {}

User.prototype = Object.create(Person.prototype);
User.prototype.constructor = User;

User.prototype.getName = function() {
  //Call a method with the same name as the parent
  Person.prototype.getName.call(this);
  console.log("child method");
};

let hd = new User();
hd.getName();


parent method
child method

polymorphic

Different results will be produced according to a variety of different forms, and different results will be obtained according to the objects of different forms.

function User() {}
User.prototype.show = function() {
  console.log(this.description());
};

function Admin() {}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.description = function() {
  return "Administrator here";
};

function Member() {}
Member.prototype = Object.create(User.prototype);
Member.prototype.description = function() {
  return "I'm a member";
};

function Enterprise() {}
Enterprise.prototype = Object.create(User.prototype);
Enterprise.prototype.description = function() {
  return "Enterprise account";
};

for (const obj of [new Admin(), new Member(), new Enterprise()]) {
  obj.show();
}

Deep excavation and inheritance

Inheritance is to reuse code. The essence of inheritance is to point the prototype to another object.

Constructor

We want to call the parent constructor to initialize the properties of the object, but using it like this will not succeed. Because this points to window at this time, properties cannot be declared for the current object.

function User(name) {
  this.name = name;
  console.log(this);// Window
}
User.prototype.getUserName = function() {
  return this.name;
};

function Admin(name) {
  User(name);
}

Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};

let xj = new Admin("wgchen");
console.log(xj.getUserName()); //undefined


The solution to the above problem is to use call/apply to set properties for each generated object

function User(name) {
  this.name = name;
  console.log(this); // Admin
}
User.prototype.getUserName = function() {
  return this.name;
};

function Admin(name) {
  User.call(this, name);
}
Admin.prototype = Object.create(User.prototype);

let xj = new Admin("wgchen");
console.log(xj.getUserName()); //wgchen

Prototype factory

The prototype factory encapsulates the inherited process and simplifies the business using inheritance.

function extend(sub, sup) {
  sub.prototype = Object.create(sup.prototype);
  sub.prototype.constructor = sub;
}

function Access() {}
function User() {}
function Admin() {}
function Member() {}

extend(User, Access); //User inherits Access
extend(Admin, User); //Admin inherits User
extend(Member, Access); //Member inherits Access

Access.prototype.rules = function() {};
User.prototype.getName = function() {};

console.log(new Admin()); // Inheritance relationship: admin > User > access > object
console.log(new Member()); //Inheritance relationship: member > access > object


Object factory*

On the basis of prototype inheritance, the generation of objects is completed by functions, and attributes or methods are added to the objects within the functions.

function User(name, age) {
  this.name = name;
  this.age = age;
}
User.prototype.show = function() {
  console.log(this.name, this.age);
};

function Admin(name, age) {
  let instance = Object.create(User.prototype);
  User.call(instance, name, age);
  instance.role=function(){
    console.log('admin.role');
  }
  return instance;
}
let hd = Admin("wgchen", 19);
hd.show();

function member(name, age) {
  let instance = Object.create(User.prototype);
  User.call(instance, name, age);
  return instance;
}
let lisi = member("Li Si", 28);
lisi.show();

Mixin mode

JS cannot implement multiple inheritance. If you want to use methods of multiple classes, you can use mixin mixed mode.

  1. A mixin class is a class that contains many methods for use by other classes
  2. mixin class is not used to inherit as the parent of other classes

Other languages also have similar operations. For example, in php language, trait can be used to complete similar operations.

The following is the example where Admin needs to use request Because JS is a single inheritance, we have to connect unrelated classes. Obviously, the following code is not well implemented

function extend(sub, sup) {
  sub.prototype = Object.create(sup.prototype);
  sub.prototype.constructor = sub;
}

function Credit() {}
function Request() {}

function User(name, age) {
  this.name = name;
  this.age = age;
}

extend(Request, Credit);
extend(User, Request);

Credit.prototype.total = function() {
  console.log("Statistical integral");
};
Request.prototype.ajax = function() {
  console.log("Request background");
};
User.prototype.show = function() {
  console.log(this.name, this.age);
};
function Admin(...args) {
  User.apply(this, args);
}

extend(Admin, User);
let hd = new Admin("wgchen", 19);
hd.show();  // wgchen 19
hd.total(); //Statistical integral
hd.ajax(); //Request background

The following split function uses Mixin to realize multi inheritance, and the code structure is clearer.
Let Admin inherit User prototype only

function extend(sub, sup) {
  sub.prototype = Object.create(sup.prototype);
  sub.prototype.constructor = sub;
}

function User(name, age) {
  this.name = name;
  this.age = age;
}
User.prototype.show = function() {
  console.log(this.name, this.age);
};

const Credit = {
  total() {
    console.log("Statistical integral");
  }
};
const Request = {
  ajax() {
    console.log("Request background");
  }
};

function Admin(...args) {
  User.apply(this, args);
}
extend(Admin, User);

Object.assign(Admin.prototype, Request, Credit);
let hd = new Admin("wgchen", 19);
hd.show();  // wgchen 19
hd.total(); //Statistical integral
hd.ajax(); //Request background

mixin class can also inherit other classes, such as the following Create class to obtain points. To Request the background, you need to inherit Request to complete it.

super is found in the prototype of the mixin class, not in the User prototype

function extend(sub, sup) {
  sub.prototype = Object.create(sup.prototype);
  sub.prototype.constructor = sub;
}
function User(name, age) {
  this.name = name;
  this.age = age;
}
User.prototype.show = function() {
  console.log(this.name, this.age);
};
const Request = {
  ajax() {
    return "Request background";
  }
};
const Credit = {
  __proto__: Request,
  total() {
    console.log(super.ajax() + ",Statistical integral");
  }
};

function Admin(...args) {
  User.apply(this, args);
}
extend(Admin, User);
Object.assign(Admin.prototype, Request, Credit);
let hd = new Admin("wgchen", 19);
hd.show();  // wgchen 19
hd.total(); //Statistical integral
hd.ajax(); //Request background

Instance operation

Making tabs using call/apply

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>wgchen</title>
</head>
<style>
  * {
    padding: 0;
    margin: 0;
  }

  body {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100vw;
    height: 50vh;
  }

  main {
    width: 400px;
    flex-direction: column;
    position: relative;
    margin-right: 20px;
  }

  main nav {
    display: flex;
    height: 50px;
    align-items: center;
  }

  main nav a {
    background: #95a5a6;
    margin-right: px;
    padding: 10px 20px;
    border: solid 1px #333;
    color: #fff;
    text-decoration: none;
  }

  main nav a:first-of-type {
    background: #e67e22;
  }

  section {
    height: 200px;
    width: 100%;
    background: #f1c40f;
    position: absolute;
    font-size: 5em;
    display: none;
  }

  .hd-tab section:first-of-type {
    display: block;
  }

  section:nth-child(even) {
    background: #27ae60;
  }
</style>

<body>
  <main class="tab1">
    <nav>
      <a href="javascript:;">wgchen</a>
      <a href="javascript:;">blog</a>
    </nav>
    <section>1</section>
    <section>2</section>
  </main>
  <main class="tab2">
    <nav>
      <a href="javascript:;">willem</a>
      <a href="javascript:;">cms</a>
    </nav>
    <section>1</section>
    <section>2</section>
  </main>
</body>

<script>
  //Succession factory
  function extend(sub, sup) {
    sub.prototype = Object.create(sup.prototype);
    sub.prototype.constructor = sub;
  }

  //Action class
  function Animation() { }
  Animation.prototype.show = function () {
    this.style.display = "block";
  };
  //Hide all elements
  Animation.prototype.hide = function () {
    this.style.display = "none";
  };
  //Required element set background
  Animation.prototype.background = function (color) {
    this.style.background = color;
  };

  //Tab class
  function Tab(tab) {
    this.tab = tab;
    this.links = null;
    this.sections = null;
  }

  extend(Tab, Animation);

  Tab.prototype.run = function () {
    this.links = this.tab.querySelectorAll("a");
    this.sections = this.tab.querySelectorAll("section");
    this.bindEvent();
    this.action(0);
  };

  //Binding event
  Tab.prototype.bindEvent = function () {
    this.links.forEach((el, i) => {
      el.addEventListener("click", () => {
        this.reset();
        this.action(i);
      });
    });
  };

  //Trigger action after clicking
  Tab.prototype.action = function (i) {
    this.background.call(this.links[i], "#e67e22");
    this.show.call(this.sections[i]);
  };
  
  //Reset link and section
  Tab.prototype.reset = function () {
    this.links.forEach((el, i) => {
      this.background.call(el, "#95a5a6");
      this.hide.call(this.sections[i]);
    });
  };

  new Tab(document.querySelector(".tab1")).run();
  new Tab(document.querySelector(".tab2")).run();
</script>
</html>

Topics: Javascript Front-end Mini Program