JavaScriptプロトタイプの理解


JavaScriptはプロトタイプベースの言語であると言われています.それで、「プロトタイプ」は重要な概念でなければなりませんか?
今日、私はプロトタイプが何であるか、あなたが知っている必要があり、プロトタイプをどのように効果的に使うかを説明します.

プロトタイプとは


まず第一に、「プロトタイプ」という言葉を誤解させないでください.JavaScriptの“プロトタイプ”は英語で“プロトタイプ”と同じものではありません.それはすぐに一緒に置かれた製品の初期バージョンを意味しません.
代わりに、JavaScriptのプロトタイプは、単に何も意味しない単語です.私たちはプロトタイプをオレンジに置き換えることができます、そして、それは同じことを意味することができます.
たとえば、アップルを考えてください.アップルコンピュータが普及する前に、あなたはおそらくアップルを赤い色の果物と考えます.アップルコンピュータの「アップル」は当初意味を持ちません、しかし、それは現在それをします.
JavaScriptの場合、プロトタイプはシステムを指します.このシステムでは、オブジェクトのインスタンスを介してアクセスできるオブジェクトのプロパティを定義できます.
注:注意
プロトタイプはオブジェクト指向プログラミングと密接に関連している.どのようなオブジェクト指向プログラミングについて理解していない場合、それは意味をなさないでしょう.
私はあなたがthis introductory series on Object Oriented Programming さらに進む前に.
::
例えば、Array 配列インスタンスの青写真です.配列のインスタンスを作成する[] or new Array() .
const array = ['one', 'two', 'three']
console.log(array)

// Same result as above
const array = new Array('one', 'two', 'three')
あなたならばconsole.log この配列では、メソッドはありません.しかし、あなたはconcat , slice , filter , and map !

なぜ?
これらのメソッドは、配列のプロトタイプにあります.あなたは拡張することができます__proto__ オブジェクト(クロムdevtools )あるいは<prototype> オブジェクト(Firefoxのdevtools)とメソッドのリストが表示されます.
配列.プロトタイプメソッド
プロトタイプログprototype注:注意
両方__proto__ クロムと<prototype> Firefoxはプロトタイプオブジェクトを指します.それらは異なるブラウザーで異なって書かれます.
::
あなたが使うときmap , ジャバスクリプトmap オブジェクト自体.If map が見つからない場合、JavaScriptはプロトタイプを探します.JavaScriptがプロトタイプを見つけた場合、それはmap そのプロトタイプで.
したがって、プロトタイプの正しい定義はインスタンスがプロパティを探すときにアクセスできるオブジェクトです.

プロトタイプチェーン


ここでは、プロパティにアクセスするときにJavaScriptがどのように動作しますか
Step 1 : JavaScriptはオブジェクト内で使用可能なプロパティをチェックします.yesの場合、JavaScriptはすぐにプロパティを使用します.
ステップ2 :プロパティがオブジェクトの内部でない場合、JavaScriptはプロトタイプが利用可能かどうかをチェックします.プロトタイプがあれば、ステップ1を繰り返してください(そして、プロパティがプロトタイプの中にあるかどうかチェックしてください).
ステップ3 :プロトタイプが残っていない場合、JavaScriptはプロパティを見つけることができません.
  • リターンundefined (プロパティにアクセスしようとした場合).
  • エラーをスローします(メソッドを呼び出した場合).
  • 概略的に、次のようになります.

    プロトタイプチェーンの例


    我々にはあると言いましょうHuman クラス.また、私たちはDeveloper 継承するサブクラスHuman . Human sがあるsayHello メソッドとDevelopers を持っているcode メソッド.
    ここにコードがありますHuman
    class Human {
      constructor(firstName, lastName) {
        this.firstName = firstName
        this.lastname = lastName
      }
    
      sayHello () {
        console.log(`Hi, I'm ${this.firstName}`)
      }
    }
    
    注:注意HumanDeveloper 以下はコンストラクタ関数で記述できます.コンストラクタ関数を使用する場合prototype より明確になりますが、サブクラスの作成は難しくなります.だから私はクラスの例を示している.(参照)this article オブジェクト指向プログラミングを使用する4つの異なる方法のために.
    ここでどのように書くだろうHuman 代わりにコンストラクタを使用する場合.
    function Human (firstName, lastName) {
      this.firstName = firstName
      this.lastName = lastName
    }
    
    Human.prototype.sayHello = function () {
      console.log(`Hi, I'm ${this.firstName}`)
    }
    
    ::
    ここにコードがありますDeveloper .
    class Developer extends Human {
      code (thing) {
        console.log(`${this.firstName} coded ${thing}`)
      }
    }
    
    エーDeveloper インスタンスはcode and sayHello これらのメソッドはインスタンスのプロトタイプチェーンにあります.
    const zell = new Developer('Zell', 'Liew')
    zell.sayHello() // Hi, I'm Zell
    zell.code('website') // Zell coded website
    
    あなたならばconsole.log インスタンスは、プロトタイプのチェーンでメソッドを見ることができます.

    プロトタイプ型委任/原型継承


    プロトタイプの委任と原型継承は同じことを意味します.
    彼らは単にプロトタイプシステムを使うと言いますprototype オブジェクト.

    プロトタイプの代表団を使うべきですか?


    JavaScriptはプロトタイプベースの言語であるため、プロトタイプ型のデリゲートを使用する必要があります.正しい?
    ではなく.
    私は、あなたがオブジェクト指向プログラミングを書く方法に依存すると主張します.より便利であるので、あなたがクラスを使うならば、それはプロトタイプを使うことを意味します.
    class Blueprint {
      method1 () {/* ... */}
      method2 () {/* ... */}
      method3 () {/* ... */}
    }
    
    しかし、ファクトリ関数を使用する場合、プロトタイプを使用しないという意味です.
    function Blueprint {
      return {
          method1 () {/* ... */}
          method2 () {/* ... */}
          method3 () {/* ... */}
      }
    }
    
    再び、読み取りthis article オブジェクト指向プログラミングを書く4つの異なった方法のために.

    性能含意


    あなたのアプリが何百万もの操作を必要としない限り、2つの方法の間のパフォーマンスは重要でありません.このセクションでは、この点を証明するためにいくつかの実験を共有します.

    セットアップ


    私たちはperformance.now 任意の操作を実行する前にタイムスタンプをログ出力します.操作を実行した後、我々は使用されますperformance.now タイムスタンプを再度ログ出力します.
    その後、タイムスタンプの違いを取得し、どのくらいの操作がかかったかを測定します.
    const start = performance.now()
    // Do stuff
    const end = performance.now()
    
    const elapsed = end - start
    console.log(elapsed)
    
    私はperf 私のテストを支援する関数
    function perf (message, callback, loops = 1) {
      const startTime = performance.now()
      for (let index = 0; index <= loops; index++) {
        callback()
      }
      const elapsed = performance.now() - startTime
      console.log(message + ':', elapsed)
    }
    
    注:あなたはもっと学ぶことができますperformance.now インthis article .

    試作実験1プロトタイプを用いないプロトタイプの使用


    まず最初に、私はそれがプロトタイプ自身を通してメソッドにアクセスするのにどれくらいかかるかをテストしました.
    以下はコードです.
    class Blueprint () {
      constructor () {
        this.inObject = function () { return 1 + 1 }
      }
    
      inPrototype () { return 1 + 1 }
    }
    
    const count = 1000000
    const instance = new Blueprint()
    perf('In Object', _ => { instance.inObject() }, count)
    perf('In Prototype', _ => { instance.inPrototype() }, count)
    
    この結果は以下の通りである.
    テスト
    10万のops
    1000万ops
    オブジェクト
    3 ms
    15 ms
    プロトタイプ
    2 ms
    12 ms
    注:結果はFirefoxのdevtoolsからです.読めるthis 私がなぜFirefoxでベンチマークであるかについて理解するために.
    評決:プロトタイプを使用するかどうかは問題ではない.100万の操作を実行しない限り、違いは生じません.

    実験対2:クラス対工場機能


    私がこのテストを実行しなければならなかったのは、クラスを使用するときにプロトタイプを使うことを推奨し、ファクトリ関数を使用するときにプロトタイプを使用しないことです.
    私は、ファクトリ関数を作成するかどうかをクラスを作成するよりもかなり遅くテストする必要がありました.
    これがコードです.
    // Class blueprint
    class HumanClass {
      constructor (firstName, lastName) {
        this.firstName = firstName
        this.lastName = lastName
      }
    
      sayHello () {
        console.lg(`Hi, I'm ${this.firstName}}`)
      }
    }
    
    // Factory blueprint
    function HumanFactory (firstName, lastName) {
      return {
        firstName,
        lastName,
        sayHello () {
            console.log(`Hi, I'm ${this.firstName}}`)
          }
      }
    }
    
    // Tests
    const count = 1000000
    perf('Class', _ => { new HumanClass('Zell', 'Liew') }, count)
    perf('Factory', _ => { HumanFactory('Zell', 'Liew') }, count)
    
    平均結果を次の表にまとめます.
    テスト
    10万のops
    1000万ops
    クラス
    5 ms
    18 ms
    ファクトリー
    6 ms
    18 ms
    評決:クラスまたはファクトリ関数を使用するかどうかは問題ではありません.100万稼動をしても違いはない.

    性能試験終了


    クラスまたはファクトリ関数を使用できます.あなたはプロトタイプを使用するかを選択することができますを選択します.それは本当にあなた次第です.
    パフォーマンスを心配する必要はありません.
    読書ありがとう.この記事はもともと投稿されたmy blog . サインアップするmy newsletter あなたがより良いフロントエンド開発者になるために、より多くの記事が欲しいならば.