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 タグの属性と使用法

推薦する

クリックイメージ反転効果を実現するJavaScript

最近、顔コレクションに関するプロジェクトに取り組んでいましたが、フロントエンドモジュールを書いている...

Vue プロジェクトで addRoutes を使用する際の問題の解決策

目次序文1. 404 ページ1. 原因2. 解決策2.白い画面を更新する1. 原因2. 解決策3. ...

データページング効果を実現する js オブジェクト

この記事の例では、データのページング効果を実現するためのjsオブジェクトの具体的なコードを参考までに...

CentOS8 - bash: 文字化けとその解決方法

この状況は通常、中国語言語パックがインストールされていないか、デフォルトの言語設定に問題があるために...

MySQL kill コマンドの使用ガイド

KILL [接続 | クエリ] processlist_id MySQL では、各接続は個別のスレ...

MySQL トランザクション分離レベルの表示と変更の例

トランザクション分離レベルを確認するMySQL では、'%tx_isolation%'...

HTMLはWEB標準の開発の中心的な基盤です

HTML 中心のフロントエンド開発は、ほぼ Web 標準の意味です。共通しているのは「分離」という考...

Linux で MongoDB のリモート自動バックアップを実装する方法

序文古いプロジェクトを引き継ぐ苦労 - MongoDB クラスターの学習と構築に関する前回の記事を読...

ウェブページを白黒に変換します(Google、Firefox、IE、その他のブラウザと互換性があります)

CSSファイルに書き込むコードをコピーコードは次のとおりです。 01.html {グレイスケール(1...

自動的にフォーカスを取得する要素入力ボックスの実装

最近のプロジェクトでフォームを作成するときに、コメント ボックスまで自動的にスクロールし、コメント ...

Vueは複数列レイアウトドラッグを実装します

この記事では、マルチカラムレイアウトドラッグを実装するためのVueの具体的なコードを参考までに共有し...

マルチコア CPU を使用して Linux コマンドを高速化する方法 (GNU Parallel)

非常に大量のデータ(数百 GB)を計算する必要があったことはありますか?または、その内部を検索したり...

MySQL DEFINER の使用方法の詳細な説明

目次序文: 1.DEFINERの簡単な紹介2. いくつかの注意点要約:序文: MySQL データベー...

Docker に MySQL をデプロイする例

目次1 コンテナクラウドとは何ですか? 2 Dockerの紹介3 dockerを使ってMySQLをイ...

MySQL 8.0.21 のインストール手順と問題解決

公式サイトをダウンロードまず公式ウェブサイトにアクセスしてMySQLをダウンロードしてくださいリンク...