Original text: zhehuaxuan. Dead simple. IO / 2019/02/21 /… Author: zhehuaxuan

The words written in the front

The front entry is relatively simple and the ceiling may be relatively low relative to other directions. But a good front end is still in demand in the market. Very few people can stand on a pyramid.

At present, the front end has been one and a half years, and the knowledge stack in the company is relatively backward, and the employment situation is not optimistic, so it is necessary to ponder on their own and advance to the middle and senior front end. I will publish the JavaScript Advanced Series, on the one hand, as a process of monitoring their own learning, on the other hand, it will also give some inspiration to children.

The process of creating new objects in JavaScript

Define a function in ES5 to create an object as follows:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    return name;
}
var person = new Person("xuan");
console.log(person.name);// Output: Xuan
console.log(person.getName());// Output: Xuan
Copy the code

We see that when we create a new object, we can access the properties in the constructor that point to this, and we can access the properties in the prototype. The JavaScript call to new consists of the following four steps:

  1. Generates a new empty object
  2. Link the empty object into the stereotype
  3. Binding this
  4. Return a new object

Here’s how I create objects:

function create(){
    //Todo
}
person = create(Person,"xuan");//create(ObjectName,... arguments)
Copy the code

We use the function shown above to simulate the new keyword.

First, create an object:

function create(){
    var obj = new Object(a);return obj;
}
person = create(Person,"xuan");
Copy the code

Now that an object has been created and returned, it must now print out a normal object, since the process has not yet finished, so let’s move on.

Step 2 Link to the prototype:

function create(){
    var obj = new Object(a);var constructor = [].shift.call(arguments);
    console.log(constructor);
    console.log(arguments);
    obj.__proto__ = constructor.prototype;
    return obj;
}

person = create(Person,"xuan");
Copy the code

The constructors and arguments are now printed out. No problem!

Step 3 bind this as follows:

function create() {
  let obj = new Object(a);let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  constructor.apply(obj, arguments);
  console.log(obj);  
  return obj;
}
person = create(Person,"xuan");
Copy the code

Print the result to look like a new object.

Now change the constructor code:

function Person(name){
    this.name = name;
    return {
        name:"abc"}}var person = new Person("xuan");
console.log(person);
console.log(Object.prototype.toString.call(person));
Copy the code

The effect is as follows:

function create() {
  let obj = new Object(a);let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  //constructor.apply(obj, arguments);
  let res = constructor.apply(obj, arguments);
  if(res){
     return res;
  }else{
     return obj;
  }
}
person = create(Person,"xuan");
Copy the code

The effect is as follows:

Now let’s think about the three types of res returned here: undefined, primitive, and object.

If res is undefined, return obj;

If res is primitive we also return obj;

If res is an object we return the RES object;

To sum it up:

Return res if the returned RES Object is of type Object, obj otherwise. Of course, other criteria are also available. The final code optimization is as follows:

function create() {
  let obj = new Object(a);let constructor = [].shift.call(arguments)
  obj.__proto__ = constructor.prototype
  //constructor.apply(obj, arguments);
  let res = constructor.apply(obj, arguments); return res instanceof Object? res:obj; } person = create(Person,"xuan");Copy the code

A few questions

Is the code now perfect? Let’s start with a few questions.

  1. Is the Object created by new Object() pure?
  2. Why use [].shift.call() for parameter splitting? Is arguments an array?

Is the Object created by new Object() pure?

What is purity in the first place? We define an object whose __proto__ attribute is empty as a pure object.

The prototype chain of obj has changed in step 2, so it doesn’t matter what the prototype chain before it looks like, but to keep the Object pure, we need to introduce object.create (), which creates a new Object and uses the existing Object to provide the __proto__ of the newly created Object. Let’s take a look:

var person1 = Object.create({});
Copy the code

Print the following:

We see that person1’s __proto__ refers to the {} object, so in the above code we modify it directly as follows:

function create() {
  let constructor = [].shift.call(arguments);
  let obj = Object.create(constructor.prototype);
  let res = constructor.apply(obj, arguments); return res instanceof Object? res:obj; } person = create(Person,"xuan");Copy the code

Why use [].shift.call() for parameter splitting? Is arguments an array?

Arguments is the argument passed to the function. Let’s print out:

console.log(arguments);
console.log(Object.prototype.toString.call(arguments));
console.log(arguments instanceof Array);
Copy the code

The results are as follows

Not an array. We expand it out and see that it looks like an array, and we look it up and see that this object is an array of classes. There is no shift function. Calling shift directly returns an error. Another idea is to use array. from(arguments) to convert arguments to an Array and then call shift. But here we use Apply best. So the following code is the best code to emulate new Object() :

function create() {
  let constructor = [].shift.call(arguments);
  let obj = Object.create(constructor.prototype);
  let res = constructor.apply(obj, arguments); return res instanceof Object? res:obj; } person = create(Person,"xuan");Copy the code

There are better implementation methods, please big guys don’t hesitate to pat brick!