Typescript prototype object prototype in-depth understanding

Typescript prototype object prototype



Preface

This article mainly describes the prototype object prototype under Typescript and the method of obtaining the prototype object.


Tip: The following is the content of this article, the following cases are for reference

1. What is prototype?

In JavaScript, the prototype object is an important mechanism for object-oriented. Each function is an object (Function), the function object has a sub-object prototype object, and the class is defined in the form of a function. Prototype represents the prototype of the function and also represents a collection of members of a class. In JavaScript, when it comes to inheritance, JavaScript has only one structure: objects. The class keyword was introduced in ES2015/ES6, but that is just syntactic sugar, and JavaScript is still prototype-based.

1. The __proto__ attribute of the object instance

Each instance object (object) has a private property (called __proto__) that points to the prototype object of its constructor. The prototype object also has its own prototype object (__proto__ ), layer by layer until the prototype object of an object is null. By definition, null has no prototype and serves as the last link in the prototype chain.

2.Object.getPrototypeOf() gets the __proto__ attribute of the object instance

Following the ECMAScript standard, someObject. __proto__ symbol is used to point to the prototype object prototype of someObject. Starting from ECMAScript 6, __proto__ can be accessed through Object.getPrototypeOf() and Object.setPrototypeOf() accessors. This is equivalent to the non-standard JavaScript property __proto__ that many browsers implement. But it should not be confused with the prototype property of the constructor Function. The __proto__ of the instance object created by the constructor points to the prototype property of Function. The Object.prototype property represents the prototype object of Object.

3. Examples

The following example illustrates the prototype chain of the instance dog: namely
dog–>Animal.prototype –> Object.prototype –> null
. The prototype object __proto__ of the instance object dog refers to the prototype property of the constructor function Animal, and prototype is an object that also has The internal property __proto_, therefore points to the prototype property of the constructor Object. All properties, methods, and behaviors will go up layer by layer until the prototype object of an object is null.

class Animal {
    
    
    name:string;
    constructor(name:string="Animal") {
    
    
      this.name = name;  
    }
    sayHello(){
    
    
        console.log("Hello ",this.name);
    }
}
console.log(typeof(Animal.prototype)) // object

let dog = new Animal("Dog")
console.log(Object.getPrototypeOf(dog) === Animal.prototype) // true
console.log(Object.getPrototypeOf(Animal.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true

2. In-depth understanding of prototype

1.Function object

Each function created by a function declaration (function) is a Function object with all the properties, methods, and behaviors of the Function object. We look at the Function declaration file. First, it is defined as a FunctionConstructor type object type interface, which contains two methods, both of which return an object type interface called Function type and a read-only prototype Function object type interface Attributes.

interface FunctionConstructor {
    
    
    /**
     * Creates a new function.
     * @param args A list of arguments the function accepts.
     */
    new(...args: string[]): Function;
    (...args: string[]): Function;
    readonly prototype: Function;
}

declare var Function: FunctionConstructor;

2.Function.prototype

Function.prototype, or FunctionConstructor.prototype, is the Function object type interface, and the Function object type interface is also an object Object. Almost all objects in JavaScript are instances of Object at the top of the prototype chain. Each object Object defines a constructor function.

interface Object {
    
    
    /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
    constructor: Function;
    ...
}
/**
 * Creates a new function.
 */
interface Function {
    
    
    /**
     * Calls the function, substituting the specified object for the this value of the function, and the specified array for the arguments of the function.
     * @param thisArg The object to be used as the this object.
     * @param argArray A set of arguments to be passed to the function.
     */
    apply(this: Function, thisArg: any, argArray?: any): any;

    /**
     * Calls a method of an object, substituting another object for the current object.
     * @param thisArg The object to be used as the current object.
     * @param argArray A list of arguments to be passed to the method.
     */
    call(this: Function, thisArg: any, ...argArray: any[]): any;

    /**
     * For a given function, creates a bound function that has the same body as the original function.
     * The this object of the bound function is associated with the specified object, and has the specified initial parameters.
     * @param thisArg An object to which the this keyword can refer inside the new function.
     * @param argArray A list of arguments to be passed to the new function.
     */
    bind(this: Function, thisArg: any, ...argArray: any[]): any;

    /** Returns a string representation of a function. */
    toString(): string;

    prototype: any;
    readonly length: number;

    // Non-standard extensions
    arguments: any;
    caller: Function;
}

3. Examples

The following example illustrates the prototype chain of function Animal:
Animal->Function.prototype –> Object.prototype –> null
instance method Animal’s prototype object __proto__ refers to the prototype property of the constructor Function, and prototype is an object that also has The internal property __proto_, therefore points to the prototype property of the constructor Object.

class Animal {
    
    
    name:string;
    constructor(name:string="Animal") {
    
    
      this.name = name;  
    }
    sayHello(){
    
    
        console.log("Hello ",this.name);
    }
}

console.log(typeof Animal); // function
console.log(Object.getPrototypeOf(Animal) === Function.prototype) // true
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype) // true
console.log(Object.getPrototypeOf(Object.prototype) === null) // true

4. The difference between prototype and Object.getPrototypeOf()

You may have noticed that our function Animal has a special property called prototype. This special attribute can be used with JavaScript's new operator. The reference to the prototype object is copied to the internal __proto__ property of the new instance. For example, when var animal = new Animal(); is executed, JavaScript (after creating the object in memory and before running the Animal() function to point this to the object) sets animal.__proto__ = Animal.prototype;. Then when you access the properties of the instance, JavaScript will first check whether they exist directly on the object, and if they do not exist, it will look in __proto__. This means that everything you define in the prototype can be effectively shared by all instances, and you can even change parts of the prototype later and show the changes in all existing instances (if necessary).

Like in the above example, if you execute var a1 = new Animal(); var a2 = new Animal(); then a1.sayHello() will actually point to Object.getPrototypeOf(a1).sayHello(), it is what you are The content defined in Animal.prototype.sayHello. In other words: Object.getPrototypeOf(a1).sayHello == Object.getPrototypeOf(a2).sayHello == Animal.prototype.sayHello (Supplement: In fact, executing a1.sayHello() is equivalent to executing Object.getPrototypeOf(a1) .sayHello.call(a1)==Animal.prototype.sayHello.call(a1))

In short, prototype is used for classes, and Object.getPrototypeOf() is used for instances (instances), and both have the same function.

3. The role of the prototype chain

inherit

In ES5 inheritance, the essence is to first create an instance object this of the subclass, and then add the method of the parent class to this (Parent.apply(this)). The inheritance mechanism of ES6 is completely different. The essence is to first add the properties and methods of the parent class instance object to this (so the super method must be called first), and then use the subclass constructor to modify this.

class Parent {
    
    }

class Child extends Parent {
    
    
  constructor() {
    
    
    super();
  }
}

Note that although super represents the constructor of the parent class Parent, it returns an instance of the child class Child, that is, this inside super refers to an instance of Child, so super() is equivalent to Parent.prototype.constructor.call( this). The super() in the constructor of the child class Child represents calling the constructor of the parent class. This is necessary, otherwise the JavaScript engine will report an error.

Prototype chain icon:
Insert picture description here

1. Property inheritance

JavaScript objects are dynamic property "bags" (referring to their own properties). JavaScript objects have a link to a prototype object. When trying to access the properties of an object, it not only searches on the object, but also searches for the prototype of the object and the prototype of the object, searching upwards successively until it finds a property with a matching name or reaches the prototype The end of the chain.

Instance

	// 让我们从一个函数里创建一个对象o,它自身拥有属性a和b的:
	let f = function () {
    
    
	   this.a = 1;
	   this.b = 2;
	}
	/* 这么写也一样
	function f() {
	  this.a = 1;
	  this.b = 2;
	}
	*/
	let o = new f(); // {a: 1, b: 2}
	
	// 在f函数的原型上定义属性
	f.prototype.b = 3;
	f.prototype.c = 4;
	
	// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链
	// o.[[Prototype]] 有属性 b 和 c
	//  (其实就是 o.__proto__ 或者 o.constructor.prototype)
	// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
	// 最后o.[[Prototype]].[[Prototype]].[[Prototype]]是null
	// 这就是原型链的末尾,即 null,
	// 根据定义,null 就是没有 [[Prototype]]。
	
	// 综上,整个原型链如下:
	
	// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
	
	console.log(o.a); // 1
	// a是o的自身属性吗?是的,该属性的值为 1
	
	console.log(o.b); // 2
	// b是o的自身属性吗?是的,该属性的值为 2
	// 原型上也有一个'b'属性,但是它不会被访问到。
	// 这种情况被称为"属性遮蔽 (property shadowing)"
	
	console.log(o.c); // 4
	// c是o的自身属性吗?不是,那看看它的原型上有没有
	// c是o.[[Prototype]]的属性吗?是的,该属性的值为 4
	
	console.log(o.d); // undefined
	// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
	// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
	// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
	// 找不到 d 属性,返回 undefined

2. Method inheritance

JavaScript has no "methods" defined by other class-based languages. In JavaScript, any function can be added to an object as a property of the object. Function inheritance is no different from other attribute inheritance, including the above "attribute masking" (this situation is equivalent to method rewriting in other languages)

Instance

	var o = {
    
    
	  a: 2,
	  m: function(){
    
    
	    return this.a + 1;
	  }
	};
	
	console.log(o.m()); // 3
	// 当调用 o.m 时,'this' 指向了 o.
	
	var p = Object.create(o);
	// p是一个继承自 o 的对象
	
	p.a = 4; // 创建 p 的自身属性 'a'
	console.log(p.m()); // 5
	// 调用 p.m 时,'this' 指向了 p
	// 又因为 p 继承了 o 的 m 函数
	// 所以,此时的 'this.a' 即 p.a,就是 p 的自身属性 'a' 

Fourth, the performance of the prototype chain

Finding attributes on the prototype chain is time-consuming and has side effects on performance, which is very important under demanding performance requirements. In addition, trying to access non-existent properties will traverse the entire prototype chain.
When traversing the properties of an object, every enumerable property on the prototype chain will be enumerated. To check whether an object has its own defined properties, rather than a property on its prototype chain, you must use the hasOwnProperty method that all objects inherit from Object.prototype.
A specific example is given below to illustrate it:

	class A {
    
    
		name:string
		constructor(name:sting="defalut"){
    
    
			this.name = name
		}	
	}
	
	let a = new A()
	console.log(a.hasOwnProperty('name'));
	// true
	
	console.log(a.hasOwnProperty('abc'));
	// false
	
	console.log(a.hasOwnProperty('efg'));
	// false

Note: Checking whether the property is undefined is not able to check whether it exists. The property may already exist, but its value happens to be set to undefined. hasOwnProperty and Object.keys() are the only methods in JavaScript that handle properties and do not traverse the prototype chain.


to sum up

The article introduces the prototype of the constructor function prototype, the __proto__ attribute of the instance object (instance) created by it, and the method Object.getPrototypeOf() to get the __proto__ attribute of the instance. Explains the difference and usage of the two. When the instance finds a certain property and method, it queries upwards according to the __proto__ property chain until it is null, but traversing the entire prototype chain will cause performance problems. Use hasOwnProperty() and Object.keys() to determine whether the property is changing In object instances, this avoids traversing the entire prototype chain.

Guess you like

Origin blog.csdn.net/Suarez1987/article/details/112531456