In-depth analysis of prototypes

Review the writing of the prototype pattern

function Person(){}
Person.prototype.name = 'test';
Person.prototype.sayName = function(){
    console.log(this.name);
};
let person1 = new Person();
let person2 = new Person();
person1.sayName = () = >{}
person1.sayName(); // "test"
person2.sayName(); // "test"
console.log(person1.sayName === person2.sayName); // true
Copy the code

Every time a function is created, a new object is created

Whenever a function is created, a Prototype attribute (pointing to the prototype object) is created for that function according to certain rules

Person.prototype points to the prototype object

By default, all stereotype objects automatically get a property called constructor that refers back to the constructor associated with them.

console.log(Person.prototype.constructor == Person) // true
Copy the code

Each time an instance of the constructor is created by calling new, the [[Prototype]] pointer inside it is assigned to the constructor’s Prototype object. In the browser we can get the prototype object from the __proto__ property of the instance.

(There is no standard way to get the [[Prototype]] feature in scripts, but the browser exposes the __proto__ attribute on an object, which can be used to access the object’s Prototype in code.)

console.log(Person.prototype === person1.__proto__) // true
Copy the code

Implicit and explicit archetypes

We often call the stereotype pointer of an instance an implicit stereotype and the stereotype pointer of a function an explicit stereotype.

These two Pointers point to the same prototype object, only the pointer names are different.

The key to understand is this:

There is a direct relationship between the instance and the constructor stereotype, but not between the instance and the constructor. Simply put, there is no constructor pointer to the instance’s own property that points to the constructor.

console.log(person1.hasOwnProperty('constructor')) // false
Copy the code

So to sum up:

Constructor, prototype object, and instance are different objects. The constructor creates and associates a prototype object with the new constructor. The constructor gets the prototype by pointer to prototype, and the instance gets the prototype by __proto__.

Instances are only directly related to prototype objects.

Prototype chain

An object has an implicit stereotype pointer to a stereotype object.

A prototype object is also an object, so it is looped until the end is null, and a chain is formed.

console.log(person1.__proto__.__proto__.__proto__) // null
Copy the code

Here’s another example of a full view of the prototype chain

function A(){}
let a = new A()
function B(){}
// let B be called the constructor of a instance object
B.prototype = a
let b = new B()
// Then the prototype chain of b is b.__proto__
console.log(b.__proto__ === B.prototype&&
b.__proto__.__proto__ == A.prototype &&
b.__proto__.__proto__.__proto__ === Object.prototype &&
b.__proto__.__proto__.__proto__.__proto__ === null) // true
Copy the code

That is, every object has a chain of implicit stereotypes that starts with null stereotypes and ends with null stereotypes. Object. Prototype = null;

Property access rules

When an object accesses a property, it looks in its own property. If it can’t find one, it looks in its prototype chain. If it finds one, it returns.

Person1.sayname is found in person1.__proto__.

console.log(person1.sayName === person1.__proto__.sayName) // true
Copy the code

Properties can be obtained from prototypes, but can they be modified directly from examples?

While an instance can get a value on a prototype object, it cannot be overridden.

Assignment is simply equivalent to adding an attribute of the same name as the prototype object to the instance itself, creating a “masking” effect. Such as:

person1.sayName() // 'test'
person1.sayName = () = >console.log('rewrite');
person1.__proto__.sayName() // 'test'
person1.sayName() // 'override'
Copy the code

How do I remove stereotype attributes after they are “Shadowed”

Delete the instance’s own attributes using the DELETE operator

delete person1.sayName
person1.sayName() // 'test'
Copy the code

The way you get your own and stereotype attributes

HasOwnPropertype and in, the former is judgment own and the latter is judgment own and stereotype enumerable

Combine existing capabilities to implement a function that can only enumerate stereotypes.

function hasProtoProperty(obj,name){
	return! obj.hasOwnProperty(name)&& (namein obj)
}
Copy the code

Properties that can only be enumerated

Self with the prototype

for in

Only their own

Object.keys

Only a prototype

Use the above custom hasProtoProperty

All the properties

Own property only

Use the static method of Object.

Object. GetOwnPropertyNames () for the attribute named character.

Object. GetOwnPropertySymbols () called Symbol for attributes.

Attribute enumeration order

The order of the for in loop, object.keys () enumeration is indeterminate, depends on the JS engine and varies from browser to browser.

Object. GetOwnPropertyNames (), Object. GetOwnPropertySymols (), the Object, the assign () is a sequence.

The order rules: numeric keys are first in ascending order, then characters and symbolic keys are both literals and insertions in first-come-last order.

let o = {
	2:2.jim:'jim'.1:1.tony:'tony'
};
o['lily'] = 'lily';
console.log(o) // {1: 1, 2: 2, jim: 'jim', tony: 'tony', lily: 'lily'}
Copy the code

Dig deeper into constructors

Let’s review how constructors are written

function CreatePerson(name){
	this.name = name;
	this.toDo = function(){
            console.log(this.name); }}let person1 = new CreatePerson('test')
Copy the code

Constructors are also functions

The only difference between a constructor and a normal function is how it is called. Any function that calls with the new operator is a constructor.

What’s different about constructors creating objects

First, constructors are written in the shadow of the factory pattern, but the constructor pattern has some key changes in comparison:

  • Objects are not explicitly created, but instead are usedthis
  • Properties and methods are directly assigned to thisthis
  • Remove thereturnBut the way it’s called has changednewThe call.

So that’s the standard way.

Constructors start with a capital letter

One detail is that constructors have uppercase names. This is the convention: constructors should always start with uppercase names. There is no real difference between constructors and functions.

Using thenewThe way you call the constructor, what you’re secretly doing

  1. Create a new object in memory called “little New”
  2. Internal of the new object “little New”[[prototype]]Features thatImplicit stereotypeThe partners assigned to the constructor are the function’sExplicit prototypeprototypeThis behavior is simply ownership: the new object “shin” recognizes the constructor’s partner as its creator.
  3. Inside the constructorthisI’m pointing to this new object, shin.
  4. This completes the execution of the code inside the function.
  5. If the constructor does not return, or returns an empty value, the new object is returned. (If a non-null value is returned, the new object “new” is forcibly returned instead and the ability to identify the object is lost).

So the new object created by the constructor is this little new object.

Resolved the problem of resolving object identification in factory mode

Constructor can judge its own type by judging the value of the instance’s constructor.

console.log(person1.constructor === createPerson) // true
Copy the code

We should actually make it clear that constructor is an attribute of the stereotype.

console.log(person1.hasOwnProperty('constructor')) // false
console.log(person1.__proto__.constructor === createPerson) // true
Copy the code

But there is the possibility of tampering with constructor,

person1.__proto__.constructor  = () = >{}
console.log(person1.constructor === createPerson) // false
Copy the code

So it’s not very reliable

The instanceof operator

Usage is:

console.log(person1 instanceof createPerson) // true
Copy the code

The left side of the instanceof operator is an instance object and the right side is a constructor.

How do you understand that?

Recall the prototype chain: then any object has a prototype chain that starts with an implicit prototype and ends with null.

Then combining the prototype chain can be understood as:

Object’s stereotype chain has an object that is an explicit stereotype of this constructor

function A(){}
let a = new A()
function B(){}
// let B be called the constructor of a instance object
B.prototype = a
let b = new B()
// Then the prototype chain of b is b.__proto__
console.log(b.__proto__ === B.prototype&&
b.__proto__.__proto__ == A.prototype &&
b.__proto__.__proto__.__proto__ === Object.prototype &&
b.__proto__.__proto__.__proto__.__proto__ === null) // true

console.log(b instanceof B) // true
console.log(b instanceof A) // true
console.log(b instanceof Object) // true
Copy the code