2

I'm rewriting a game I've designed in Flash/AS3.0 to javascript and am having trouble wrapping my head around prototypes, having learned classes. I've read a ton of info about it and am still not quite sure how I apply it to this scenario.

So... In my AS3.0 project I had a level class, which had the guts of what happens in all levels of the game, the Enter_Frame functionality etc. Then I extended that class with each level, for level specific details such as item placement, terrain details etc... So my question is how is the most efficient and 'proper' way to do this.

Right now I have the two functions:

function level() {
//generic level functionality
}

function level1() {
//level1 specific functionality
}

Then as I've read I should I have set

level1.prototype = new level();

But I have found this executes everything within the level function at runtime, which I don't want. Ideally I'd like a start button to instantiate level1, and level1 to have the properties and methods of level - AND, if possible, execute everything within level1's and level's constructor when this happens... Is it possible?

I'm sure it's obvious but I can't seem to click, sorry if I'm asking a silly question. Much appreciation to any help in advance.

Josh
  • 23
  • 3
  • In this answer there is another solution to setting inheritance without calling the constructor that doesn't use Object.create: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Jul 11 '13 at 09:15
  • @HMR I think I stumbled across that before writing this post, but you're right it's similar to Kevins answer I guess and will work. Thanks for your help! – Josh Jul 11 '13 at 09:55

4 Answers4

2

To avoid executing the constructor when establishing inheritance, you can use Object.create():

level1.prototype = Object.create(level.prototype);

Though, to "chain" constructor calls, you'll also need to call level within level1:

function level1() {
    level.call(this);

    // ...
}

Using call allows you to pass the context (this) along so both constructors work with the same object.

Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199
  • This does exactly what I want it to do even though I don't completely understand it. I will need to do more reading to properly understand when 'this' points to what etc. But thanks a lot for your answer you've helped me over a hurdle! – Josh Jul 11 '13 at 09:19
0

What you are trying to achieve is some sort of Object Oriented solution in JavaScript. Take a look at this blog post, which describes it in detail, and also gives an nice example how to go by. :-)

Eric
  • 18,532
  • 2
  • 34
  • 39
0

The parent constructor can be called by using Obj.call(this,args). The code should also keep the assignment of level1's prototype, level1.prototype = new level() since this will create an inheritance like relationship between the two objects.

function level(config) {
    if(config){
       this.someProperty = config.prop;
       alert("Level constructor");
       alert("Level property " + this.someProperty);
    }
}

function level1(config) {
   level.call(this, config);
   this.someProperty2 = config.prop2;
   alert("Level1 Construtor");
   alert("Leve1 property " + this.someProperty2);
   alert("Level1 using prop from level " + this.someProperty);
}

level1.prototype = new level();

var myLevel = new level1({
    prop: "Test prop",
    prop2: "Test prop2"
});

Working Example http://jsfiddle.net/kz2Xc/1/

Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
  • Hi Kevin, So in this example the 'level' constructor won't do anything at runtime because its contents are in the conditional if statement? Is it more advantageous to do it like this or use Object.create? Both seem to establish inheritance without executing constructor code. Object.create seems more efficient to me? - but I wouldn't know. Thanks for your help! – Josh Jul 11 '13 at 09:40
  • @Josh Go with johns method, I like it better – Kevin Bowersox Jul 11 '13 at 09:43
  • Sure, it seemed simpler to me. Thanks a bunch for your quick help – Josh Jul 11 '13 at 09:45
0

The user JonathanLonowski already answered your question about how to avoid to call the construction when setting up inheritanc. But I also read your question to be a general question about how inhertance works in JavaScript. So here is my attempt to explain this topic:

When it comes to inheritance, what you basically want to do is to enhance the prototype chain.

So, what it the prototype chain?

When you request a property of an object with i.e. obj.prop, JavaScript first checks, if the object obj has such a property. If not, it checks if the prototype of that object has such a property. If not, it checks if the prototype of the prototype has such a property and so on.

When you create a new object with a constructor function by writing obj = new B() then the prototype chains look as follows:

obj -> B.prototype -> Object -> null

How inheritance works with prototype chains

If you want your B objects to inherit from object of type A, you want you prototype chain to look like this:

obj -> B.prototype -> A.prototype -> Object -> null

To achieve this you "break" the prototype chain after B.prototype and insert instead the full prototype chain of A. Or in other words, you want to set B.prototype to point to A.prototype.

You can do this, as you did before:

B.prototype = new A();

Now why does this work? The object created by new A() has the following prototype chain:

objA -> B.prototype -> Object -> null

If you override B.prototype with this object, the prototype chain of an object of type B looks like this:

objB -> objA -> A.prototype -> Object -> null

And as you see, it works. By simply overriding B.prototype you just lose some properties like the constructor property. So if you try to get the constructor of an object of type B with objB.constructor, it first looks in the object itself and doesn't find it, then looks in objA and doesn't find it and then looks in A.prototype and find A, which is wrong. Although you don't need the constructor property very often it can be considered good practice to set it again after overriding B.prototype:

B.prototype = new A();
B.prototype.constructor = B;

Now, your way is just ONE way to achieve linking of the prototype chains. The solution by JonathanLonowski is another one and indeed a better one, since it doesn't call the function A. It creates an empty object with the same prototype chain as an object of type A:

(empty object) -> A.prototype -> Object -> null

Another solution would be to not override B.prototype but modify it to simply point to A.prototype instead of Object. Not every browser supports this, but in some you can achieve this by setting the __proto__ property:

B.prototype.__proto__ = A.prototype;

This will result in the following prototype chain:

obj -> B.prototype -> A.prototype -> Object -> null

Please note, that I simplyfied it here and there, but I think this should give you an idea of how it works. The very basic thing to understand is prototyping. Now, technically, there is only one true prototype property, that every object has and that is the internal [[prototype]] property. You can't access it directly in JavaScript, but you have .prototype and .__proto__ to set it and manipulate it under certain conditions.

basilikum
  • 10,378
  • 5
  • 45
  • 58
  • basilikum I read through what you wrote here and it makes sense, I now really need to go and put it into application for it to sink in. Thanks heaps for the thorough and clear explanation! – Josh Jul 11 '13 at 12:42