“This is the third day of my participation in the First Challenge 2022. For details: First Challenge 2022”

How to create objects and how to implement inheritance are two other common interview questions. Here we list ten ways to create objects and seven ways to implement inheritance.

Ten ways to create objects

1. New Object mode

const xiaoming = new Object({
	name: 'Ming'.age: 18
})
Copy the code

You can abbreviate it in the following way

2. Brace abbreviations

const xiaoming = {
	name: 'Ming'.age: 18
}
Copy the code

These two ways of creating objects are equivalent, and the code is redundant when creating objects with the same structure.

3. Factory function mode

The factory function solves the problem of code redundancy caused by the above two approaches

function createPerson(name, age) {
	return {
  	name,
    age,
    getName: function() {
    	console.log(this.name, this.age)
    }
  }
}

const xiaoming = createPerson('Ming'.18)
Copy the code

This method is still a new Object in nature and cannot accurately determine the type of an Object based on its prototype Object

4. Constructor mode

Constructors can repeatedly create multiple objects with the same property structure and different property values.

function Person(name, age) {
	this.name = name
  this.age = age
  this.getInfo = function() {
  	console.log(this.name, this.age)
  }
}

const xiaoming = new Person('Ming'.18)
Copy the code

If the constructor contains methods, they are created repeatedly, resulting in a waste of memory

Fifth, the prototype object method

Putting common methods into prototypes avoids recreating methods with the same functionality and reduces memory usage. 👉 Click here to see how it works

function Person() {}

Person.prototype.name = "This man is lazy and has no name."
Person.prototype.age = 1
Person.prototype.getInfo = function() {
	console.log(this.name, this.age)
}

const xiaoming = new Person()
// The attributes on the prototype are used here
xiaoming.getInfo() // "This person is lazy and has no name ", 1

// Add its own attributes
xiaoming.name = 'Ming'
xiaoming.age = 18
// Use its own attributes
xiaoming.getInfo() // "xiao Ming ", 18
Copy the code

This approach also leads to code redundancy

6. Hybrid mode

function Person(name, age) {
	this.name = name
  this.age = age
}

Person.prototype.getInfo = function() {
	console.log(this.name, this.age)
}

const xiaoming = new Person(name, age)
Copy the code

Although this approach solves the problem of code redundancy, it does not conform to the idea of object-oriented encapsulation.

7. Dynamic mixing

function Person(name, age) {
	this.name = name
  this.age = age
  if (Person.prototype.getInfo === "undefined") {
  	Person.prototype.getInfo = function() {
    	console.log(this.name, this.age)
    }
  }
}

// The getInfo method is added to Person.prototype the first time it is called
const xiaoming = new Person("Xiao Ming".18) 

const xiaoguang = new Person("Little light".15)
Copy the code

The downside of this approach is that it is semantically inconsistent, and only the first object is created when the if judgment is used

Parasitic constructors

By calling other constructors in a function, this approach is also a form of inheritance.

function Person(name, age) {
	this.name = name
  this.age = age
  if (Person.prototype.getInfo === "undefined") {
  	Person.prototype.getInfo = function() {
    	console.log(this.name, this.age)
    }
  }
}

function Programmer(name, age, lang) {
	const p = new Person(name, age)
  p.lang = lang
  return p
}

const xiaoming = Programmer("Xiao Ming".18."JavaScript")
const xiaoguang = Programmer("Little light".15."C++")
Copy the code

Nine, class

Class is a new syntax for creating objects in ES6, which is essentially a constructor, just a syntactic sugar. This syntax takes care of all the cases we’ve mentioned above.

class Person {
	constructor(name, age) {
  	this.name = name
    this.age = age
  }
  getInfo() {
  	console.log(this.name, this.age)
  }
}

const xiaoming = new Person("Xiao Ming".18)
Copy the code

Ten, closures

It is also possible to create objects using the features of closures. This approach has the advantage of not using this and new. The disadvantage is easy to cause memory leaks.

function Person(name, age) {
  return {
  	getName() {
    	return name
    },
    setName: function(value) {
    	name = value
    },
    getAge: function() {
    	return age
    }
  }
}

const xiaoming = Person("Xiao Ming".18)
const xiaoguang = Person("Little light".15)
console.log(xiaoming.getName()) / / xiao Ming
console.log(xiaoguang.getName()) / / light

xiaoming.setName("Little Ming disguised as a little light.")
console.log(xiaoming.getName()) // Xiao Ming disguised as a light
Copy the code

There are seven ways to implement inheritance

First, prototype chain inheritance

The disadvantage of using an instance of a parent class as a prototype for a subclass is that you cannot pass arguments to the parent constructor when creating an instance of a subclass

function Animal(name) {
	this.name = name
  this.age = age
  this.say = function() {
  	console.log(this.name)
  }
}

Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}Is eating:${food}`)}function Cat() {}
Cat.prototype = new Animal() // Set prototype directly to Animal instance object
Cat.prototype.name = 'cat'

const cat = new Cat()
cat.eat("Cat food")
Copy the code

Constructor inheritance

Advantages:

  1. The problem of modifying reference attribute of parent class in prototype chain inheritance is solved
  2. You can now pass arguments to the superclass constructor

Disadvantages:

  1. An instance is not an instance of a parent class, only an instance of a subclass!
  2. Function reuse is not possible; each subclass has a copy of the parent class instance function
function Animal(name) {
	this.name = name
  this.say = function(sound) {
  	console.log(`The ${this.name}Said:${sound}`)
  }
}

Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}eat${food}`)}function Cat(name, age) {
	Animal.call(this, name) // Build with Cat
  this.age = age
}

const cat = new Cat('the cat'.1)
cat.say('meow ~') / / OK!
cat.eat('Dried fish') // Throw wrong!! Because cat is not an instance of a parent class, it is only an instance of a subclass, and it cannot call eat
Copy the code

Instance inheritance

The disadvantage is also obvious. The result of a “subclass instance” is actually an instance of the parent class, and the “subclass instance” cannot call its own prototype method

function Animal(name) {
	this.name = name
  this.say = function(sound) {
  	console.log(`The ${this.name}Said:${sound}`)
  }
}
Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}eat${food}`)}function Cat(name, age) {
	const p = new Animal(name) // Create a subtype instance
  p.age = age
  return p // Note that this is an instance of the parent class, so it doesn't matter if we call Cat with or without new
}
Cat.prototype.jump = function() {
	console.log('jump! ')}// Call new directly
const cat = new Cat('the cat'.1)
cat.eat('Dried fish') // The cat eats dried fish
cat.jump() / / wrong
Copy the code

Copy inheritance

Use for in to access Prototype features for copy inheritance. Disadvantages of this approach:

  1. Cannot get the parent classfor inTraversal method.
  2. Every timenewEach child instance recreates the parent instance and copies a copy of the parent attributes
function Animal(name) {
	this.name = name
    this.say = function(sound) {
  	console.log(`The ${this.name}Said:${sound}`)
  }
}

Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}eat${food}`)}function Cat(name, age) {
	const animal = new Animal(name) // The disadvantage is that every time Cat is called, the new Animal and the copy operation will be performed
  for (let p in animal) { // Use for in to access prototype features
  	Cat.prototype[p] = animal[p] // Some non-enumerable attributes cannot be accessed
  }
  this.age = age
}

const cat = new Cat('the cat'.1)
cat.eat('Dried fish') // The cat eats dried fish
Copy the code

5. Combinatorial inheritance

function Animal(name) {
	this.name = name
    this.say = function(sound) {
  	console.log(`The ${this.name}Said:${sound}`)
  }
}

Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}eat${food}`)}function Cat(name, age) {
	Animal.call(this, name) // Constructor inheritance
  this.age = age
}

Cat.prototype = new Animal() // Prototype chain inheritance
Cat.prototype.constructor = Cat // Point the constructor of the prototype to itself

const cat = new Cat('the cat'.1)
Copy the code

Parasitic combination inheritance

Inheriting internal properties and methods through constructor inheritance inherits internal properties and methods by creating an empty class to inherit a parent class prototype, and then assigning an instance to a subclass’s prototype

function Animal(name) {
	this.name = name
    this.say = function(sound) {
  	console.log(`The ${this.name}Said:${sound}`)
  }
}

Animal.prototype.eat = function(food) {
	console.log(`The ${this.name}eat${food}`)}function Cat(name, age) {
	Animal.call(this, name) // Constructor inheritance
  this.age = age
}

// Implement inheritance on the prototype by creating an empty class to inherit the parent's prototype and then assigning an instance to the child's prototype
(function() {
  const Super = function() {} // Create an empty class
  Super.prototype = Animal.prototype
  Cat.prototype = new Super() // Use the instance as the prototype of the subclass}) ()const cat = new Cat('the cat'.1)
cat.eat('Dried fish')
Copy the code

ES6 extends extends

class Animal {
	constructor(name) {
  	this.name = name
  }
  eat(food) {
  	console.log(`The ${this.name}eat${food}`)}}class Cat extends Animal {
	constructor(name, age) {
  	super(name)
    this.age = age
  }
}

const cat = new Cat('the cat'.1)
Copy the code