Original text: blog. Xieyangogo. Cn / 2019/04/12 /…


I believe that many front-end partners, even those who have worked for several years, still have a vague understanding of the new operator.

For example, when I met a front-end friend who had been working for two years recently, he told me that new is used to create objects. There is nothing wrong with that. Probably many people would say the same!

So is it wrong or is it right?


Let’s discuss the problem comprehensively:

There are many ways to get an object, and one of the most common is object literals:

var obj = {}
Copy the code

But syntactically, this is an assignment that assigns the opposite literal to the variable obj.

A lot of times, when we say we’re going to create an object, a lot of people just touch the keyboard with their hands and tap out this code.

If you want to create an instance of an object, you can create an instance of an object.

Let’s move on.

To get an instance of an object, the equivalent of an object literal is a constructor:

var obj = new Object(a)Copy the code

Obj is just an instance object. Obj is an instance object.

Then many friends will ask: this is not a new new object out!

Yes, this is really a new object, because in javascript, everything interprets objects.

Obj is an object, and is obtained by the new operator, so many people are sure that new is used to create objects!

This explains why many people confuse creating objects with instantiating them!!

If js is all about objects, why create objects? How can we create an object that is itself an object? Can we call it an inheritance?

Having said so much, I believe many partners have been confused, but our purpose is one: clear new is to do inheritance rather than the so-called creation of objects!!


What are the characteristics of inherited instance objects?

  1. Access properties inside the constructor
  2. Access properties on the prototype chain

Here’s a classic inheritance to warm up with:

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

Person.prototype.nation = 'han'

Person.prototype.say = function() {
  console.log(`My name is The ${this.age}`)}var person = new Person('Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()
Copy the code
  • Now let’s solve the first question: how can we access the properties inside the constructor? The answer iscallorapply
function Parent() {
  this.name = ['A'.'B']}function Child() {
  Parent.call(this)}var child = new Child()
console.log(child.name) // ['A', 'B']

child.name.push('C')
console.log(child.name) // ['A', 'B', 'C']
Copy the code
  • With the first problem solved, let’s tackle the second: how do we access properties on the prototype chain? The answer is__proto__

Now let’s modify the warm-up code a little bit without using new:

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

Person.prototype.nation = 'han'

Person.prototype.say = function() {
  console.log(`My name is The ${this.age}`)}// var person = new person (' small ', 25)
var person = New(Person, 'Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments) // Get arguments first argument: constructor
  // Note that the arguments argument is left with only two elements after the shift() method intercepts
  obj.__proto__ = Constructor.prototype // Assign the constructor's prototype to obj
  Constructor.apply(obj, arguments) // Change the way you access properties and methods in the constructor by touching the function pointer to obj
  return obj
}
Copy the code

The New function in the above code is an implementation of the New operator

Main steps:

1. Create an empty object

2. Get arguments first

3. Assign the constructor's prototype chain to OBj

4. Use apply to change constructor this to point to object obj, and then obj can access constructor properties as well as properties and methods on the prototype

5. Return the obj object

Maybe a lot of friends see here and think that new is not doing these things, however ~~

However, we have overlooked the fact that js functions return values, even constructors.

If we return an object or a base value inside the constructor, what happens to the New function above?

Let’s look at another piece of code:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = 'male'
  
  return {
    name: name,
    gender: 'male'
  }
}

Person.prototype.nation = 'han'

Person.prototype.say = function() {
  console.log(`My name is The ${this.age}`)}var person = new Person('Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()
Copy the code

Only name and gender are output as expected. Age and nation are undefined. Say () is error.

Change the code constructor:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = 'male'
  
  // return {
  // name: name,
  // Gender: male
  // }
  return 1
}

// ...
Copy the code

Execute the code and see that all the fields are finally printed as expected.

Here's a quick summary:

1. When a constructor returns a reference type, the property in the constructor cannot be used. Only the returned object can be used.

2. When a constructor returns a primitive type, as when no value is returned, the constructor is not affected.

Now let’s think about how to change the New function to achieve the above two functions. Read on:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  
  // return obj
  return typeof result === 'object' ? result : obj
}

var person = New(Person, 'Ming'.25)

console.log(person.name)
// ...

Copy the code

When you execute this code, you see that you have implemented the two points summarized above.

Solution: Use a variable to receive the return value from the constructor, and then check the return value type in the New function.

See here. Now new has been fully implemented, right? !!!!!

The answer is definitely no.

Let’s continue with a piece of code:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = 'male'
  
  // Return the reference type
  // return {
  // name: name,
  // Gender: male
  // }
  
  // Return the base type
  // return 1
  
  / / exception
  return null
}
Copy the code

Execute the code again and find the problem again!!

Another problem! Why…… ? . The constructor is not affected when a primitive type is returned, and null is a primitive type. .

It is true that null is a primitive type, but using the typeof operator we can see that:

typeof null= = ='object' // true
Copy the code

Exception: Typeof NULL returns ‘object’ because the special value NULL is considered an empty object reference.

With this in mind, the problem is easily solved:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, 'Ming'.25)

console.log(person.name)
// ...

Copy the code

Solution: assess the constructor return value result, if the result is a reference (reference types and null), it returns the result, but if the result to false (null), use the operator | | after obj

Well, now there should be some friends to ask, this New function is completely realized!!

The answer is, it’s not far off!!

Functionally, the New function is almost complete, but we need to do a little more work on code rigor. Keep reading:

Here’s where we come in handy:

var obj = {}
Copy the code

It’s actually equivalent to

var obj = new Object(a)Copy the code

As mentioned above, the above two pieces of code are actually just getting an instance of an Object. Again, we were supposed to implement new, but we used new to implement new!

Which brings us to the chicken or the egg question!

Here we need to consider ECMAScript’s underlying API — Object.create(null)

This code actually creates an object!!

function Person(name, age) {
  // ...
}

function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, 'Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// The following two sentences will be commented out. The reasons will be discussed later
// console.log(person.nation)
// person.say()

Copy the code

Good good, small partners often relieved, so finally completed! However, the reality is always cruel!

Friend: What? Is there any more?

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

Person.prototype.nation = 'han'

Person.prototype.say = function() {
  console.log(`My name is The ${this.age}`)}function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, 'Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// Unwrap the comment here
console.log(person.nation)
person.say()
Copy the code

The nation attribute and the say() method on the prototype chain failed.

Object. Create (null) creates objects that have no prototype chain, while the last two objects have __proto__ attributes that have prototype chains, proving that the last two objects are inherited.

Object. Create (null) creates an Object with no prototype chain (the prototype chain is broken).

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

Person.prototype.nation = 'han'

Person.prototype.say = function() {
  console.log(`My name is The ${this.age}`)}function New() {
  Constructor = [].shift.call(arguments)
  
  // var obj = {}
  // var obj = new Object()
  // var obj = Object.create(null)
  var obj = Object.create(Constructor.prototype)
  
  // obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, 'Ming'.25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()
Copy the code

The object thus created has its original prototype chain, which was assigned to it by the constructor we passed in.

In other words, when we create a new object, we assign it a prototype chain — the newly created object inherits its constructor!

See here, small partners long long relieved, have the ability you to arrange a pit for me!

Now that we have seen this, we must believe that we are not far from the final dawn!

I’d like to say that the pit is gone, but for the spirit of programmer nitpicking! Oh, no, is the spirit of excellence, we have to be a little wordy!!

The following code in the last step has already been noticed:

Constructor = [].shift.call(arguments)
var obj = Object.create(Constructor.prototype)
Copy the code

Can’t use the following code instead?

var obj = Object.create(null)
Constructor = [].shift.call(arguments)
obj.__proto__ = Constructor.prototype
Copy the code

To put it another way, the two pieces of code basically say the same thing: they both stereotype the constructor to the newly created object. But why does the second code report an error (unable to access properties on the prototype chain)?

This problem is very basic, seriously to study the js underlying APIObject. Create and prototype chain knowledge, you will understand the truth. Friends can pull to the end of the article, I have recorded the key points, for everyone’s reference.


Now, let’s tease out what the final New function does, as discussed in this article — what does the New operator do?

  1. Gets the first argument (constructor) passed in by the call to New, temporarily denoted asConstructor;
  2. useConstructorThe prototype chain is combinedObject.createtocreateAn object, where the prototype chain of the new object isConstructorThe prototype object of a function; (In conjunction with our discussion above, to access properties and methods above the prototype chain, use the __proto__ property of the instance object.)
  3. changeConstructorThe function this points to the newly created instance object, and thencallMethod recallConstructorFunction to assign attributes and methods to the new object; (To access constructor properties and methods, use call or apply, as we discussed above.)
  4. Returns the newly created object, asConstructorAn instance object of the function.

Now I, let’s answer the question at the beginning of this article, is new used to create objects?

Now we can bravely answer that new is used for inheritance, and the Object that creates it is object.create (null). With the new operator, we use the newly created object to inherit properties and methods from its constructor, as well as from its prototype chain!


Write at the end:

A little bit more about prototype chains:

  1. Functions in JavaScript are also objects, and objects need to be created using functions except for literals.
  2. The prototype attribute adds shareable methods and attributes to functions and objects, while __proto__ is a prototype chain way of finding a function or object.
  3. Prototype and __proto__ both refer to prototype objects;
  4. Every function (including constructors) has a prototype attribute that points to the function’s prototype object.
  5. Any instantiated object has a __proto__ attribute that points to the prototype object of the instantiated object’s constructor.

Add a few things about object.create () :

  1. Object.create(null) creates a truly empty Object with no prototype chain, which does not have any of the features and functions of js native objects. As in: even by artificially assigning values (newObj.__proto__ = constructor.prototypeAssigning a stereotype chain to this object does not allow the stereotype chain to look up attributes layer by layer, because the object appears to have one"__proto__"Property, but it has not directly or indirectly inherited from Object.prototype, also can not have js native Object (Object) features or functions;
  2. This API works with Object.defineProperty to create custom objects that are extremely flexible in javascript;
  3. The API is a way to implement inheritance;
  4. .

Original text: blog. Xieyangogo. Cn / 2019/04/12 /…