JavaScript の知識: コンストラクタも関数である

JavaScript の知識: コンストラクタも関数である

まず明確にしておきたいのは、コンストラクタも関数であるということです。

インスタンス オブジェクトを作成するには、コンストラクターをよく使用します。たとえば、オブジェクトと配列のインスタンス化は、対応するコンストラクター Object() と Array() を通じて完了できます。

コンストラクタと通常の関数の構文定義に違いはありません。主な違いは次の 3 点に反映されます。

(1)コンストラクタ関数名の最初の文字は通常大文字になります。慣例により、コンストラクター名は大文字で始まり、非コンストラクター名は小文字で始まります。これはオブジェクト指向プログラミング言語から借用したもので、ECMAScript でコンストラクターを通常の関数と区別するのに役立ちます。

(2) thisキーワードは、関数本体内で生成するオブジェクトインスタンスを示すために使用されます。コンストラクタは明示的に値を返しませんが、デフォルトで「this」を返します。次のコード スニペットでは、Person() 関数は return キーワードを使用して情報を返しませんが、出力変数 person1 は、name および age 属性値を持つ Person インスタンスです。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>
<本文>
  <スクリプト>
      //1. コンストラクタ関数を定義する Person(name,age){
        this.age = 年齢;
        this.name = 名前;
        this.sayName = 関数(){
            コンソールにログ出力します。
        } 
      }
      // 2. インスタンス オブジェクトを生成します。var person1 = new Person('Xiao Su','18');
      // 3. インスタンスオブジェクトを出力します。console.log(person1);
  </スクリプト>
</本文>
</html>

表示効果は以下のとおりです


(3)コンストラクタとして呼び出される場合は、new演算子と組み合わせて使用​​する必要があります。これは非常に重要です。new 演算子で呼び出される関数はすべてコンストラクターであり、new 演算子で呼び出されない関数は通常の関数です。

1. コンストラクタの定義と呼び出し

コンストラクターはカスタム関数とも呼ばれ、関数の形式で独自のオブジェクト タイプのプロパティとメソッドを定義します。

例:

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>
<本文>
  <スクリプト>
      //1. コンストラクタ関数を定義する Person(name,age){
        this.age = 年齢;
        this.name = 名前;
        this.sayName = 関数(){
            コンソールにログ出力します。
        } 
      }
      // 2. インスタンス オブジェクトを生成します。var person1 = new Person('Xiao Su','18');
      // 3.メソッド person1.sayName() を呼び出します。
  </スクリプト>
</本文>
</html>

2. 新しいキーワードの目的

コンストラクターが実行されると、次の 4 つのステップが実行されます。

  • new キーワード (演算子) を使用してインスタンス オブジェクトを作成すると、メモリ内に新しいアドレスが作成されます。
  • コンストラクター内の this の参照をインスタンス自体に決定します。
  • コンストラクター コードを実行して、インスタンスにプロパティを追加します。
  • 新しく作成されたオブジェクトを返します。

例として、 person1 インスタンスを生成する前のコードを見てみましょう。

ステップ 1: person1 インスタンスのメモリ内に新しいアドレスを作成します。

ステップ 2: person1 インスタンスの this ポインターが person 自体を指していることを確認します。

ステップ 3: person1 インスタンスに name、age、sayName 属性を追加します。ここで、sayName 属性値は関数です。

ステップ 4: person1 インスタンスを返します。

var person1 = new Person("ボブ", 18);

このコード行は、

// 新しい関数 Person(name,age) の機能を説明します。
  // 1. 新しいオブジェクトを作成する var instance = new Object();
  // 2. 関数内の this をこの新しいオブジェクトにポイントします。this = instance;
  // 3. コンストラクター内のコードを実行します。this.name = name;
  this.age = 年齢;
  this.sayName = 関数 () {
    コンソールにログ出力します。
  };
  // 4. 新しいオブジェクトを戻り値として返します。 return instance;
}

これは、インスタンスが person1 に割り当てられる、つまり person1 = インスタンスであることを意味します。実際は次のようになります。

// 新しい関数 Person(name,age) の機能を説明します。
  // 1. 新しいオブジェクトを作成する var person1 = new Object();
  // 2. 関数内の this をこの新しいオブジェクトにポイントします。this = person1;
  // 3. コンストラクター内のコードを実行します。this.name = name;
  this.age = 年齢;
  this.sayName = 関数 () {
    コンソールにログ出力します。
  };
  // 4. 新しいオブジェクトを戻り値として返します。 return person1;
}

3. コンストラクタの問題: メモリの無駄

基本的な事実: sayName 属性はインスタンスごとに異なります。例えば:

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>
<本文>
  <スクリプト>
    // 1. カスタムコンストラクター関数 Person(name,age) {
      this.name = 名前;
      this.age = 年齢;
      // この内部の type プロパティ値は変更されません this.type = "human";
      // 各オブジェクトの sayName メソッドは同じです this.sayName = function () {
        コンソールにログ出力します。
      };
    }
    
    var person1 = new Person('Bob',18);
    var person2 = new Person('Mike',20);
    // 2. それぞれのメソッドが同じかどうかを判定する function console.log(person1.sayName === person2.sayName); // false
  </スクリプト>
</本文>
</html>

説明: console.log(person1.sayName === person2.sayName) メソッドを使用すると、2 つの関数が同じかどうかを判断できます。明らかに、これらは同じ関数ではないため、2 つの関数は 2 つのメモリ領域を占有し、メモリの無駄が発生します。

では、関数がメモリを占有する問題をどのように解決するのでしょうか?答えは、オブジェクトの内部関数を公開関数として抽出することです。

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>
<本文>
  <スクリプト>
    // 解決策 1: コンストラクター関数 sayName() からパブリック関数を抽出する {
      コンソールにログ出力します。
    }
    関数 sayAge() {
      コンソールにログ出力します。
    }
    //1. カスタムオブジェクト関数 Person(name,age) {
      this.name = 名前;
      this.age = 年齢;
      // この内部の type プロパティ値は変更されません this.type = "human";
      // 各オブジェクトの sayName メソッドは同じです this.sayName = sayName;
      年齢を言う
    }
    //2. オブジェクトをインスタンス化します var person1 = new Person('Bob',18);
    var person2 = new Person('Mike',20);
    console.log(person1.sayName === person2.sayName); // 真
    </スクリプト>
</本文>
</html>

この時点でも問題があります。パブリック関数が複数ある場合、外部で複数の関数を作成する必要があり、名前の競合が発生する可能性があります。

解決策: 複数の共通関数を1つのオブジェクトにカプセル化する

<!DOCTYPE html>
<html lang="ja">
<ヘッド>
  <メタ文字セット="UTF-8">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>
<本文>
  <スクリプト>
    // 3. 複数のパブリック関数を1つのオブジェクトにカプセル化する var fns = {
      sayName: 関数() {
        コンソールにログ出力します。
      },
      sayAge: 関数() {
        コンソールにログ出力します。
      }      
    };    
    // 1. カスタムオブジェクト関数 Person(name,age) {
      this.name = 名前;
      this.age = 年齢;
      this.type = "人間";
      this.sayName = fns.sayName;
      年齢を言う = fns.sayAge;
    }
    // 2. オブジェクトインスタンスを生成する var person1 = new Person("Bob",18);
    var person2 = new Person("マイク",20);

    // person1.sayName();
    コンソールにログ出力します。
    コンソールに記録します。(person1.sayAge === person2.sayAge);
  </スクリプト>
</本文>
</html>

説明: 複数の関数を 1 つのオブジェクトにカプセル化すると、関数名の競合の問題を解決できます。

グローバルにアクセス可能な関数を設定すると、繰り返し作成しなくてもすべてのインスタンスからアクセスできるようになります。

ただし、問題があります。オブジェクトに追加されたすべての関数がグローバル関数として扱われると、カスタム型オブジェクトのプロパティのカプセル化を完了できなくなります (つまり、グローバル関数は関数のみをカプセル化でき、プロパティはカプセル化できません。プロパティは関数の外部でカプセル化され、呼び出す前にグローバル変数として定義する必要があり、グローバル変数が汚染されるためです)。したがって、これは良い解決策ではありません。

この時点で、プロトタイプの概念が導入されます。プロトタイプの概念を使用すると、この問題をうまく解決できます。

要約する

この記事はこれで終わりです。皆さんのお役に立てれば幸いです。また、123WORDPRESS.COM のその他のコンテンツにも注目していただければ幸いです。

以下もご興味があるかもしれません:
  • Js クラスの構築と継承のケースの詳細な説明
  • JavaScript クラス継承の複数の実装方法
  • js クラスの継承定義と使用法の分析
  • JS 継承の分類、原則、使用法について深く理解するには 15 分かかります。
  • JavaScript のコンストラクター、プロトタイプ、プロトタイプ チェーン、new についてどれくらい知っていますか?
  • JS 関数とコンストラクタを簡単に理解する
  • JavaScript のクラス、継承、コンストラクタの詳細な説明

<<:  OpenSSL は双方向認証のチュートリアルを実装します (サーバーとクライアントのコード付き)

>>:  MySQL を使用した分散ロックの実装

推薦する

判定条件を使用してCSSファイルをインポートする

解決策 1: HEAD に次のコードを挿入するなど、HTML ドキュメントで条件付きインポートを使用...

Linux でのファイル コンテンツの重複排除と交差と差異の実装

1. データ重複排除日常業務では、Hive や Impala を使用してクエリとエクスポートを行う際...

WeChatアプレットコンポーネント開発:視覚的な映画座席選択機能

目次1. はじめに1. コンポーネントデータ2. コンポーネントページのレイアウト1. ロゴエリアの...

VueはElement el-uploadコンポーネントを使用してピットに足を踏み入れます

目次1. 基本的な使い方2. 画像量の制御3. 画像形式の制限/複数の画像を選択可能補足: vueプ...

MySQL方言の簡単な紹介

データベースはさておき、人生における方言とは何でしょうか?方言とは、ある場所特有の言語です。他の場所...

Tomcat の静的ページ (html) で中国語の文字化けが発生する問題の究極の解決策

tomcatでは、jspは文字化けしませんが、htmlの中国語は文字化けします理由はいくつかあります...

Vue Elementのテーブルコンポーネントをカプセル化する方法

Vue コンポーネントをカプセル化する場合でも、機能コンポーネントをクロスファンクショナルに使用しま...

Javascript 仮想 DOM の詳細な説明

目次仮想DOMとは何ですか?なぜ仮想DOMが必要なのでしょうか?仮想 DOM はどのようにして実際の...

MySQL アップグレードのベストプラクティス

MySQL 5.7 には、オンライン DDL、マルチソース レプリケーション、拡張された半同期、テー...

MySQLがトランザクション分離を実装する方法の簡単な分析

目次1. はじめに2. RC および RR 分離レベル2.1. RRトランザクション分離レベルでのク...

MySQL 作成ルーチン権限に関する注意事項

1. ユーザーにルーチン作成権限がある場合は、プロシージャ | 関数を作成できます。 2. ユーザー...

Dockerの基本的な手順

目次基本的な指示1. 現在のマシンのコンテナステータスを確認する2. イメージをダウンロードまたは取...

MySQLテーブルのテーブル構造を素早く変更する方法

MySQL テーブルのテーブル構造をすばやく変更する - 「MySQL 管理」から抜粋 ALTER ...

Google ブラウザのラベルと入力間のスペースに関する小さな問題

最初にコード、次にテキストコードをコピーコードは次のとおりです。 <!DOCTYPE html...

JavaScript キャンバス テトリス ゲーム

テトリスは非常に古典的な小さなゲームで、私もそれを書いてみました。しかし、できるだけ簡潔で論理的なコ...