preface

What can you learn from this article?

  • The f.protoType attribute (not to be confused with [[Prototype]]) assigns the new object’s [[Prototype]] value when new F is called.

  • The value of f.protoType is either an object or null: nothing else works.

  • The “prototype” attribute has this special effect only if a constructor function is set and called by new.


Remember that a constructor such as new F() can be used to create a new object.

If f.protoType is an object, the new operator uses it to set the new object [[Prototype]].

Please note:

JavaScript has had prototype inheritance since the beginning. This is one of the core features of the JavaScript programming language.

But in the past, there was no way to access it directly. The only reliable way to do this is the constructor’s “Prototype” property, described in this chapter. Many scripts still use it.

Note that f.protoType here refers to a regular property of F called “prototype”. This sounds similar to the term “stereotype,” but here we’re actually referring to a generic property that has that name.

Here’s an example:

let animal = {
  eats: true
};

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = animal;

let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal

alert( rabbit.eats ); // trueCopy the code

Set rabbit. prototype = animal when creating a new Rabbit, assign its [[prototype]] value to animal.

Here’s the result:



In the figure above, “Prototype” is a horizontal arrow indicating a normal property, and [[Prototype]] is vertical indicating that Rabbit inherits from animal.

F.prototypeUse only in
new F

The f.protoType attribute is only used when new F is called, and it assigns the [[Prototype]] value of the new object. After that, there is no connection between f.protoType and the new object. Think of it as a one-time gift.

If the f.protoType attribute changes after creation (f.protoType = < Another Object >), then the new object created by new F will have the new object as [[Prototype]], but the existing object will keep the old value.

The default F. protoType, constructor property

Every function has a “prototype” attribute, even if we don’t provide it.

The default “prototype” is an object with only the property constructor, which points to the function itself.

Like this:

function Rabbit() {}

/* default prototype Rabbit.prototype = { constructor: Rabbit }; * /Copy the code



We can check:

Normally, if we do nothing, the constructor attribute can be used for all Rabbits using [[Prototype]] :



We can use the constructor property to create a new object that uses the same constructor as the existing object.

Like this:

function Rabbit(name) {
  this.name = name;
  alert(name);
}

let rabbit = new Rabbit("White Rabbit");

let rabbit2 = new rabbit.constructor("Black Rabbit");Copy the code

This is handy when we have an object, but don’t know which constructor it uses (for example, it comes from a third-party library), and we need to create another similar object.

But the most important thing about “constructor” is…

… JavaScript by itself is not guaranteed to be correct"constructor"Function value.

Yes, it exists in the function’s default “prototype”, but that’s about it. What happens after that — it’s up to us.

In particular, if we replace the entire default prototype, there will be no “constructor” in it.

Such as:

function Rabbit() {}
Rabbit.prototype = {
  jumps: true
};

let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // falseCopy the code

Therefore, to ensure correct “constructor”, we can choose to add/remove attributes to the default “prototype” instead of overwriting it entirely:

function Rabbit() {}

// Don't overwrite Rabbit.prototype
// You can add content to it
Rabbit.prototype.jumps = true
/ / the default Rabbit. Prototype. The constructor is preservedCopy the code

Alternatively, you can manually recreate the constructor property:

Rabbit.prototype = {
  jumps: true.constructor: Rabbit
};

// This constructor is also correct because we added it manuallyCopy the code

conclusion

In this chapter, we briefly looked at how to set [[Prototype]] for objects created through constructors. We’ll see more advanced programming patterns that rely on this later.

It’s all very simple, just need to remember a few key points to grasp clearly:

  • F.prototypeAttribute (do not associate it with[[Prototype]]Mixed up) innew FOf a new object when called[[Prototype]]The assignment.
  • F.prototypeThe value of is either an object ornull: All other values have no effect.
  • "prototype"Property only if a constructor function is set and passesnewWhen called, it has this special effect.

Prototype is nothing special on a regular object:

let user = {
  name: "John".prototype: "Bla-bla" // There is no magic here
};Copy the code

By default, all functions have f.constructor = {constructor: F}, so we can get an object’s constructor by accessing its “constructor” property.

A few little chestnuts

Degree of importance: *****

In the code below, we create new Rabbit and try to modify its Prototype.

Initially, we had the following code:

function Rabbit() {}
Rabbit.prototype = {
  eats: true
};

let rabbit = new Rabbit();

alert( rabbit.eats ); // trueCopy the code

  1. We added a string (emphasis). Now what will the Alert show?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype = {};
    
    alert( rabbit.eats ); // ?Copy the code

  2. … What if the code looks like this?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    Rabbit.prototype.eats = false;
    
    alert( rabbit.eats ); // ?Copy the code

  3. How about something like this?

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete rabbit.eats;
    
    alert( rabbit.eats ); // ?Copy the code

  4. The final variation:

    function Rabbit() {}
    Rabbit.prototype = {
      eats: true
    };
    
    let rabbit = new Rabbit();
    
    delete Rabbit.prototype.eats;
    
    alert( rabbit.eats ); // ?Copy the code

The solution

The answer:

  1. True.

    Rabbit.prototype’s assignment sets [[prototype]] for the new object, but it doesn’t affect existing objects.

  2. False.

    Objects are assigned by reference. The object from Rabbit.prototype is not copied; it is still a single object referenced by Rabbit.prototype and Rabbit’s [[Prototype]].

    So when we change its content through one reference, it is also visible to other references.

  3. True.

    All DELETE operations apply directly to the object. Here delete Rabbit. eats attempts to remove the EATS attribute from rabbit, but the Rabbit object does not have an EATS attribute. So this operation doesn’t have any effect.

  4. Undefined.

    The eats attribute was removed from Prototype; it is not present in Prototype.

Create an object using the same constructor

Degree of importance: *****

Imagine that we have an object obj created by a constructor — we don’t know which constructor to use, but we want to use it to create a new object.

Can we do that?

let obj2 = new obj.constructor();Copy the code

Give an example of an OBJ constructor that would make such code work. Give an example of how such code might not work correctly.

The solution

This approach can be used if we are sure that the “constructor” property has the correct value.

For example, if we don’t touch the default “prototype”, this code will definitely work:

It works because the User. The prototype. The constructor = = User.

… But if someone overwrites User.prototype and forgets to recreate constructor to reference User, this code will fail.

Such as:

function User(name) {
  this.name = name;
}
User.prototype = {}; / / (*)

let user = new User('John');
let user2 = new user.constructor('Pete');

alert( user2.name ); // undefinedCopy the code

Why is user2.name undefined?

This is the workflow for new user.constructor(‘Pete’) :

  1. First of all, it’s inuserTo look forconstructor. Didn’t find it.
  2. It then traces the prototype chain.userThe prototype isUser.prototypeIt also has nothing.
  3. User.prototypeThe value of is a normal object{}, the prototype of this object isObject.prototype. andObject.prototype.constructor == Object. So I used it.

Finally, we have let user2 = new Object(‘Pete’). The built-in Object constructor ignores arguments, and it always creates an empty Object like let user2 = {}, which is what we end up with in User2.