Typescriptプロトタイプオブジェクトプロトタイプの深い理解

Typescriptプロトタイプオブジェクトプロトタイプ



序文

この記事では、主にTypescriptでのプロトタイプオブジェクトのプロトタイプと、プロトタイプオブジェクトの取得方法について説明します。


ヒント:以下はこの記事の内容です。以下のケースは参照用です。

1.プロトタイプとは何ですか?

JavaScriptでは、プロトタイプオブジェクトはオブジェクト指向の重要なメカニズムです。各関数はオブジェクト(関数)であり、関数オブジェクトにはサブオブジェクトプロトタイプオブジェクトがあり、クラスは関数の形式で定義されます。プロトタイプは、関数のプロトタイプを表し、クラスのメンバーのコレクションも表します。JavaScriptでは、継承に関しては、JavaScriptの構造はオブジェクトのみです。classキーワードはES2015 / ES6で導入されましたが、これは単なる構文上の糖衣であり、JavaScriptはまだプロトタイプベースです。

1.オブジェクトインスタンスの__proto__属性

各インスタンスオブジェクト(オブジェクト)には、コンストラクターのプロトタイプオブジェクトを指すプライベートプロパティ(__proto__と呼ばれる)があります。プロトタイプオブジェクトには、オブジェクトのプロトタイプオブジェクトがnullになるまで、レイヤーごとに独自のプロトタイプオブジェクト(__proto__)もあります。定義上、nullにはプロトタイプがなく、プロトタイプチェーンの最後のリンクとして機能します。

2.Object.getPrototypeOf()は、オブジェクトインスタンスの__proto__属性を取得します

ECMAScript標準に従い、someObject。__proto__シンボルは、someObjectのプロトタイプオブジェクトプロトタイプを指すために使用されます。ECMAScript 6以降、__ proto__にはObject.getPrototypeOf()およびObject.setPrototypeOf()アクセサーを介してアクセスできます。これは、多くのブラウザが実装している非標準のJavaScriptプロパティ__proto__と同等です。ただし、コンストラクターFunctionのプロトタイププロパティと混同しないでください。コンストラクターによって作成されたインスタンスオブジェクトの__proto__は、Functionのプロトタイププロパティを指します。Object.prototypeプロパティは、Objectのプロトタイプオブジェクトを表します。

3.例

次の例は、インスタンスdogのプロトタイプチェーンを示しています。つまり、
dog–> Animal.prototype –> Object.prototype –> null
です。インスタンスオブジェクトdogのプロトタイプオブジェクト__proto__は、コンストラクター関数Animalのプロトタイププロパティを参照します。は内部プロパティ__proto_も持つオブジェクトであるため、コンストラクターObjectのプロトタイププロパティを指します。オブジェクトのプロトタイプオブジェクトが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.プロトタイプの深い理解

1.関数オブジェクト

関数宣言(function)によって作成された各関数は、Functionオブジェクトのすべてのプロパティ、メソッド、および動作を備えたFunctionオブジェクトです。関数宣言ファイルを見てみましょう。まず、FunctionConstructorタイプのオブジェクトタイプインターフェイスとして定義されます。このインターフェイスには、関数タイプと呼ばれるオブジェクトタイプインターフェイスと読み取り専用のプロトタイプ関数オブジェクトタイプインターフェイス属性の2つのメソッドが含まれています。

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またはFunctionConstructor.prototypeは、Functionオブジェクトタイプインターフェイスであり、Functionオブジェクトタイプインターフェイスもオブジェクトオブジェクトです。JavaScriptのほとんどすべてのオブジェクトは、プロトタイプチェーンの最上位にあるObjectのインスタンスです。各オブジェクトオブジェクトはコンストラクター関数を定義します。

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.例

次の例は、関数Animalのプロトタイプチェーンを示しています
。Animal-> Function.prototype –> Object.prototype –> null
インスタンスメソッドAnimalのプロトタイプオブジェクト__proto__は、コンストラクタFunctionのprototypeプロパティを参照し、prototypeは、したがって、内部プロパティ__proto_は、コンストラクター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.プロトタイプとObject.getPrototypeOf()の違い

あなたは私たちの関数Animalがprototypeと呼ばれる特別なプロパティを持っていることに気づいたかもしれません。この特別な属性は、JavaScriptの新しい演算子で使用できます。プロトタイプオブジェクトへの参照は、新しいインスタンスの内部__proto__プロパティにコピーされます。たとえば、var animal = new Animal();が実行されると、JavaScript(メモリ内にオブジェクトを作成した後、Animal()関数を実行してこれをオブジェクトにポイントする前)はanimal .__ proto__ = Animal.prototype;を設定します。次に、インスタンスのプロパティにアクセスすると、JavaScriptは最初にそれらがオブジェクトに直接存在するかどうかを確認し、存在しない場合は__proto__を検索します。つまり、プロトタイプで定義したすべてのものをすべてのインスタンスで効果的に共有でき、後でプロトタイプの一部を変更して、既存のすべてのインスタンスの変更を表示することもできます(必要な場合)。

上記の例のように、var a1 = new Animal(); var a2 = new Animal();を実行すると、a1.sayHello()は実際にはObject.getPrototypeOf(a1).sayHello()を指します。 Animal.prototype.sayHelloで定義されているコンテンツです。言い換えると、Object.getPrototypeOf(a1).sayHello == Object.getPrototypeOf(a2).sayHello == Animal.prototype.sayHello(補足:実際、a1.sayHello()を実行することは、Object.getPrototypeOf(a1 ).sayHello.call(a1)== Animal.prototype.sayHello.call(a1))

つまり、プロトタイプはクラスに使用され、Object.getPrototypeOf()はインスタンス(インスタンス)に使用され、どちらも同じ関数を持っています。

3.プロトタイプチェーンの役割

継承

ES5継承では、本質は、最初にサブクラスのインスタンスオブジェクトthisを作成し、次に親クラスのメソッドをこれに追加することです(Parent.apply(this))。ES6の継承メカニズムは完全に異なります。本質は、最初に親クラスのインスタンスオブジェクトのプロパティとメソッドをこれに追加し(したがって、最初にスーパーメソッドを呼び出す必要があります)、次にサブクラスのコンストラクターを使用して変更することです。この。

class Parent {
    
    }

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

superは親クラスParentのコンストラクターを表しますが、子クラスChildのインスタンスを返すことに注意してください。つまり、この内部のsuperはChildのインスタンスを参照するため、super()はParent.prototype.constructor.callと同等です。 (これ)。子クラスChildのコンストラクターのsuper()は、親クラスのコンストラクターの呼び出しを表します。これは必要です。そうでない場合、JavaScriptエンジンはエラーを報告します。

プロトタイプチェーンアイコン:
ここに画像の説明を挿入

1.プロパティの継承

JavaScriptオブジェクトは、動的プロパティの「バッグ」です(独自のプロパティを参照します)。JavaScriptオブジェクトには、プロトタイプオブジェクトへのリンクがあります。オブジェクトのプロパティにアクセスしようとすると、オブジェクトを検索するだけでなく、オブジェクトのプロトタイプとオブジェクトのプロトタイプも検索し、名前が一致する属性が見つかるか、プロトタイプに到達するまで上向きに検索します。チェーンの終わり。

インスタンス

	// 让我们从一个函数里创建一个对象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.メソッドの継承

JavaScriptには、他のクラスベース言語で定義された「メソッド」はありません。JavaScriptでは、任意の関数をオブジェクトのプロパティとしてオブジェクトに追加できます。関数の継承は、上記の「属性マスキング」を含む他の属性の継承と同じです(この状況は他の言語でのメソッドの書き換えと同等です)

インスタンス

	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' 

第四に、プロトタイプチェーンのパフォーマンス

プロトタイプチェーンで属性を見つけるのは時間がかかり、パフォーマンスに副作用があります。これは、厳しいパフォーマンス要件の下で非常に重要です。さらに、存在しないプロパティにアクセスしようとすると、プロトタイプチェーン全体がトラバースされます。
オブジェクトのプロパティをトラバースすると、プロトタイプチェーン上のすべての列挙可能なプロパティが列挙されます。オブジェクトがプロトタイプチェーン上のプロパティではなく、独自に定義されたプロパティを持っているかどうかを確認するには、すべてのオブジェクトがObject.prototypeから継承するhasOwnPropertyメソッドを使用する必要があります。
具体的な例を以下に示します。

	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

注:プロパティが未定義かどうかを確認しても、プロパティが存在するかどうかは確認できません。プロパティはすでに存在している可能性がありますが、その値はたまたま未定義に設定されています。hasOwnPropertyとObject.keys()は、プロパティを処理し、プロトタイプチェーンをトラバースしないJavaScriptの唯一のメソッドです。


総括する

この記事では、コンストラクター関数プロトタイプのプロトタイプ、それによって作成されたインスタンスオブジェクト(インスタンス)の__proto__属性、およびインスタンスの__proto__属性を取得するためのメソッドObject.getPrototypeOf()を紹介します。2つの違いと使用法を説明します。インスタンスが特定のプロパティとメソッドを見つけると、それがnullになるまで__proto__プロパティチェーンに従って上向きにクエリを実行しますが、プロトタイプチェーン全体をトラバースすると、パフォーマンスの問題が発生します。hasOwnProperty()とObject.keys()を使用して、プロパティがオブジェクトインスタンスでは、これによりプロトタイプチェーン全体をトラバースする必要がなくなります。

おすすめ

転載: blog.csdn.net/Suarez1987/article/details/112531456