views:

93

answers:

3

Hi, i was using object literal to create an object with methods.
Here a simple example.

var SizeManager = {
    width : 800,
    height : 600,
    ratio : this.width / this.height,
    resize : function (newWidth) {
        width = newWidth;
        height = newWidth / ratio;
    }
}

My issue is that SizeManager.ratio returns "NaN". I'm quite sure it's an initialization issue.
Is there a way to obtain a correct ratio value?
Is there a way to assign a costructor or initializer to an object literal?
Is defining a constructor objcet the only way?

EDIT: off course SizeManager is ideally a singleton (only one object), that's way i was using object literal.

+3  A: 

Your ratio property should actually be a method if you want it to change based on changes to width and height:

var SizeManager = {
    width : 800,
    height : 600,
    ratio : function() {
      return this.width / this.height;
    },
    resize : function (newWidth) {
        width = newWidth;
        height = newWidth / ratio;
    }
}

Also, you probably want to refer to this.width and this.height instead of width and height in the resize method.

Daniel Vandersluis
In resize function, `ratio` needs to be changed to `this.ratio()`
Josh Stodola
So ratio must be changed to a method and can not be a property. Right?
bonfo
@Josh I didn't change the resize method, I'll leave that to the OP. It'd probably be best to cache the old ratio before changing the width in it though, because with `ratio` being a function, the value will change after `this.width` is changed.
Daniel Vandersluis
@bonfo if `ratio` isn't a method, the value will never change (unless you explicitly reset `SizeManager.ratio = ...`), so unless you want the ratio fixed at 800/600, it should be a method.
Daniel Vandersluis
+6  A: 

Yes, it's an initialization issue. this does not refer to your SizeManager object at the point you're using it. (Literals don't create a this value.) this is set by how you call a function and has the same value throughout that function call. You're not creating any new function there (well, except for resize), so this has whatever value it had prior to the beginning of that code.

(I've pointed out something about ratio from your specific example at the very end of this, but first let's walk through a few options for the general case you raise.)

Daniel's given you a good steer on making ratio a function except he doesn't seem to have realized that you want to vary the width. Alternately, if width and height aren't going to change, just calculate it afterward:

var SizeManager = {
    width : 800,
    height : 600,
    resize : function (newWidth) {
        this.width = newWidth;
        this.height = newWidth / this.ratio;
    }
};
SizeManager.ratio = SizeManager.width / SizeManager.height;

(Side note: I've added this. to the properties you're referencing in resize. They were missing from your original, but they're required. Without them, you're dealing with the horror of implicit globals, which is a Bad Thing(tm).)

Of course, you might encapsulate all of that into a factory:

function makeSizeManager(width, height) {
    return {
        width : width,
        height : height,
        ratio : width / height,
        resize : function (newWidth) {
            this.width = newWidth;
            this.height = newWidth / this.ratio;
        }
    };
}
var SizeManager = makeSizeManager(800, 600);

...but then you might as well make it an actual constructor function so you don't create lots of duplicate (but identical) resize functions:

function SizeManager(width, height) {
    this.width = width;
    this.height = height;
    this.ratio = width / height;
}
SizeManager.prototype.resize = function (newWidth) {
    this.width = newWidth;
    this.height = newWidth / this.ratio;
};
var aSizeManagerInstance = new SizeManager(800, 600);

(Note I changed the names a bit on this last one.)

And as one last final note: In your specific example, you don't actually need to store ratio at all, you could do this:

var SizeManager = {
    width : 800,
    height : 600,
    resize : function (newWidth) {
        var ratio = this.width / this.height;
        this.width = newWidth;
        this.height = newWidth / ratio;
    }
};

But that's just for that specific example, hence the discussion above to talk about the general case.

T.J. Crowder
If you're changing it from an object literal to a function, you need to change the way you specify properties (in the third block of code). ie. `this.width = width` instead of `this.width: width`
Daniel Vandersluis
@Daniel: Thanks! looks like I only half-edited those lines after pasting. Fixed.
T.J. Crowder
A: 

In an object literal, this only refers to the object literal itself when it is inside a function. You can try something like this instead of an object literal; this will refer to the object you are creating both inside and outside of functions.

var SizeManager = new function(){
    this.width = 800;
    this.height = 600;
    this.ratio = this.width / this.height;
    this.resize = function (newWidth) {
        this.width = newWidth;
        this.height = newWidth / this.ratio;
    }
}

That will work for a singleton pattern. If you need more than one SizeManager, make it a constructor function instead of an object.

function SizeManager (w, h){
  w && (this.width=w);
  h && (this.height=h);
  this.ratio = this.width / this.height;
}
SizeManager.prototype={ 
  width: 800,
  height: 600,
  resize: function (newWidth) {
    this.width = newWidth;
    this.height = newWidth / this.ratio;
  }
}
no
*"In the object literal, `this` only refers to the object literal itself when it is inside a function."* There is no object literal **at all** in your code snippet. There's a constructor function, which you're calling immediately via `new`, but there's no object literal. Inside the constructor function, `this` refers to the new object created by `new` as part of calling the function.
T.J. Crowder
After calling `resize`, the `ratio` will be inaccurate
Josh Stodola
@T.J. Crowder: I was talking about the object literal in his code, not mine, obviously, as mine doesn't contain one, as you point out. Context, man.
no
Josh: good point.
no
@no: I *think* I see what you mean, but frankly I'd reword it. I know this stuff pretty well, but still thought you were talking about your code, not his.
T.J. Crowder
@T.J. Crowder: reworded it.
no
Juan Mendes
@Juan Mendes - you're right, it needs parens... fixed.
no
no
Juan Mendes