Instance object and new command

The JavaScript language has strong object-oriented programming capabilities. This chapter introduces the basics of JavaScript object-oriented programming.

What is the object

Object-oriented programming (Object Oriented Programming, abbreviated as OOP) is the current mainstream programming paradigm. It abstracts various complex relationships in the real world into objects, and then completes the simulation of the real world by the division of labor and cooperation between the objects.

Each object is a functional center, with a clear division of labor, and can complete tasks such as receiving information, processing data, and sending information. Objects can be reused and customized through inheritance mechanisms. Therefore, object-oriented programming has the characteristics of flexibility, code reusability, and high modularity. It is easy to maintain and develop. Compared with traditional procedural programming consisting of a series of functions or instructions, it is more suitable for multi-person cooperation. Large-scale software projects.

So, what exactly is an "object" (object)? We understand from two levels.

**(1) An object is an abstraction of a single object. **

A book, a car, or a person can be an object, and a database, a web page, or a remote server connection can also be an object. When the real thing is abstracted into an object, the relationship between the real thing becomes the relationship between the objects, so that you can simulate the real situation and program the object.

**(2) An object is a container that encapsulates properties and methods. **

Attributes are the state of the object, and methods are the behavior of the object (to complete a certain task). For example, we can abstract an animal as an animal object, use "attributes" to record which animal it is, and use "methods" to represent certain behaviors of animals (running, hunting, resting, etc.).

Constructor

The first step in object-oriented programming is to generate objects. As mentioned earlier, an object is an abstraction of a single object. Usually a template is needed to represent the common characteristics of a certain type of object, and then the object is generated based on this template.

Typical object-oriented programming languages ​​(such as C++ and Java) have the concept of "class". The so-called "class" is the template of the object, and the object is the instance of the "class". However, the object system of JavaScript language is not based on "classes", but based on constructors and prototype chains.

The JavaScript language uses constructors as templates for objects. The so-called "constructor" is a function specifically used to generate instance objects. It is the template of the object, describing the basic structure of the instance object. A constructor can generate multiple instance objects, all of which have the same structure.

The constructor is an ordinary function, but has its own characteristics and usage.

var Vehicle = function () {
  this.price = 1000;
};

In the above code, Vehicle is the constructor. In order to distinguish it from ordinary functions, the first letter of the constructor name is usually capitalized.

There are two characteristics of the constructor.

-The this keyword is used inside the function body, which represents the object instance to be generated. -When creating an object, you must use the new command.

Let's first introduce the new command.

new command

Basic usage

The function of the new command is to execute the constructor and return an instance object.

var Vehicle = function () {
  this.price = 1000;
};

var v = new Vehicle();
v.price; // 1000

The above code uses the new command to make the constructor Vehicle generate an instance object and save it in the variable v. This newly generated instance object gets the price property from the constructor Vehicle. When the new command is executed, the this inside the constructor represents the newly generated instance object, and this.price indicates that the instance object has a price attribute with a value of 1000.

When using the new command, the constructor can also accept parameters as needed.

var Vehicle = function (p) {
  this.price = p;
};

var v = new Vehicle(500);

The new command itself can execute the constructor, so the following constructor can be with or without parentheses. The following two lines of code are equivalent, but to indicate that this is a function call, parentheses are recommended.

// Recommended writing
var v = new Vehicle();
// Not recommended
var v = new Vehicle();

A natural question is, what happens if you forget to use the new command and call the constructor directly?

In this case, the constructor becomes an ordinary function, and no instance object is generated. And because of the reasons that will be mentioned later, this now represents the global object, which will cause some unexpected results.

var Vehicle = function () {
  this.price = 1000;
};

var v = Vehicle();
v; // undefined
price; // 1000

In the above code, when calling the Vehicle constructor, I forgot to add the new command. As a result, the variable v becomes undefined, and the price property becomes a global variable. Therefore, you should be very careful to avoid calling the constructor directly without using the new command.

In order to ensure that the constructor must be used with the new command, one solution is to use strict mode inside the constructor, that is, add use strict to the first line. In this case, once you forget to use the new command, you will get an error if you call the constructor directly.

function Fubar(foo, bar) {
  "use strict";
  this._foo = foo;
  this._bar = bar;
}

Fubar();
// TypeError: Cannot set property'_foo' of undefined

The Fubar in the above code is the constructor, and the use strict command ensures that the function runs in strict mode. In strict mode, this inside a function cannot point to a global object, and is equal to undefined by default, which results in an error when calling without new (JavaScript does not allow adding attributes to undefined).

Another solution is to determine whether to use the new command inside the constructor. If it is found that it is not used, it will directly return an instance object.

function Fubar(foo, bar) {
  if (!(this instanceof Fubar)) {
    return new Fubar(foo, bar);
  }

  this._foo = foo;
  this._bar = bar;
}

Fubar(1, 2)._foo(
  // 1
  new Fubar(1, 2)
)._foo; // 1

The constructor in the above code will get the same result regardless of whether the new command is added or not.

Principle of the new command

When using the new command, the functions following it execute the following steps in sequence.

  1. Create an empty object as the object instance to be returned.
  2. Point the prototype of this empty object to the prototype property of the constructor.
  3. Assign this empty object to the this keyword inside the function.
  4. Start executing the code inside the constructor.

In other words, inside the constructor, this refers to a newly generated empty object, and all operations on this will occur on this empty object. The reason why a constructor is called a "constructor" means that the purpose of this function is to manipulate an empty object (that is, the this object) and "construct" it as needed.

If there is a return statement inside the constructor and an object is followed by return, the new command will return the object specified by the return statement; otherwise, it will return the this object regardless of the return statement.

var Vehicle = function () {
  this.price = 1000;
  return 1000;
};

new Vehicle() === 1000;
// false

In the above code, the return statement of the constructor Vehicle returns a value. At this time, the new command ignores this return statement and returns the this object after "construction".

However, if the return statement returns a new object that has nothing to do with this, the new command will return this new object instead of the this object. This point requires special attention.

var Vehicle = function () {
  this.price = 1000;
  return { price: 2000 };
};

new Vehicle().price;
// 2000

In the above code, the return statement of the constructor Vehicle returns a new object. The new command will return this object instead of the this object.

On the other hand, if the new command is used for a normal function (a function without the this keyword inside), an empty object will be returned.

function getMessage() {
  return "this is a message";
}

var msg = new getMessage();

msg; // {}
typeof msg; // "object"

In the above code, getMessage is an ordinary function that returns a string. Use the new command on it, and you will get an empty object. This is because the new command always returns an object, either an instance object or the object specified by the return statement. In this example, the return statement returns a string, so the new command ignores this statement.

The internal process simplified by the new command can be represented by the following code.

function _new(
  /* constructor*/ constructor,
  /* constructor parameters*/ params
) {
  // Turn the arguments object into an array
  var args = [].slice.call(arguments);
  // Take out the constructor
  var constructor = args.shift();
  // Create an empty object, inherit the prototype property of the constructor
  var context = Object.create(constructor.prototype);
  // Execute the constructor
  var result = constructor.apply(context, args);
  // If the return result is an object, return directly, otherwise return the context object
  return typeof result === "object" && result != null ? result : context;
}

// instance
var actor = _new(Person, "Zhang San", 28);

new.target

The new.target property can be used inside the function. If the current function is called by the new command, new.target points to the current function, otherwise it is undefined.

function f() {
  console.log(new.target === f);
}

f(); // false
new f(); // true

Using this attribute, you can determine whether to use the new command when calling the function.

function f() {
  if (!new.target) {
    throw new Error("Please use the new command to call!");
  }
  // ...
}

f(); // Uncaught Error: Please use the new command to call!

In the above code, when the constructor f is called, an error is thrown without using the new command.

Object.create() Create an instance object

The constructor is used as a template to generate instance objects. However, sometimes you can't get the constructor, you can only get an existing object. We hope to use this existing object as a template to generate a new instance object, then we can use the Object.create() method.

var person1 = {
  name: "Zhang San",
  age: 38,
  greeting: function () {
    console.log("Hi! I'm " + this.name + ".");
  },
};

var person2 = Object.create(person1);

person2.name; // Zhang San
person2.greeting(); // Hi! I'm Zhang San.

In the above code, the object person1 is a template of person2, and the latter inherits the properties and methods of the former.

For a detailed introduction of Object.create(), please see the relevant chapters later.