JSにおけるnewの原理と実装について詳しく話しましょう

JSにおけるnewの原理と実装について詳しく話しましょう

意味

new 演算子は、ユーザー定義のオブジェクト タイプのインスタンス、またはコンストラクターを持つ組み込みオブジェクトのインスタンスを作成します。

オブジェクト インスタンスを作成するには、新しい [コンストラクター] メソッドを使用しますが、コンストラクターが異なると、異なるインスタンスが作成されます。

コンストラクタ本体は異なる

コンストラクタも関数です。唯一の違いは呼び出し方法です。new 演算子で呼び出される関数はすべてコンストラクタであり、new 演算子で呼び出されない関数は通常の関数です。

したがって、コンストラクターにも戻り値がありますが、これにより new の結果が異なります。

戻り値なし

関数 Person(名前) {
  this.name = 名前;
}

obj = new Person("Jalenl"); とします。
コンソールにログ出力します。

明らかに、印刷されているのは{name:'Jalenl'}です

戻りオブジェクト

関数 Person(年齢) {
  this.age = 年齢;
  戻り値: { name: "Jalenl" };
}

obj = new Person(18); とする。
コンソールにログ出力します。

印刷されるのは {name:'Jalenl'} であり、これは return の前のすべての定義が上書きされることを意味します。ここで返されるのはオブジェクトですが、基本型の場合はどうなるでしょうか?

非オブジェクトを返す

関数 Person(年齢) {
  this.age = 年齢;
  1 を返します。
}

obj = new Person(18); とする。
コンソールにログ出力します。

{age:21} を返します。これは戻り値が無効であることを意味し、戻り値がない場合と同じ結果になります。このバインドされた内部属性がなく、基本データ型が返された場合はどうなるでしょうか。

プロパティバインディングなし + 非オブジェクトを返す

関数Person(){
    戻り値 1
}
新しい人()

返される値は、予想どおり、空のオブジェクト {} です。

要約すると、初期結果は、コンストラクターの戻り値がオブジェクト型を返す場合にのみ変更できます。

コンストラクタの型は異なります

コンストラクタは通常の関数である

ECMA-262 第 3 版仕様では、オブジェクト インスタンスを作成するプロセスについて説明しています。

13.2.2 [[構築]]
FunctionオブジェクトFの[[Construct]]プロパティが呼び出されると、次の手順が実行されます。

  1. 新しいネイティブ ECMAScript オブジェクトを作成します。
  2. Result(1)の[[Class]]プロパティを「Object」に設定します。
  3. F のプロトタイプ プロパティの値を取得します。
  4. Result(3)がオブジェクトの場合、Result(1)の[[Prototype]]プロパティをResult(3)に設定します。
  5. Result(3)がオブジェクトでない場合は、15.2.3.1で説明されているように、Result(1)の[[Prototype]]プロパティを元のObjectプロトタイプオブジェクトに設定します。
  6. Fの[[Call]]プロパティを呼び出し、Result(1)をthis値として提供し、[[Construct]]に渡された引数リストを引数値として提供します。
  7. Type(Result(6))がObjectの場合はResult(6)を返します。
  8. 結果(1)を返します。

総括する:

  1. メモリ内に新しいオブジェクトを作成します。
  2. この新しいオブジェクト内の [[Prototype]] プロパティは、コンストラクター関数の prototype プロパティに割り当てられます。
  3. コンストラクター内の this は新しいオブジェクトに割り当てられます (つまり、 this は新しいオブジェクトを指します)。
  4. コンストラクター内のコードを実行します (新しいオブジェクトにプロパティを追加します)。
  5. コンストラクターがオブジェクトを返す場合はそのオブジェクトが返されます。それ以外の場合は、新しく作成されたオブジェクト (空のオブジェクト) が返されます。

5 番目のステップでは、異なるコンストラクターが異なる新しい結果につながる理由をすでに説明しました。

以下はMDNからの説明です。

コード new Foo(…) が実行されると、次のことが起こります。

  1. Foo.prototype から継承する新しいオブジェクトが作成されます。
  2. 指定された引数を使用してコンストラクター Foo を呼び出し、これを新しく作成されたオブジェクトにバインドします。 new Foo は new Foo() と同等です。つまり、引数リストは指定されず、Foo は引数なしで呼び出されます。
  3. コンストラクターによって返されるオブジェクトは、新しい式の結果です。コンストラクターが明示的にオブジェクトを返さない場合は、手順 1 で作成されたオブジェクトが使用されます。 (通常、コンストラクターは値を返しませんが、ユーザーは通常のオブジェクト作成手順をオーバーライドするためにオブジェクトを積極的に返すことを選択できます)

コンストラクタは矢印関数である

通常の関数が作成されると、エンジンは特定のルールに従って、この関数のプロトタイプ プロパティ (プロトタイプ オブジェクトを指す) を作成します。デフォルトでは、すべてのプロトタイプ オブジェクトは、関連付けられているコンストラクターを指すコンストラクターと呼ばれるプロパティを自動的に取得します。

関数Person(){
    18歳未満
}
人物プロトタイプ
/**
{
    コンストラクタ: ƒFoo()
    __proto__: オブジェクト
}
**/

矢印関数を作成する場合、エンジンはそれのプロトタイプ プロパティを作成しません。矢印関数には new を呼び出すコンストラクターがないため、new を使用して矢印関数を呼び出すとエラーが発生します。

定数 Person = ()=>{}
new Person() // TypeError: Foo はコンストラクタではありません

手書きの新着

まとめると、new の動作原理を理解した後は、自分でロープロファイルバージョンの new を実装できます。実装の鍵は次のとおりです。

  1. インスタンスがプライベート プロパティにアクセスできるようにします。
  2. インスタンスがコンストラクター プロトタイプ (constructor.prototype) が配置されているプロトタイプ チェーン上のプロパティにアクセスできるようにします。
  3. コンストラクターによって返される最終結果は参照データ型です。
関数_new(コンストラクタ、...引数) {
    // コンストラクタ型の法的判断 if(typeof コンストラクタ !== 'function') {
      throw new Error('コンストラクタは関数である必要があります');
    }
    // 新しい空のオブジェクトインスタンスを作成します。let obj = new Object();
    // コンストラクターのプロトタイプを新しく作成されたオブジェクトインスタンスにバインドします。obj.__proto__ = Object.create(constructor.prototype);
    // コンストラクターを呼び出して戻り値を決定します。let res =constructor.apply(obj, args);
    isObject = typeof res === 'object' && res !== null とします。
    isFunction = typeof res === 'function' とします。
    // 戻り値があり、それがオブジェクト型である場合は、それを戻り値として使用し、それ以外の場合は以前に作成されたオブジェクトを返します。 return isObject || isFunction ? res : obj;
};

この目立たない new 実装は、カスタム クラスのインスタンスを作成するために使用できますが、組み込みオブジェクトはサポートされません。結局のところ、new は演算子であり、基礎となる実装はより複雑です。

要約する

JS における new の原理と実装に関するこの記事はこれで終わりです。JS における new の原理と実装に関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • JS の new 関数の詳細な説明
  • JavaScript の new 演算子の原理と例の詳細な説明
  • JavaScript で new を実装する 2 つの方法の調査
  • JavaScript の new 演算子を自分で実装する方法
  • Js における new 演算子の役割の詳細な説明
  • c# Newtonsoft.Json の一般的なメソッドの概要
  • C# Newtonsoft.Json は、複数のネストされた JSON を解析してデシリアライズする例です。
  • c# Newtonsoft.Json パッケージ操作の追加
  • JavaScriptの新しいコマンド
  • JS での new の手書き実装

<<:  ウェブデザインにおけるポップアップウィンドウとフローティングレイヤーのデザイン

>>:  ins タグと del タグの属性と使用法

推薦する

Dockerの基本的なネットワーク構成の詳細な説明

外部アクセスポートをランダムにマップする -P フラグを使用すると、Docker は 49000 か...

フロントエンド開発者のための HTML 入門

1 HTML入門1.1 初めてのコード体験、最初のウェブページの作成XML/HTML コードコンテン...

jsを使用してシンプルな虫眼鏡効果を実現します

この記事では、簡単な虫眼鏡効果を実現するためのjsの具体的なコードを参考までに共有します。具体的な内...

Windows Server のインストール後にワイヤレスとオーディオが機能しない問題を解決する

1. ワイヤレスPowerShell を実行し、次のコマンドを入力します。 install-wind...

JavaScriptタイマーの詳細な説明

目次簡単な紹介間隔の設定説明するパラメータ戻り値使用法タイムアウトの設定説明するパラメータ使用法:タ...

Linuxにグラフィカルインターフェースをインストールする方法

1. Linuxのインストール(rootユーザー操作) 1. vncserver をインストールしま...

Vueはブラウザ側のコードスキャン機能を実装します

背景少し前にブラウザカメラの取得とスキャンコード認識の機能を作りました。その際の知識ポイントと具体的...

Linux Autofs 自動マウント サービスのインストールと展開のチュートリアル

目次1. autofs サービスの紹介2. Autofsのインストールと展開3. Autofs効果の...

MySQL のロック待機とデッドロック問題の分析

目次序文: 1. ロック待機とデッドロックを理解する2. 現象の再発と治療要約:序文: MySQL ...

シンプルなカルーセル効果を実現するネイティブ js

この記事では、シンプルなカルーセル効果を実現するためのjsの具体的なコードを参考までに紹介します。具...

Windows Server 2008 のサーバー パフォーマンス監視に関するチュートリアル

次に、ログ管理、ログのアーカイブ、ログのトラブルシューティング、イベントの転送と収集のためのコンピュ...

Linux でのマルチスレッドにおけるフォークの紹介

目次質問:ケース(1)子スレッドを作成する前にフォークするケース(2)子スレッドを作成した後にフォー...

Jenkins の紹介と Docker で Jenkins をデプロイする方法

1. 関連概念1.1 Jenkins の概念: Jenkins は、使用されるプラットフォームに関係...

CSSクラス名の問題の詳細な説明

数字で始まる次の CSS クラス名は有効になりません。 .1番目{ 色: 赤; }有効な CSS ク...

Linux システムで Tomcat のポート 80 を使用する方法

アプリケーションシナリオ多くの場合、Linux サーバーに tomcat や nginx などのソフ...