Look at JavaScript, those missing or confusing knowledge points

Posted by Ansel_Tk1 on Sat, 25 Dec 2021 10:54:53 +0100

Prototype and inheritance

Prototype inheritance is to enable an object to use some properties on another object. It is required that this object does not have this property. If you have this attribute, you can directly use your own (except the accessor attribute).

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

For example, the above two objects, animal and rabbit, I want rabbit to use the eat attribute. What to do without changing rabbit? There are many ways. Let's take a look one by one.

[[Prototype]]

This property is a hidden property of JavaScript. Its value can only be null or a reference to another object. Note that this is a reference, not a copy. There are not many problems prone to object reference here.

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

console.log(rabbit.eats);
rabbit.__proto__ = animal; // Set rabbit [[Prototype]] = animal'
console.log(rabbit.eats);

// Because of the reference of the object, the animal has an additional attribute name
rabbit.__proto__.name = "jack";

console.log(animal);

JavaScript prototype and inheritance

Here we can say "animal is the prototype of rabbit", or "the prototype of rabbit is inherited from" animal ".

Therefore, if {animal} has many useful properties and methods, they will automatically become available in {rabbit}. This property is called inheritance.

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

console.log(rabbit.eats);
rabbit.__proto__ = animal; // Set rabbit [[Prototype]] = animal'
console.log(rabbit.eats);

// Because of the reference of the object, the animal has an additional attribute name
rabbit.__proto__.name = "jack";

console.log(animal);

// Long, long prototype chain
let longer = {
  longer: 10,
  __proto__: rabbit
};

console.log(longer.eats);

There are only two limitations:

  1. References cannot form a closed loop. If we try to distribute in a closed loop__ proto__, JavaScript throws an error.
  2. __ proto__ The value of can be an object or null. Other types are ignored.

Note:__ proto__ It is a getter/setter left by [[Prototype]] for historical reasons. The two are essentially different. But__ proto__ It's a little out of date. Now we generally use object getPrototypeOf/Object. Setprototypeof to replace__ proto__ Go to get/set Prototype. But both work, and__ proto__ Easier.

Add the existing properties on the prototype on the object

let animal = {
  eats: true,
  walk() {
    /* rabbit This method will not be used */
  }
};

let rabbit = {
  __proto__: animal
};

rabbit.walk = function() {
  alert("Rabbit! Bounce-bounce!");
};

rabbit.walk(); // Rabbit! Bounce-bounce!

There is another walk function on the prototype animal. The prototype of the object rabbit is inherited from animal. Now add a function named walk on rabbit and call this function at this time. You won't find it on the prototype, because you have this method.

However, the accessor (get/set) property is an exception.

let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith

// setter triggers!
admin.fullName = "Alice Cooper";

alert(admin.fullName); // The content of Alice Cooper, admin has been modified
alert(user.fullName);  // John Smith, user's content is protected

If the attribute added on the object is the accessor attribute set by the prototype, the attribute on the object will act on the prototype and directly call the getter/setter of the prototype.

Notice the last two lines of code. admin and user have different fullnames. Then why is it different? The reason is because of this in the accessor property. Always remember one thing here: this always points to The object before the symbol. Simply put, it refers to who calls it. (except for the arrow function (the accessor property cannot use the arrow function) and the function using call, apply and bind).

Traversal object

Traversing objects, let's talk about for In loop and object The difference between keys.

let animal = {
  eats: true
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

// Object.keys returns only their own key s
console.log(Object.keys(rabbit)); // jumps

// for..in traverses itself and the inherited keys
for(let prop in rabbit) console.log(prop); // jumps, then eats

The difference is as in the code comments above. So, in object We all need to use the keys method before it appears obj.hasOwnProperty(key) To judge. All objects have a hasOwnProperty attribute, which is inherited from the Object object

Function.prototype

function Fn() {}
console.log(Fn.prototype); // { constructor: Fn() {} }
console.log(Fn.prototype.constructor); // FN() {}
const fn = new Fn();
console.log(fn.__proto__); // { constructor: Fn() {} }

Each function has a prototype attribute. The value * * of this attribute is not necessarily * * equal to {constructor: XXX() {}}. Because we can modify it manually.

Fn.prototype = {name: 'Jack'};

So instead of writing like this, we add attributes to the prototype

Fn.prototype.name = 'Jack';

Or modify the constructor and specify it again later

Fn.prototype = {
	constructor: Fn,
	name: 'Jack'
};

The prototype value of the constructor is {constructor: Fn() {}}. The instance value of the constructor is also {constructor: Fn() {}}. But the two are different (memory addresses are different), but their constructors are the same, both pointing to this constructor.

function Fn() {}
let fn = new Fn();
console.log(Fn.prototype === fn.__prpto__) // false
console.log(Fn.prototype.constructor === fn.constructor) // true

Therefore, when we do not know the constructor of an object, we can use the constructor to create it (note that it has not been modified).

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

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

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

JS prototype chain has a very classic diagram