52

ここ(Douglas Crockford)を読んで、プロトタイプ演算子を使用してJavascriptクラスにメソッドを追加すると、メモリも節約されます。

次に、このJohn Resigの記事 「多数のプロトタイププロパティを使用して関数をインスタンス化するのは非常に高速ですを読みましたが、彼は標準的な方法でプロトタイプを使用することについて話しているのでしょうか、それとも彼の記事で彼の特定の例について話しているのでしょうか。

たとえば、次のオブジェクトを作成しています。

function Class1()
{
   this.showMsg = function(string) { alert(string); }
}
var c = new Class1();
c.showMsg();

では、このオブジェクトを作成するよりも遅いですか?

function Class1() {}
Class1.prototype.showMsg = function(string) { alert(string); }
var c = new Class1();
c.showMsg();

PS

プロトタイプが継承やシングルトンオブジェクトなどを作成するために使用されていることは知っています。しかし、この質問はこれらの主題とは何の関係もありません。


編集: JSオブジェクトとJS静的オブジェクトのパフォーマンス比較にも関心があるかもしれない人は、以下のこの回答を読むことができます。静的オブジェクトは間違いなく高速です。明らかに、オブジェクトの複数のインスタンスが必要ない場合にのみ使用できます。

4

10 に答える 10

64

2021年に編集:

この質問はclass、JSで利用できなかった2010年に行われました。今日でclassは、それを使用しない言い訳がないほど最適化されています。を使用する必要がある場合はnew、を使用してclassください。しかし、2010年には、メソッドをオブジェクトコンストラクターにバインドするときに、2つのオプションがありました。1つはを使用して関数コンストラクター内で関数をthisバインドすることで、もう1つはを使用してコンストラクター外で関数をバインドすることでしたprototype。@MarcoDemaioの質問には、非常に簡潔な例があります。JSに追加されたときclass、初期の実装はパフォーマンスが近いものでしたが、通常は低速でした。それはもはやリモートでは真実ではありません。を使用するだけclassです。今日使う理由は思いつかないprototype


興味深い質問だったので、いくつかの非常に簡単なテストを実行しました(メモリをクリアするためにブラウザを再起動する必要がありましたが、実行しませんでした。これを価値のあるものと見なしてください)。少なくともSafariとFirefoxでのように見え、prototype大幅に高速に実行されます[編集:前述の20倍ではありません]。フル機能のオブジェクトを使用した実際のテストの方が、より良い比較になると確信しています。私が実行したコードは次のとおりです(テストを別々に数回実行しました):

var X,Y, x,y, i, intNow;

X = function() {};
X.prototype.message = function(s) { var mymessage = s + "";}
X.prototype.addition = function(i,j) { return (i *2 + j * 2) / 2; }

Y = function() {
  this.message = function(s) { var mymessage = s + "";}
  this.addition = function(i,j) { return (i *2 + j * 2) / 2; }
};


intNow = (new Date()).getTime();
for (i = 0; i < 10000000; i++) {
  y = new Y();
  y.message('hi');
  y.addition(i,2)
}
console.log((new Date()).getTime() - intNow); //FF=5206ms; Safari=1554

intNow = (new Date()).getTime();
for (i = 0; i < 10000000; i++) {
  x = new X();
  x.message('hi');
  x.addition(i,2)
}
console.log((new Date()).getTime() - intNow);//FF=3894ms;Safari=606

私は本当に使用するのが嫌いなので、それは本当に残念prototypeです。オブジェクトコードが自己カプセル化され、ドリフトしないようにするのが好きです。スピードが重要なときは、私には選択の余地がないと思います。くそー。

[編集]私の前のコードが間違っていたことを指摘してくれた@Kevinに感謝し、報告されたprototypeメソッドの速度を大幅に向上させました。修正後も、プロトタイプはかなり高速ですが、違いはそれほど大きくありません。

于 2010-08-16T13:46:43.270 に答える
32

作成するオブジェクトのタイプに依存すると思います。アンドリューと同様のテストを実行しましたが、静的オブジェクトを使用して、静的オブジェクトが圧倒的に勝ちました。テストは次のとおりです。

var X, Y, Z, x, y, z;

X = function() {};
X.prototype.message = function(s) {
  var mymessage = s + "";
}
X.prototype.addition = function(i, j) {
  return (i * 2 + j * 2) / 2;
}

Y = function() {
  this.message = function(s) {
    var mymessage = s + "";
  }
  this.addition = function(i, j) {
    return (i * 2 + j * 2) / 2;
  }
};

Z = {
  message: function(s) {
    var mymessage = s + "";
  },
  addition: function(i, j) {
    return (i * 2 + j * 2) / 2;
  }
}

function TestPerformance() {
  var closureStartDateTime = new Date();
  for (var i = 0; i < 100000; i++) {
    y = new Y();
    y.message('hi');
    y.addition(i, 2);
  }
  var closureEndDateTime = new Date();

  var prototypeStartDateTime = new Date();
  for (var i = 0; i < 100000; i++) {
    x = new X();
    x.message('hi');
    x.addition(i, 2);
  }
  var prototypeEndDateTime = new Date();

  var staticObjectStartDateTime = new Date();
  for (var i = 0; i < 100000; i++) {
    z = Z; // obviously you don't really need this
    z.message('hi');
    z.addition(i, 2);
  }
  var staticObjectEndDateTime = new Date();
  var closureTime = closureEndDateTime.getTime() - closureStartDateTime.getTime();
  var prototypeTime = prototypeEndDateTime.getTime() - prototypeStartDateTime.getTime();
  var staticTime = staticObjectEndDateTime.getTime() - staticObjectStartDateTime.getTime();
  console.log("Closure time: " + closureTime + ", prototype time: " + prototypeTime + ", static object time: " + staticTime);
}

TestPerformance();

このテストは、次の場所で見つけたコードを変更したものです。

リンク

結果:

IE6: 閉鎖時間: 1062、プロトタイプ時間: 766、静的オブジェクト時間: 406

IE8: 閉鎖時間: 781、プロトタイプ時間: 406、静的オブジェクト時間: 188

FF: 閉鎖時間: 233、プロトタイプ時間: 141、静的オブジェクト時間: 94

Safari: 閉鎖時間: 152、プロトタイプ時間: 12、静的オブジェクト時間: 6

Chrome: 閉鎖時間: 13、プロトタイプ時間: 8、静的オブジェクト時間: 3

学んだ教訓は、同じクラスから多くの異なるオブジェクトをインスタンス化する必要がない場合は、それを静的オブジェクトとして作成する方が有利だということです。そのため、本当に必要なクラスを慎重に検討してください。

于 2010-10-28T09:40:18.023 に答える
6

ということで、これも試してみることにしました。作成時間、実行時間、メモリ使用量をテストしました。Nodejs v0.8.12 と、Windows 7 で起動した Mac Book Pro で実行される mocha テスト フレームワークを使用しました。「速い」結果はプロトタイプを使用しており、「遅い」結果はモジュール パターンを使用しています。各タイプのオブジェクトを 100 万個作成し、各オブジェクトの 4 つのメソッドにアクセスしました。結果は次のとおりです。

c:\ABoxAbove>mocha test/test_andrew.js

Fast Allocation took:170 msec
·Fast Access took:826 msec
state[0] = First0
Free Memory:5006495744

·Slow Allocation took:999 msec
·Slow Access took:599 msec
state[0] = First0
Free Memory:4639649792

Mem diff:358248k
Mem overhead per obj:366.845952bytes

? 4 tests complete (2.6 seconds)

コードは次のとおりです。

var assert = require("assert"), os = require('os');

function Fast (){}
Fast.prototype = {
    state:"",
    getState:function (){return this.state;},
    setState:function (_state){this.state = _state;},
    name:"",
    getName:function (){return this.name;},
    setName:function (_name){this.name = _name;}
};

function Slow (){
    var state, name;
    return{
        getState:function (){return this.state;},
        setState:function (_state){this.state = _state;},
        getName:function (){return this.name;},
        setName:function (_name){this.name = _name;}
    };
}
describe('test supposed fast prototype', function(){
    var count = 1000000, i, objs = [count], state = "First", name="Test";
    var ts, diff, mem;
    it ('should allocate a bunch of objects quickly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){objs[i] = new Fast ();}
        diff = Date.now () - ts;
        console.log ("Fast Allocation took:%d msec", diff);
        done ();
    });
    it ('should access a bunch of objects quickly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){
            objs[i].setState (state + i);
            assert (objs[i].getState () === state + i, "States should be equal");
            objs[i].setName (name + i);
            assert (objs[i].getName () === name + i, "Names should be equal");
        }
        diff = Date.now() - ts;
        console.log ("Fast Access took:%d msec", diff);
        console.log ("state[0] = " + objs[0].getState ());
        mem = os.freemem();
        console.log ("Free Memory:" + mem + "\n");
        done ();
    });
    it ('should allocate a bunch of objects slowly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){objs[i] = Slow ();}
        diff = Date.now() - ts;
        console.log ("Slow Allocation took:%d msec", diff);
        done ();
    });
    it ('should access a bunch of objects slowly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){
            objs[i].setState (state + i);
            assert (objs[i].getState () === state + i, "States should be equal");
            objs[i].setName (name + i);
            assert (objs[i].getName () === name + i, "Names should be equal");
        }
        diff = Date.now() - ts;
        console.log ("Slow Access took:%d msec", diff);
        console.log ("state[0] = " + objs[0].getState ());
        var mem2 = os.freemem();
        console.log ("Free Memory:" + mem2 + "\n");
        console.log ("Mem diff:" + (mem - mem2) / 1024 + "k");
        console.log ("Mem overhead per obj:" + (mem - mem2) / count + 'bytes');
        done ();
    });
});

結論:これは、この投稿の他の人が見つけたものを裏付けています. 常にオブジェクトを作成している場合は、プロトタイプ メカニズムの方が明らかに高速です。コードがほとんどの時間をオブジェクトへのアクセスに費やしている場合は、モジュール パターンの方が高速です。メモリの使用に敏感な場合、プロトタイプ メカニズムはオブジェクトごとに最大 360 バイト少なくなります。

于 2012-11-13T22:51:52.317 に答える
2

直観的には、プロトタイプで関数を作成する方がメモリ効率が高く、高速であるように思われます。関数は、新しいインスタンスが作成されるたびに作成されるのではなく、一度だけ作成されます。

ただし、関数にアクセスするときは、わずかなパフォーマンスの違いがあります。がc.showMsg参照されると、JavaScript ランタイムは最初に のプロパティをチェックしますc。見つからない場合は、cのプロトタイプがチェックされます。

そのため、インスタンスにプロパティを作成すると、アクセス時間がわずかに短縮されますが、これはプロトタイプ階層が非常に深い場合にのみ問題になる可能性があります。

于 2010-08-17T02:31:19.957 に答える
1

高解像度ブラウザ パフォーマンス API テスト

ここでのテストはいずれも、高解像度テストにパフォーマンス APIを利用していないため、ほとんどの実行で他のどの回答よりも高速な 2 を含む、さまざまなシナリオで現在最速の結果を示すものを作成しました。

各カテゴリで絶食 (10,000 回の繰り返し)

  • プロパティ アクセスのみ(~0.5ms) : { __proto__: Type }
  • プロパティ アクセスによるオブジェクト作成のループ(<3ms) : Object.create(Type)

このコードは、精度を確保するためにバベル変換なしで ES6 を使用しています。現在のクロムで動作します。以下のテストを実行して、内訳を確認します。

function profile () {
  function test ( name
                , define
                , construct
                , { index = 0
                  , count = 10000
                  , ordinals = [ 0, 1 ]
                  , constructPrior = false
                  } = {}
                ) {
    performance.clearMarks()
    performance.clearMeasures()
    const symbols = { type: Symbol('type') }
    const marks = (
      { __proto__: null
      , start: `${name}_start`
      , define: `${name}_define`
      , construct: `${name}_construct`
      , end: `${name}_end`
      }
    )

    performance.mark(marks.start)
    let Type = define()
    performance.mark(marks.define)

    let obj = constructPrior ? construct(Type) : null
    do {
      if(!constructPrior)
        obj = construct(Type)
      if(index === 0)
        performance.mark(marks.construct)

      const measureOrdinal = ordinals.includes(index)
      if(measureOrdinal)
          performance.mark(`${name}_ordinal_${index}_pre`)

      obj.message('hi')
      obj.addition(index, 2)

      if(measureOrdinal)
        performance.mark(`${name}_ordinal_${index}_post`)
    } while (++index < count)
    performance.mark(marks.end)

    const measureMarks = Object.assign (
      { [`${name}_define`]: [ marks.start, marks.define ]
      , [`${name}_construct`]: [ marks.define, marks.construct ]
      , [`${name}_loop`]: [ marks.construct, marks.end ]
      , [`${name}_total`]: [ marks.start, marks.end ]
      }
    , ordinals.reduce((reduction, i) => Object.assign(reduction, { [`${name}_ordinal_${i}`]: [ `${name}_ordinal_${i}_pre`, `${name}_ordinal_${i}_post` ] }), {})
    )

    Object.keys(measureMarks).forEach((key) => performance.measure(key, ...measureMarks[key]))

    const measures = performance.getEntriesByType('measure').map(x => Object.assign(x, { endTime: x.startTime + x.duration }))
    measures.sort((a, b) => a.endTime - b.endTime)
    const durations = measures.reduce((reduction, measure) => Object.assign(reduction, { [measure.name]: measure.duration }), {})

    return (
      { [symbols.type]: 'profile'
      , profile: name
      , duration: durations[`${name}_total`]
      , durations
      , measures
      }
    )
  }

  const refs = (
    { __proto__: null
    , message: function(s) { var mymessage = s + '' }
    , addition: function(i, j) { return (i *2 + j * 2) / 2 }
    }
  )

  const testArgs = [
    [ 'constructor'
    , function define() {
        return function Type () {
          this.message = refs.message
          this.addition = refs.addition
        }
      }
    , function construct(Type) {
        return new Type()
      }
    ]
  , [ 'prototype'
    , function define() {
        function Type () {
        }
        Type.prototype.message = refs.message
        Type.prototype.addition = refs.addition
        return Type
      }
    , function construct(Type) {
        return new Type()
      }
    ]
  , [ 'Object.create'
    , function define() {
        return (
          { __proto__: null
          , message: refs.message
          , addition: refs.addition
          }
        )
      }
    , function construct(Type) {
        return Object.create(Type)
      }
    ]
  , [ 'proto'
    , function define() {
        return (
          { __proto__: null
          , message: refs.message
          , addition: refs.addition
          }
        )
      }
    , function construct(Type) {
        return { __proto__: Type }
      }
    ]
  ]

  return testArgs.reduce(
    (reduction, [ name, ...args ]) => (
      Object.assign( reduction
      , { [name]: (
            { normal: test(name, ...args, { constructPrior: true })
            , reconstruct: test(`${name}_reconstruct`, ...args, { constructPrior: false })
            }
          )
        }
      )
    )
  , {})
}

let profiled = profile()
const breakdown = Object.keys(profiled).reduce((reduction, name) => [ ...reduction, ...Object.keys(profiled[name]).reduce((r, type) => [ ...r, { profile: `${name}_${type}`, duration: profiled[name][type].duration } ], []) ], [])
breakdown.sort((a, b) => a.duration - b.duration)
try {
  const Pre = props => React.createElement('pre', { children: JSON.stringify(props.children, null, 2) })
  
  ReactDOM.render(React.createElement(Pre, { children: { breakdown, profiled } }), document.getElementById('profile'))
} catch(err) {
    console.error(err)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="profile"></div>

于 2016-08-29T06:43:18.957 に答える
0

オブジェクトのインスタンス化に関する限り、それははるかに高速であり、メモリの消費も少ないと確信していますが、それについては疑いの余地はありませんが、JavaScript エンジンはオブジェクトのすべてのプロパティをループして、プロパティが/呼び出されたメソッドはそのオブジェクトの一部であり、そうでない場合は、プロトタイプを確認します。私はこれについて100%確信が持てませんが、それがどのように機能するかを想定しています.少し遅いですが、それは私が何もテストしていない単なる仮定です.

しかし、最終的には、一般的なルールとして、prototype を使用した方が高速であることに同意します。

于 2010-08-16T13:15:15.797 に答える
-3

そのため、インスタンスにプロパティを作成すると、アクセス時間がわずかに速くなりますが、これはプロトタイプ階層が非常に深い場合にのみ問題になる可能性があります。

実際には、結果は予想とは異なります。プロトタイプ化されたメソッドへのアクセス時間は、オブジェクトに正確にアタッチされたメソッドにアクセスするよりも高速です (FF テスト済み)。

于 2010-11-03T09:58:10.707 に答える