es5 instance __proto__ (prototype chain) prototype (prototype object) {constructor: constructor}

Looking at this picture now, it is starting to become confusing, so I will briefly review the basic content of the prototype to basically understand the context of this picture.
Insert image description here
Let’s first introduce a basic concept:

function Person() {
    
    }

Person.prototype.name = 'KK';

let person1 = new Person();

In the above example,

Person is called a constructor (the function is constructed and called, for the convenience of the following, it is called 构造函数)
Person.prototype is called 原型对象
person1 of Person 实例.
TL;DR.
The constructor will contain a prototype attribute by default, and its value points to the prototype object, that is, Parent.prototype = Parent.prototype
By default, constructor.prototypeobject.constructor = constructor Parent.prototype.constructor = Parent. But if there is a clear rewrite, it may not be this direction.
instance.proto = constructor.prototype object person.proto = Parent.prototype There
is a direct link between an instance and the constructor's prototype object, but there is no direct link between the instance and the constructor.

prototype object

When creating a function, the constructor of Function generates a function object bound to an object property prototype that stores inherited characteristics. When Function's constructor generates a function object, it runs similar code:

this.prototype = {
    
     constructor : this}

The value of this attribute prototype is that by default, only an object containing the constructor attribute will be obtained, and the rest of the methods are inherited from Object. This object is the prototype of the object created by calling the constructor. The advantage of using the prototype object prototype is that the properties and methods defined on it can be shared by instances of the object. The values ​​originally assigned to object instances in the constructor can be assigned directly to their prototypes.

function Parent() {
    
    }

// 定义在 Parent.prototype 的值,可以被该对象实例共享。
Parent.prototype.name = 'KK';

Parent.prototype.sayHi = function () {
    
    
  console.log('hi,', this.name);
};

// Parent 对象的实例 person1, person2
let person1 = new Parent();
let person2 = new Parent();

person1.sayHi(); // hi, KK
person2.sayHi(); // hi, KK

The constructor contained in the prototype points to the constructor associated with it. As shown in the figure below, we can see that the prototype of Parent points to Parent.prototype (seems like nonsense), and the constructor in Parent.prototype points to Parent. In other words, a circular reference is formed between the constructor and the constructor of the constructor's prototype object.

Insert image description here
Insert image description here

proto | [[prototype]]

Before we start, let's review the new operator. The Object.create() method creates a new object, using an existing object to provide the proto of the newly created object . Through this step, the link between the instance object and the prototype object of the constructor func is realized, so as to facilitate access to the methods or properties defined on the prototype object.

function myNew(func, ...args) {
    
    

		// 1. 在内存中创建一个新对象
    const obj = Object.create()
		
		// 2. 在这个新对象内部的 [[ prototype ]] 特性被赋值为构造函数的 prototype 属性
    obj.__proto__ = func.prototype
		
		// 3. 构造函数内部的 this 被赋值为这个新对象
		// 执行构造函数内部代码
    let result = func.apply(obj, args)
    
		// 4. 如果构造函数返回一个非空对象,则返回该对象,都则则返回刚刚创建的新对象
		return result instanceof Object ? result : obj
}

Among them, step 1.2 can be combined to become const obj = Object.create(func.prototype), the Object.create() method will create a new object and associate the proto of the new object with the specified object.

After reviewing the new operator, let's continue with our just example, person1 and person2, which are object instances created by Parent. Every time we create a new instance through the constructor, the instance will be linked to the prototype object of the constructor through proto , and the first part of the figure below.

function Parent() {
    
    }

// Parent 对象的实例 person1, person2
let person1 = new Parent();
let person2 = new Parent();

However, we can see from the figure that the proto of the object instance person directly points to the prototype object Parent.prototype of its constructor. The relationship between the instance and the prototype object is actually a reference relationship, not a new prototype object. a copy of. At the same time, we can also see that there is no direct connection between object instances and constructors.

Insert image description here

console.log(person1.__proto__ === Parent.prototype); // true
console.log(person1.__proto__.constructor === Parent); // true

console.log(person1 instanceof Parent)

Prototype chain lookup

We often mention searching along the prototype chain, but what is a prototype chain search? How to search the prototype chain?

For the default get operation of an object, it will start searching from the object instance first, and return the given object attribute value if found, otherwise the search will enter the prototype object, start searching on the prototype object, and then return the corresponding value. As shown in the previous section, we can see that when we enter person.name, he will first enter person to search. When the search is fruitless, enter Parent.prototype to search again. This is how prototypes can share properties and methods between multiple object instances.

function Parent() {
    
    
  name: 'Parent';
}

Parent.prototype.nickname = 'KK';

Parent.prototype.sayHi = function () {
    
    
  console.log(this.nickname);
};

let person1 = new Parent();

person1.nickname = 'person1';
person1.sayHi();

console.log(person1.nickname); // person1 

If an attribute with the same name as the prototype object is added to the instance object, the attribute value will be created on the instance, and the attribute with the same name on the instance object will cover the original attribute on the prototype object. The so-called masking is just because access to the properties of the same name on the prototype object will be blocked, but it will not be modified. Only using delete can completely delete this attribute on the instance and restore access to the attribute with the same name on the prototype object. Otherwise, even if the property with the same name on the instance is modified to null, the connection between it and the property with the same name on the prototype object cannot be restored.

Attribute 'override' or shadow.
Of course, there are certain rules for setting the properties of this object, and the so-called shielding is more complicated than we imagined. We can analyze the process of setting the attribute with the same name on the instance object. This will be divided into several situations:

When the attribute does not exist on the prototype object, it can be set directly;
when the attribute exists on the prototype object:
it is only an ordinary data access attribute, and writable: false is not set, a property with the same name will be added to the instance object , to shield the access to the property with the same name on the original prototype object;
when writable: false is set, the property is marked as a read-only property (read-only), then the operation of setting the property with the same name to the instance object will be intercepted, and this copy operation is in An error will be reported in strict mode and ignored by default in non-strict mode. In short, it is impossible to have a shadowing effect on the properties of the same name of the prototype object.
Attention: It seems a bit confusing that as long as there is a read-only attribute on the prototype object, the attribute with the same name cannot be assigned. But this restriction only exists in the copy operation of =, if you use Object.defineProperty() directly, it will not be affected, and you can still directly copy the instance object.

If the property of the same name of the prototype object has a setter set, then the setter will be called, and the action of setting the property of the same name will be applied to the setter. It will also not have a blocking effect on the property of the same name of the prototype object.

in and hasOwnProperty attribute sources

Assuming we have such a piece of code, we can see that we mask the nickname attribute on the person prototype object through ordinary copying. Regardless of whether the property is on an instance or a prototype, as long as the in operator is used when the object property can be accessed through the object, the operation result will return true.

function Parent() {
    
    
  nickname: 'Parent';
}

Parent.prototype.nickname = 'KK';

let person1 = new Parent();
let person2 = new Parent();

person1.nickname = 'person1';

console.log('nickname' in person1); // true
console.log('nickname' in person2); // true

We know that for attribute access, that is, the get operation of the attribute, it will be searched along the prototype chain until it finds or reaches the root object. Therefore, if you need to determine whether the property is on the instance object or the prototype object, you can use the hasOwnProperty() method. This method can be used to determine whether a property is on the instance or the prototype object, if and only if the property is Returns true when it is called on the object, such as:

console.log(person1.hasOwnProperty('nickname')); // true
console.log(person2.hasOwnProperty('nickname')); // false

Combined with the in and hasOwnProperty mentioned above, how can we judge if we only want the properties on the prototype object? You can create a hasPrototypeProperty method that combines the characteristics of in and hasOwnProperty:

function hasPrototypeProperty(obj, propertyKey) {
    
    
  return !obj.hasOwnProperty(propertyKey) && propertyKey in obj;
}

Property acquisition

When it comes to traversal, in object attribute traversal, we can use for-in and Object.keys(), but there are certain differences between the two.

for-in
uses the in operator in the for, and the properties that can be accessed and enumerable through the object will be returned, including instance properties and properties on the prototype object. When we set Enumberable:false, the object becomes a non-enumerable property, and it will not be returned in the loop. It should be noted that when we define a property with the same name on the instance to cover a non-enumerable property of the prototype object, the property with the same name that is not explicitly defined as non-enumerable on the instance object can be returned, that is, non-enumerable Raising attributes does not affect instance attributes.

Object.keys()
If you want to get all the enumerable properties on the object, you can use the Object.keys() method. This method accepts the object as a parameter and returns an array of all enumerable property names on the object without Contains properties on its prototype object.

This method can be understood as a combination of for-in + hasOwnProperty, which only returns the properties on the object without searching along the prototype chain.

Object.getOwnPropertyNames()
If you want to list all instance properties, whether enumerable or not, you can use Object.getOwnPropertyNames(). However, please note that the return result of this method will contain non-enumerable attributes. When we use this method on the prototype object, the method will return the constructor.

After the symbol type of Symbol appears in ES6, a Object.getOwnPropertySymbols() will appear correspondingly. This method is actually similar to getOwnPropertyNames, but this method is only for the symbol type of Symbol.

For these methods, for-in, the enumeration order of Object.keys() is uncertain, and the specific implementation depends on the implementation of the corresponding JavaScript engine, and Object.getOwnPropertyNames() or Object.assign() It is certain that the result will be returned according to the enumeration value key-insertion order of enumeration string-symbol key. There is an example in YDKJS. I will expand it a little and you may see it more clearly:

let sym1 = Symbol('sym1')
let sym2 = Symbol('sym2')
let sym3 = Symbol('sym3')

let obj = {
    
    
  1: 1,
  [sym3]: 'sym3',
  [sym1]: 'sym1',
  second: 'second',
  first: 'first',
  0: 0
}

obj[sym2] = 'sym2'
obj[3] = 3
obj.forth = 'forth'
obj.third = 'third'
obj[2] = 2

console.log(Object.getOwnPropertyNames(obj)) // [ '0', '1', '2', '3', 'second', 'first', 'forth', 'third' ]
console.log(Object.getOwnPropertySymbols(obj)) //  [ Symbol(sym3), Symbol(sym1), Symbol(sym2) ]

prototype judgment method

Object.getPrototypeOf()
The Object.getPrototypeOf() method returns the prototype of the specified object (the value of the internal [[Prototype]] property). The specific usage is:

Object.getPrototypeOf(object)

Object.getPrototypeOf(person1); // { name: 'KK', sayHi: [Function (anonymous)] } 

Object.getPrototypeOf(person1) === Parent; // true

Object.prototype.isPrototypeOf()
The Object.prototype.isPrototypeOf() method is used to test whether an object exists on the prototype chain of another object. Although not all methods implement the external exposure of proto , we can use isPrototypeOf() to determine the relationship between Parent.prototype and person, because there is a link in person pointing to Person.prototype. So the result returns true.

Parent.prototype.isPrototypeOf(person1); // true

instanceof
instanceof is used to determine whether the prototype chain of the instance contains the prototype of a constructor. The specific usage is instance instanceof constructionFunc. However, note that this method may not apply when we explicitly change the prototype of an instance.

person1 instanceof Parent ; // true

This article briefly introduces some relevant content about prototype. Regarding the picture at the beginning of the article, now we look back, although we still feel that the response will be slow, but after careful thinking, we should be able to understand the links and context of the whole picture. Take a short break and digest the content, and then start a new article chapter, prototype chain inheritance.

Guess you like

Origin blog.csdn.net/weiyi47/article/details/132644086