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

推薦する

VMware Workstation Pro は Win10 ピュア バージョンのオペレーティング システムをインストールします

この記事では、VMware Workstation Pro で Win10 オペレーティング システ...

MySQL 圧縮版 zip のインストールに関する問題の解決策

本日、MySQLの圧縮版をインストールする際に問題が発生しました。サービスが起動できず、2、3時間苦...

HTML ウェブページのメタビューポート属性の説明

HTML メタビューポート属性の説明ビューポートとはモバイル ブラウザは、Web ページを仮想の「ウ...

Zabbix は MySQL インスタンス メソッドを監視します

1. 監視計画監視項目を作成する前に、何を監視するのか、どのように監視するのか、監視データをどのよう...

WeChatミニプログラムのすべてのページがログインされていることを確認する方法

目次現状解決さらなる解決策やっと現状WeChat ミニプログラムには、ホームページ、個人ページ、いく...

MySQL IFNULL判定問題の解決方法

問題: mybatis によって返される null 型のデータが消え、フロントエンドの表示にエラーが...

MySQL のソート関数 field() の詳細な例

序文私たちの日常の開発プロセスでは、ソートが頻繁に使用され、そのような要求がある場合もあります。たと...

MySQLセグメンテーション関数substring()の具体的な使用法

MySQL には、主に left()、right()、substring()、substring_i...

アイデアはDockerプラグインを使用してワンクリックの自動デプロイを実現します

目次環境: 1. Dockerはリモート接続アクセスを可能にするidea dockerプラグインをイ...

ウェブページの読み込み速度を上げる簡単なヒント

Web ページの読み込み速度は、Web サイトの品質を評価するための重要な指標です。その理由は、ほと...

LinuxテキストエディタVimの詳しい説明

Vim は強力なフルスクリーン テキスト エディターであり、Linux/UNIX で最も一般的に使用...

MySQLデータ移行方法とツールの分析

この記事は主にMySQLデータ移行方法とツールの分析を紹介します。サンプルコードを通じて詳細に紹介さ...

純粋な CSS でマークダウンの自動番号付けを実装するサンプル コード

問題の起源私がタイトルの番号付けの問題に初めて注目したのは、学部の論文を書いていた頃まで遡ります。当...

Nginx/Httpd リバース プロキシ Tomcat 設定チュートリアル

以前のブログでは、Tomcatのサーバーの各コンポーネントの使用について学びました。 Tomcatは...

フォームアクションとonSubmitの例

まず、action はフォームの属性です。HTML5 では必須の属性値として定義されています。onS...