When I looked at the prototype and prototype chain before, I was always in a daze. Recently, I took the opportunity to rearrange it and found it was not difficult

Let’s go step by step and thoroughly understand the prototype and prototype chain

1, the prototype

  • eachInstance objectsThere is aconstructorProperty pointing to its constructor
function Person(name) {
	this.name = name
}
var person = new Person('Steve') // Custom constructor
var string = new String('Hello') // The native constructor

console.log(person.constructor === Person)
console.log(string.constructor === String)

/* * Result: * true * true **/
Copy the code
  • Each function object (including the constructor) has a Prototype property that points to the function’s prototype object

    The constructor property of the prototype object points to the function itself

function Person(name) {
	this.name = name
}
var person = new Person('Steve') // Custom constructor
var string = new String('Hello') // The native constructor

console.log(Person.prototype.constructor === Person)
console.log(String.prototype.constructor === String)

/* * Result: * true * true **/
Copy the code
  • Each object has a [[prototype]] private property that points to its constructor’s prototype object, but this property is not accessible

    Some browsers (such as Chrome) provide __proto__ attributes for accessing [[prototype]] private attributes

function Person(name) {
	this.name = name
}
var person = new Person('Steve') // Custom constructor
var string = new String('Hello') // The native constructor

console.log(person.__proto__ === Person.prototype)
console.log(string.__proto__ === String.prototype)
console.log(Person.__proto__ === Function.prototype)
console.log(String.__proto__ === Function.prototype)

/* * Result: * true * true * true * true **/
Copy the code
  • The constructor attribute points to Function and the __proto__ attribute points to function.prototype

    Because constructors are created using new Function, they are all instance objects of Function, including Function and Object

function Person(name) {
	this.name = name
}

console.log(Person.constructor === Function)
console.log(Person.__proto__ === Function.prototype)
console.log(String.constructor === Function)
console.log(String.__proto__ === Function.prototype)

console.log(Function.constructor === Function)
console.log(Function.__proto__ === Function.prototype)
console.log(Object.constructor === Function)
console.log(Object.__proto__ === Function.prototype)

/* * Result: * true * true * true * true * true * true * true * true **/
Copy the code
  • The prototype property of all constructors except Object has its __proto__ attribute pointing to Object.prototype

    The __proto__ attribute of Object’s prototype property points to NULL

function Person(name) {
	this.name = name
}

console.log(Person.prototype.__proto__ === Object.prototype)
console.log(String.prototype.__proto__ === Object.prototype)

console.log(Function.prototype.__proto__ === Object.prototype)
console.log(Object.prototype.__proto__ === null)

/* * Result: * true * true * true * true **/
Copy the code

And just to wrap it up with a picture, it looks a little bit complicated, but once you find the pattern, it’s pretty easy

Note where the constructor attribute, prototype attribute, and __proto__ attribute appear

2. Prototype chain

(1) Prototype chain

As we said above, all objects have a __proto__ attribute, and that __proto__ attribute points to a prototype object

Because a prototype object is also an object, and this prototype object also has a __proto__ property, we call this relationship a prototype chain

(2) Attribute access

When you need to access an object’s properties, you start with the object and return to it if it can be found

If not, the object’s __proto__ attribute points to the prototype object, and if it can be found, returns there

If not, look up the prototype object until the __proto__ attribute points to NULL, the top of the prototype chain

If all stereotype objects on the stereotype chain do not have this property, undefined is returned

function Person(name) { this.name = name }
Person.prototype.getName = function() { return this.name }
var person = new Person('Steve')

var name = person.getName() // Found in Person.prototype
var description = person.toString() // Find it in object.prototype
var age = person.age // Cannot be found in the prototype chain

console.log(name)
console.log(description)
console.log(age)

/* * Result: * Steven * [object object] * undefined **/
Copy the code

(3) Attribute detection

  • inOperator: Checks if the property is on the object’s stereotype chain
  • hasOwnPropertyMethod: Checks if the attribute comes from the object itself
function Person(name) { this.name = name }
Person.prototype.getName = function() { return this.name }
var person = new Person('Steve')

console.log('age' in person)
console.log('name' in person)
console.log('getName' in person)
console.log('toString' in person)

console.log(person.hasOwnProperty('age'))
console.log(person.hasOwnProperty('name'))
console.log(person.hasOwnProperty('getName'))
console.log(person.hasOwnProperty('toString'))

/* * Result: * false * true * true * true * true * false * true * false **/
Copy the code

3. Why use prototypes

Inheritance in JavaScript is based on stereotypes, which save memory by sharing data between different instance objects

function Person(name) { this.name = name }
Person.prototype.getName = function() { return this.name }
var person1 = new Person('Steve')
var person2 = new Person('Steven')

// Different instance objects person1 and person2 can use getName (shared data) defined on Person.prototype
// To avoid repeating the getName definition on each instance object (to save memory)
var name1 = person1.getName()
var name2 = person2.getName()
console.log(name1)
console.log(name2)

/* * Result: * Steve * Steven **/
Copy the code