Back to topics

Basics: JS Gap Filling 1 (Prototype)

Prototype Object

Let's look at an example:

const person = {
	arms: 2,
	legs: 2
}

// Set person as father's prototype via Object.create
const father = Object.create(person)console.log(father) //{}

console.log(father.arms) //2
console.log(father. __proto__ === person) //true// The second argument of Object.create can set properties

const son = Object.create(father, {
	name: {
		value: "xixi",
		enumerable: true
	}
})

console.log(son.__proto__ === father) //true
console.log(son.__proto__.__proto__ === person) //true

Properties not found on the object itself are looked up on the prototype at runtime.

The advantage is simplicity; the disadvantage is that Object.create requires manually setting properties and initialization logic, which is less convenient than new + constructor.

Constructor Functions

Why is new more convenient? What does new + constructor do?

function Computer (name, price){
  // 1. Create an empty object obj = {}
  // 2. Point obj.__proto__ to Computer.prototype
  // 3. Execute Computer.call(obj, name, price)
  // 4. If the constructor doesn't return an object, return obj
	this.name = name
	this.price = price
}

// Methods are usually placed on the prototype
// Ten thousand instances share one method, saving memory.
// More importantly, methods on prototype are shared along the inheritance chain, while those on this are not.
Computer.prototype.showPrice = function() {
  console.log(this.price)
}

Triangular Relationship

The prototype object, constructor function, and instance object form a triangular relationship as shown.

image
  1. Each object can access its prototype object via __proto__.

  2. The constructor's prototype points to an object that is the instance's prototype object.

  3. The prototype object's constructor property points back to its constructor function.

As an analogy: A function is like a factory:

- On the day the factory is built, a display cabinet (prototype) is automatically placed next to it, displaying samples (constructor points back to the factory itself).

- The factory's assembly line (new) connects each product to the display cabinet (__proto__ points to prototype).

- Products hold their own unique attributes (like this.name), and the display cabinet holds shared things (methods).

Double Triangular Relationship

In the previous triangular relationship, we clarified that any object has a constructor function, and the object's __proto__ points to the constructor's prototype.

Based on this, we extend 4 rules.

Rule 1: Any function's __proto__ is Function.prototype.

Because functions are also objects, they have __proto__, which points to the constructor Function's prototype, following the same logic as the previous triangular relationship.

Rule 2: The end of any ordinary object's prototype chain is Object.prototype, and beyond that is null.

The prototype chain works this way; even for const obj = {}, obj.__proto__ is Object.prototype.

Object.prototype.__proto__ === null // true,

Rule 3: Every prototype object must have a constructor property pointing back to its own function.

Foo.prototype.constructor === Foo // true