Through this article, you can learn | review the following points:

  • Use of the new operator

  • Data type, raw value

  • Built-in constructor, custom constructor

  • Prototype, prototype chain

  • ES6 Class

New operator

New is used to create instances of object types with constructors. The syntax is as follows:

new Constructor[([args])];
Copy the code

Constructor (args); Constructor (args); New Constructor and new Constructor() are equivalent when there is no args.

When we execute new, we do something like this:

  1. Create an empty object {}

  2. Constructor. Prototype (implements type inheritance, builds prototype chain)

  3. The constructor is executed with the created object as this in the constructor

  4. The constructor returns this inside the constructor without a return, which is the result of the new operator. (You can also override the normal object creation steps by actively choosing to return an object in the constructor, But this is not the recommended behavior — in TypeScript it is explicitly required that the constructor return type be void.)

Therefore, if we apply new to an empty function, we return an empty object {} :

new (function() {}); / / {}
Copy the code

For constructors, executing new Constructor() and Constructor() usually yields different results:

typeof Date(a);// 'string'
typeof new Date(a);// 'object' 
Copy the code

New can operate on classes in ES6 as well as constructors. But ES6’s class is just a syntactic sugar and is essentially a constructor, so let’s focus on constructors for now.

Built-in constructor

There are many built-in constructors, such as Date(), Promise(), Map(), Set()…

In JavaScript, convention constructors are named in a big camel shape.

We can use the constructor name as the type of the instance produced by the constructor. For example, new Date() is of type Date, new Error(‘ Error, Error ‘) is of type Error, [] is of type Array, {} is of type Object, 100 is of type Number, ‘nice ! ‘The type is String. The type of true is Boolean…… The name of the Constructor can be obtained by constructive.name. (Use this later to get the type of any object)

The corresponding constructor for the data type

Null and undefined have corresponding constructors for the eight data types:

  • Object()

  • Boolean()

  • Number()

  • String()

  • Symbol()

  • BigInt()

Const typeSymbol = Symbol(‘type’); const typeSymbol = Symbol(‘type’); const typeSymbol = Symbol(‘type’); .

It cannot be new because it is stipulated in ES6 that new cannot be performed on the constructor of the underlying type. Boolean(), Number(), and String() also support new, more for compatibility reasons. Creating raw values using literals is now recommended.

PrimitiveValue (PrimitiveValue, PrimitiveValue, PrimitiveValue, PrimitiveValue, PrimitiveValue, PrimitiveValue, PrimitiveValue)

New Object() and Object() have almost the same effect.

In JavaScript, when we say “object” or “object,” we usually mean an instance that contains key-value pairs; When we say “Object,” we usually mean the constructor Object().

Raw value wrapper

Using Boolean as an example, execute new Boolean(0) :

Boolean {false} printed in the console is a primitive value wrapper that has two characteristics:

  • It is an inheritance fromBoolean.prototypeObject inherited fromObject.prototype), therefore, if implementednew Boolean(false) && 100It actually returns100
  • It wraps around a value offalseThe havePrimitiveValue.

If only the constructor is executed without the new operator, only the original value is returned (data conversion if necessary) :

Basic types of

Seven basic types:

  • Null: null

  • Undefined: undefined

  • Boolean: true false

  • Number: 1 100

  • String: ‘Hello’, ‘hello’…

  • Symbol: Symbol.toStringTag Symbol.toPromitive

  • BigInt: 1n 9999n

Primitive types represent the lowest level of language implementation.

Values of primitive types are called primitive values, primitive data, such as NULL, undefined, true, 100, ‘hello’… These are all raw values.

As we’ve seen in our daily development, primitive types are used a lot in our code, so when designing JavaScript, primitive types have to be efficient from the get-go.

To achieve this goal, the raw values have the following characteristics:

  • Stack (references to complex types are stored in the heap)

  • immutable

  • No properties, no methods

In JavaScript, all but primitive values (values of the underlying type) are reference values (values of complex types inherited from Object.prototype), even functions are reference values with a [[callable]] inside that executes the () syntax.

The original value is immutable

let a = 1;
((num) = > num++)(a);
a; / / 1
Copy the code

Because of the immutable nature of the original value, when you pass the original value as an argument to a function, you are actually passing in a copy of the original value, a copy of the operation inside the function, which does not affect the original value.

But if a function receives a reference value, it itself changes as the function body operates:

let arr = [];
((value) = > value.push('Oh! '))(arr);
arr; // ["Oh!"]
Copy the code

Raw values have no attributes, methods

As mentioned above, raw values have no attributes or methods for efficiency, but we often do the following:

const str = 'abc';
str.substr(-1); // 'c'
str.length; / / 3
Copy the code

Why is it possible to call the length property and call the method.substr()?

This is because when you call a method or attribute on the raw value STR, the JavaScript engine creates a wrapper around the original value, new String(STR), and then calls the method or attribute on that wrapper.

And because of the immutability of the original value, all methods called by the original value wrapper, such as.substr(),.substring(),.tofixed (), etc., do not change the original value, and the function result is returned as a new original value — a property of all original values.


Having covered the use of new in JavaScript’s built-in constructors, let’s look at its use in custom constructors.

Custom constructors

// Define the object type: Phone
function Phone(make, model) {
  this.make = make;
  this.model = model;
}
Copy the code

New Phone(‘Apple’, ‘iPhone 12’)

Here we create an instance of Phone, and the properties make and Model are assigned normally when the constructor is executed.

In addition, the instance has an attribute __proto__. What is it?

__proto__

Each instance derived from new has an attribute __proto__, which only does one thing: point to the Prototype (parent) of the current instance, that instance’s [[Prototype]]. In the example above, it points to Phone. Prototype. For objects created using Object literals, it points to Object.prototype; For objects created using Array literals, it points to array. prototype. For objects created using String literals, it points to String.prototype…

It is possible to change the prototype of the current instance by changing __proto__, provided that the Object is judged extensible by Object.isextensible (). The value to be changed must be an object or NULL.

__proto__ is no longer recommended for performance reasons, if obj.__proto__ =… Very likely to go wrong! Object.getprototypeof (O)/ object.setProtoTypeof (O, proto) is now preferred.

What is the relationship between __proto__ and prototype?

prototype– the prototype

First, what objects does the Prototype property appear on?

A: Built-in constructors and custom ordinary functions.

Arrow function has no prototype:

Prototype:

Correspondingly, __proto__ appears on an object instance.

Many times we find that a prototype also has a __proto__ because a prototype is also an instance of some other prototype. Without multiple layers of inheritance, it is usually Object.prototype.

Number.prototype.__proto__ === Object.prototype; // true
Copy the code

There are two basic properties of archetypes

A “clean” Constructor. Prototype has two properties:

  • Constructor – Points to the constructor

  • Proto__ – prototypeconstructor. Prototype’s __proto__ usually refers to Object.prototype

Constructor / __proto__ / prototype

All objects in JavaScript, except for __proto__ empty objects, are instances of Object and inherit the properties and methods of Object.prototype — although they may be overridden.

Sometimes objects that do not have typical stereotype chain inheritance are intentionally created, such as objects created through Object.create(NULL), or obj.__proto__ =… Object.setprototypeof (obj, proto) changes the prototype chain.

Changing an Object stereotype changes all objects through the stereotype chain, which provides a very powerful mechanism for extending Object behavior. The following code extends Object.prototype to make it easy to get the data type of any Object anywhere in the program:

Object.defineProperty(Object.prototype, Symbol.type = Symbol('type'), {
  get() {
    // Specify NaN as type 'NaN', not 'Number'
    if (
      this.__proto__.constructor.name === 'Number' &&
      Number.isNaN(this.valueOf())
    ) {
      return 'NaN';
    }
    return this.__proto__.constructor.name; }});Copy the code

After that, all base-type data, complex type data, except null and undefined, can be obtained by calling the [symbol.type] property:

prototypeApplication in custom constructors

function Phone(make, model) {
  this.make = make;
  this.model = model;
  this.innerLogMake = function() {
    console.log('Current phone manufacturer:'.this.make);
  }
}

Phone.prototype.outerLogMake = function() {
    console.log('Current phone manufacturer:'.this.make);
}

const phone = new Phone('Apple'.'iPhone');

phone.innerLogMake(); // 'Current phone manufacturer: Apple'
phone.outerLogMake(); // 'Current phone manufacturer: Apple'
Copy the code

Output the Phone. The prototype:

OuterLogMake hangs on the prototype of the Phone, so instances can invoke the method along the prototype chain.

The innerLogMake inside the constructor is actually considered an attribute of the instance, not a method. For performance reasons, methods should hang on Phone. Prototype instead of regenerating a method each time the constructor is executed.

The difference between the constructor and the prototype of the arrow function

function Phone(make, model) {
  this.make = make;
  this.model = model;

  this.innerLogMake_arrow = () = > {
    console.log('Current phone manufacturer:'.this.make);
  }
}

Phone.prototype.outerLogMake_arrow = () = > {
      // This does not refer to Phone instance!!
    console.log('Current phone manufacturer:'.this.make);
}

const phone = new Phone('Apple'.'iPhone');

phone.innerLogMake_arrow(); // 'Current phone manufacturer: Apple'
phone.outerLogMake_arrow(); // 'Current phone manufacturer: undefined'
Copy the code

Change the prototype of the instance

The connection between the instance and the stereotype is represented by the instance’s __proto__. As mentioned above, if we need to change the prototype of the instance, we should call Object.setPrototypeof (O, proto) instead of setting __proto__ directly.

Object.setPrototypeOf(phone, null);
typeof phone.outerLogMake; // undefined
Object.setPrototypeOf(phone, Phone.prototype);
phone.outerLogMake(); // 'Current phone manufacturer: Apple'
Copy the code

Implementation inheritance:

function Phone(make, model) {
  this.make = make;
  this.model = model;
}
Phone.prototype.logMake = function() {
    console.log('Current phone manufacturer:'.this.make);
}

function HuaweiPhone(model) {
  // The parent constructor must be executed once!
  Phone.call(this.'huawei', model); / / *
}
Object.setPrototypeOf(HuaweiPhone.prototype, Phone.prototype); / / *

const p40 = new HuaweiPhone('P40');

p40.logMake(); // 'Current phone manufacturer: Huawei'
Copy the code

Print p40:

By default, huaweip.prototype. __proto__ is object. prototype. There are two key steps to implement Phone. Prototype inheritance:

  1. In the constructorHuaweiPhone()(subclass)Phone()(superclass), whether usedcall(),apply()Or any other way, as long as the properties of the Phone instance are set correctly
  2. willHuaweiPhone.prototypeThe prototype is set toPhone.prototypeTo call properties and methods on the Phone prototype

Class

The first thing to make clear is that Class in JavaScript is just a syntactic sugar.

Syntactic sugar: A syntax added to a computer language that has no effect on the language’s functionality but is more convenient for programmers. Syntax sugar makes the program more concise and more readable. — Wikipedia

A Class in JavaScript is essentially a constructor. Call (Phone); Phone (Phone); Phone (Phone);

// class declarative
class Phone {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
   logMake() {
     console.log('Current phone manufacturer:'.this.make); }}Copy the code
// constructor declaration
function Phone(make, model) {
   this.make = make;
   this.model = model;
}
Phone.prototype.logMake = function() {
  console.log('Current phone manufacturer:'.this.make);
}
Copy the code

The first conclusion: they are the same thing.

Let’s execute the instantiation code to see:

Class declares the object type and instantiates it:

Can clearly see the Phone. The prototype. Behind the constructor is the class Phone, but is actually a function, the arguments, caller, length, the name of these functions will attribute of it have, the most important thing is:

Phone.prototype.constructor.__proto__ === Function.prototype; // true 
Copy the code

False class, true function, gone!

The constructor declares the object type and performs the instantiation:

As you can see, there is little difference in the content of the two instantiations.

Inheritance in class

class Phone {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
   logMake() {
     console.log('Current phone manufacturer:'.this.make); }}// Implement inheritance using the extends keyword
class HuaweiPhone extends Phone {
  constructor(model) {
    // super means constructor() for Phone, must call!
    super('huawei', model)
  }
} 
Copy the code

Perform instantiation:


That’s all for this article, if there are any mistakes welcome to correct! Do not understand the place welcome to leave a comment!

The resources

  • stackoverflow: how is almost everything in javascript an object

  • MDN: new operator

  • MDN: Object.prototype.__proto__