Jump to content

best way to initiate a state?


Chrischi
 Share

Recommended Posts

Hello everyone , 

 

I am trying to fully figure out Phaser and one thing keeps on returning: 

In some tutorials a state is initiated via a prototype (or rather, given methods), like this: 

StateA = function (game) {};StateA.prototype = {// Things happening here}

Whereas in other tutorials it is simply written as

StateA = {// stuff}

Both ways seem to be doing the same thing. Is there a advantage for using one way or the other is it purely personal preference? 

Many thanks :) 

Link to comment
Share on other sites

Defining functions in the javascript prototype variable should be more efficient than not using the prototype field.  As a rule of thumb, include the state functions in a prototype block.  For more technical details, see this post on StackOverflow: http://stackoverflow.com/questions/3493252/javascript-prototype-operator-performance-saves-memory-but-is-it-faster

 

Tom

 

Link to comment
Share on other sites

Technically, Tom is telling you the truth. But, in practice, creating a full blown JS prototype for an object you instantiate once has no benefit over using an object literal.

 

That's not to say you shouldn't do it, 'cuz, y'know, who cares, it's your code and it'll still work just fine and not cause you any problems really. Just don't say you're doing it for performance reasons.

Link to comment
Share on other sites

With EcmaScript 5 and higher, it's merely a matter of preference. In the ES3 days, we used prototype-inheritance similar to how other OO languages use classes- that is, to make a data-structure for polymorphic instances of said data. For example, if I'm creating a Person function, and I'm going to be using it to create Bob, Tom, and Jerry, I would make a function called Person, and put properties/methods in the prototype for that function, or sometimes in the constructor itself (using this.property = val). It would look like this:

function Person (name) {  this.name = name;};Person.prototype.whoAmI = function whoAmI () {  alert("I am " + this.name);};var bob = new Person("Bob");var tom = new Person("Tom");var jerry = new Person("Jerry");bob.whoAmI();tom.whoAmI();jerry.whoAmI();

Again, in ES3 if I wanted to have an object that would only be used once, instead of making a function and all that, I would just use an object literal. Say I had a one-player game, and I wanted to store information on that player. That information may change, but I will never be creating more than one player, I would use an object literal:

var player = {  init: function (name) {    this.name = name;  },  whoAmI: function whoAmI () {    alert("I am " + this.name);  }};player.init("Peter");player.whoAmI();

In ES3, there were some tricks you could do to make the second example work polymorphic like the first example, and in ES5 we were given Object.create()- which works very much like 'new' does. So now, even-more-so than before, it is just personal preference. For kicks, here's how to make a polymorphic object-literal in ES5:

var Player = {  init: function (name) {    this.name = name;  },  whoAmI: function whoAmI () {    alert("I am " + this.name);  }};var bob = Object.create(Player);bob.init("Bob");var tom = Object.create(Player);tom.init("Tom");var jerry = Object.create(Player);jerry.init("Jerry");bob.whoAmI();tom.whoAmI();jerry.whoAmI();

There are technical differences in this last example and the first example, but they are both accomplishing the same goal. The 2nd example, however, is for monolithic data-structures. I use the first example for polymorphic data structures, and the second example for monolithic data structures so that my code is easy for me to understand. It should be noted I tend to stay away from Object.create in situations like this for sanity- though I've been known to use Object.create when making JS libraries. I prefer to know when to use 'new' based on the upper-case letter I start function names with. If it starts with a capital letter, I know I need to use 'new' when calling that function, and I know it's safe to assume the value will be an object. I suggest you adopt similar rules-of-thumb for yourself.

 

Douglas Crockford really pointed me in the direction I chose to go when I started learning JavaScript- I mean really learning the language inside and out. If you're seeking direction, I came to the conclusion, personally, that the rules he came up with for JavaScript are.. I can't think of words to describe it.. I can't see myself coding JavaScript any other way, and still be sane after all these years had I not been following Douglas Crockford's advice.

Link to comment
Share on other sites

drhayes has a point regarding optimization being of little use if an object is only instantiated once.  I was surprised that s-p-n stated prototype made little difference.  This got me thinking that maybe I'm getting too old and set in my ways.  So, I ran a benchmark to see what the current state of things is.  (The results are below the code.  Spoiler - it may make a difference on mobile devices.)

 

The following code is copied from a StackOverflow post: http://stackoverflow.com/questions/3493252/javascript-prototype-operator-performance-saves-memory-but-is-it-faster

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 < 1000000; i++) {y = new Y();y.message('hi');y.addition(i,2)}console.log((new Date()).getTime() - intNow); //FF=5206ms; Safari=1554intNow = (new Date()).getTime();for (i = 0; i < 1000000; i++) {x = new X();x.message('hi');x.addition(i,2)}console.log((new Date()).getTime() - intNow);//FF=3894ms;Safari=606

I tested on a MacBook Pro, running Chrome, Firefox, Safari.  I also tested using Intel XDK Crosswalk debugger on a Samsung Galaxy Tab 4.  I only did one trial of each test.

 

Chrome (without prototype): 7525ms

Chrome (with prototype): 7178ms

Prototype time was 95% of without prototype time.

 

Firefox (without prototype): 3110ms

Firefox (with prototype): 2956ms

Prototype time was 95% of without prototype time.

 
Safari (without prototype): 938ms
Safari (with prototype): 878ms

Prototype time was 94% of without prototype time.

 
Samsung Galaxy Tab 4 w/Crosswalk (without prototype): 19182ms
Samsung Galaxy Tab 4 w/Crosswalk (with prototype): 15217ms
Prototype time was 79% of without prototype time.
 
Taking the results at face value, I would guess that using prototype may make a difference on mobile devices.  In general, I do not trust consistency across different kinds of mobile devices.  Given what I am seeing here, I would err on the side of caution.
 
Aside from performance, I worry about code maintainability.  I hop across many languages on a regular basis, so if I were to change the way I do classes (or prototypes, as appropriate) in one place, I would want to be consistent across all the code I wrote.  That includes references to static functions, modifications to third-party libraries, etc.
 
However, all this said, I am really just speaking for myself and what works for me.  Regardless of any other factors, I would say the cardinal rule in making games is do whatever works.  But then, that's just a rule that works for me.   :)
 
Tom
 
 
Link to comment
Share on other sites

drhayes has a point regarding optimization being of little use if an object is only instantiated once.  I was surprised that s-p-n stated prototype made little difference.  This got me thinking that maybe I'm getting too old and set in my ways.  So, I ran a benchmark to see what the current state of things is.  (The results are below the code.  Spoiler - it may make a difference on mobile devices.)

 

The following code is copied from a StackOverflow post: http://stackoverflow.com/questions/3493252/javascript-prototype-operator-performance-saves-memory-but-is-it-faster

I could be wrong, but doesn't that code miss the point somewhat? The loop combines the cost of instantiation with the cost of the method calls, and doesn't involve inheritance (so the their is a trivial prototype chain that has to be walked when making the prototype method calls) - to my understanding objects with prototype methods may be slightly faster to instantiate (because they don't have to set their "function pointers") negating some of the difference
Link to comment
Share on other sites

(For anyone reading this thread who is wondering, "What the heck is a prototype?" take a look at section 4.2.1, specifically Figure 1, of the ECMAScript 2015 Language Specification: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf .)

 

Moving the object construction out of the loops, some results I got were:

 

Chrome (without prototype): 6208ms
Chrome (with prototype): 6417ms
Without prototype time was 97% of with prototype time.
 
Firefox (without prototype): 2130ms
Firefox (with prototype): 2190ms
Without prototype time was 97% of with prototype time.
 
Safari (without prototype): 993ms
Safari (with prototype): 664ms
Prototype time was 96% of without prototype time.
 
Samsung Galaxy Tab 4 w/Crosswalk (without prototype): 11190ms
Samsung Galaxy Tab 4 w/Crosswalk (with prototype): 10893ms
Prototype time was 97% of without prototype time.
 
I was a bit surprised by the inconsistent results, but this would make sense in that different optimizations are being used on different platforms.  I would expect more inconsistencies if I sampled more, especially older, mobile devices.  Whether these differences matter depends entirely on the application structure.  When offering advice, my thinking is to recommend a single construct that offers the most advantages overall, and to encourage code consistency.  Using prototypes can save memory, sometimes improves performance, and exercises an aspect of Javascript that those new to the language are sometimes reticent to explore, despite its potential importance.
 
The code with object construction outside of the loops (derived from the code at: http://stackoverflow.com/questions/3493252/javascript-prototype-operator-performance-saves-memory-but-is-it-faster):
 
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();y = new Y();for (i = 0; i < 1000000; i++) {y.message('hi');y.addition(i,2)}console.log((new Date()).getTime() - intNow); intNow = (new Date()).getTime();x = new X();for (i = 0; i < 1000000; i++) {x.message('hi');x.addition(i,2)}console.log((new Date()).getTime() - intNow);
 
 
Tom
Link to comment
Share on other sites

These benchmarks are great, but the question appears to be asking the difference between regular objects and an instance object.

 

I don't have a bunch of fancy devices to test on, but this is what I got on my desktop:

(function () {	var i, iterations, start, fooTime, barTime, foo, Bar, bar;	iterations = 10000000;	foo = {		something: "",		changeSomething: function changeSomething (to) {			this.something = to;		}	};	Bar = function Bar () {};	Bar.prototype.something = "";	Bar.prototype.changeSomething = function changeSomething (to) { 		this.something = to;	};	bar = new Bar();	start = Date.now();	for (i = 0; i < iterations; i += 1) {		foo.changeSomething("ran " + i + " times.");	}	fooTime = Date.now() - start;	start = Date.now();	for (i = 0; i < iterations; i += 1) {		bar.changeSomething("ran " + i + " times.");	}	barTime = Date.now() - start;	console.log("Foo Time:", fooTime);	console.log("Bar Time:", barTime);}());// Foo Time: 1801 <- Chrome; 1289 <- Firefox// Bar Time: 2944 <- Chrome; 1228 <- Firefox

The time that JS engines take to execute little snippets of code 10 million times is irrelevant. Are you going to be changing a value to one instance of something 10 million times in your game? I mean really?

 

Just write code that is easy to maintain- the difference in time it takes for JS engines to execute code from one style vs another is irrelevant. Put logic in the constructor if you need logic there. If you're going to be passing data during instantiation, example `new Person("Fred")`, a method defined on a prototype will not save you an amount of CPU cycles that makes a difference. In fact, I'd bet it would be slower to use a prototype in this case:

(function () {	var i, iterations, start, fooTime, barTime, Foo, foo, Bar, bar;	iterations = 10000000;	var Foo = function Foo (name) {		this.name = name;	};	Foo.prototype.name = "";	var Bar = function Bar () {};	Bar.prototype.name = "";	Bar.prototype.init = function (name) {		this.name = name;	};	start = Date.now();	for (i = 0; i < iterations; i += 1) {		foo = new Foo("Foo" + i);	}	fooTime = Date.now() - start;	start = Date.now();	for (i = 0; i < iterations; i += 1) {		bar = new Bar();		bar.init("Bar" + i);	}	barTime = Date.now() - start;	console.log("Foo Time:", fooTime);	console.log("Bar Time:", barTime);}());// Foo Time: 1786 <- Chrome; 998 <- Firefox// Bar Time: 3662 <- Chrome; 1030 <- Firefox

If you need to set data from the constructor, it not only feels more natural, it is faster to do it that way. Just code in a way that feels natural.

 

Premature optimization is the root of all evil.

 

If I wanted to optimize this code to be as fast as possible, I wouldn't be using prototypes or objects at all....

(function () {	var i, iterations, start, fooTime, barTime, Foo, foo, bar = "";	iterations = 10000000;	Foo = function Foo (name) {		this.name = name;	};	Foo.prototype.name = "";	start = Date.now();	for (i = 0; i < iterations; i += 1) {		foo = new Foo("Foo" + i);	}	fooTime = Date.now() - start;	start = Date.now();	for (i = 0; i < iterations; i += 1) {		bar = "Bar" + i;	}	barTime = Date.now() - start;	console.log("Foo Time:", fooTime);	console.log("Bar Time:", barTime);}());// Foo Time: 1737 <- Chrome; 1012 <- Firefox// Bar Time: 2978 <- Chrome; 7 <- Firefox

Lol.. Turns out that assigning a variable a string value in chrome, is almost 2x as slow as instantiating a constructor that assigns a string to a property. Does that mean I should go through all my code, and if the browser is chrome change all the strings to constructors and assign them by instantiating them or invoking a method? NO!! That does not feel natural at all, and it results in some kind of endless loop of refactoring.. 

 

Anyway, my point is, browsers optimize for these kinds of benchmarks because people are silly enough to think these benchmarks apply to real-life code, and they really don't..

 

Again, just code in a way that is easy to understand, and optimize when you need to.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...