1

An example can be seen on this JSFiddle

I'm creating an instance, changing one of the functions of that instance, and then calling that function within a different function in that instance. I'm having difficulty accessing local and instance variables inside the updated function.

Code

var MyObj = function (name) {
    var that = this;
    MyObj.myObjs = (MyObj.myObjs || []),
    this.objName = name,
    this.objIdx = MyObj.myObjs.length;
    MyObj.myObjs.push(this.objName);

    this.doOnSetName = function () {};

    this.setName = function (name) {
        that.doOnNameSet();
        that.objName = name;
        MyObj.myObjs[that.objIdx] = name;
    }
}

var obj1 = new MyObj("obj1");
//obj1.doOnNameSet = function() { alert("objName: "+this.objName) };
//obj1.setName("obj1");

var obj2 = new MyObj("obj2");
obj2.doOnNameSet = function () {
    $("#console").append("Old objName: " + this.name
                         + "<br />New objName: " + name + "<br />")
};
obj2.setName("obj2 - changed");

$("#console ").append("Objects: <br />*" + MyObj.myObjs.join(", <br />*"));

Actual Outcome

Old objName: undefined
New objName: result
Objects: 
*obj1, 
*obj2 - changed

Desired Outcome

Old objName: obj2
New objName: obj2 - changed
Objects: 
*obj1, 
*obj2 - changed
Smern
  • 18,746
  • 21
  • 72
  • 90
  • First of all; since your functions will do the same for every instance of MyObj please declare them as MyObj.prototype.setName ... properties declared with this are properties that should be unique to object instances. – HMR May 16 '13 at 14:04
  • Some more info on JS prototype and objects can be found here: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR May 16 '13 at 14:09
  • You definitly cannot access `name` that way because it is a local variable that is only available in the scope of `setName`. You should be able to access `this.objName` and i think this part of your question has been already resolved. But for `name` I really see no other possibility than passing it as an argument to `doOnSetName`. – basilikum May 16 '13 at 14:52
  • Yea, `this.objName` was a typo and `name` shouldn't have really even been part of this problem, I just threw it in while I was making the example for the question. I actually voted to close this as it really revolves around a stupid typo that I didn't notice. *facepalm* – Smern May 16 '13 at 15:18

4 Answers4

2
that.objName = name;
MyObj.myObjs[that.objIdx] = name;
that.doOnNameSet();

means that the old name is already forgotten when the listener is called. I'm not sure how you did intend to get it. Maybe call the listener before you change it, with the new name as an argument.

obj2.doOnNameSet = function () {
     $("#console").append("Old objName: " + this.name
                          + "<br />New objName: " + name + "<br />");
};

The property is named objName, and that name variable is obviously undefined. Did you want to have it as a parameter?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
2

You could pass, the old and the new name to doOnSetName (Fiddle):

var MyObj = function (name) {
    var that = this;
    MyObj.myObjs = (MyObj.myObjs || []),
    this.objName = name,
    this.objIdx = MyObj.myObjs.length;
    MyObj.myObjs.push(this.objName);

    this.doOnSetName = function (oldName, newName) {};

    this.setName = function (name) {
        that.doOnNameSet(that.objName, name);
        that.objName = name;
        MyObj.myObjs[that.objIdx] = name;
    }
}

var obj1 = new MyObj("obj1");
//obj1.doOnNameSet = function() { alert("objName: "+this.objName) };
//obj1.setName("obj1");

var obj2 = new MyObj("obj2");
obj2.doOnNameSet = function (oldName, newName) {
    $("#console").append("Old objName: " + oldName
                         + "<br />New objName: " + newName + "<br />")
};
obj2.setName("obj2 - changed");

$("#console ").append("Objects: <br />*" + MyObj.myObjs.join(", <br />*"));
basilikum
  • 10,378
  • 5
  • 45
  • 58
1

The errors are in your obj2.doOnNameSet method. this.name should be this.objName. name is undefined in your code snippet but presumably it is defined as 'result' somewhere else in your code. You can fix this with the following change

obj2.doOnNameSet = function (name) {

Finally you need to call doOnNameSet before you make the change:

this.setName = function (name) {
        that.doOnNameSet(name);
        that.objName = name;
        MyObj.myObjs[that.objIdx] = name;

    }
Rob Johnstone
  • 1,704
  • 9
  • 14
1

Not sure what it is you're trying to do but the following will set "this" name on creation and save it in a shared names list (not a global variable like your code but a prototype property shared by all instances). When setName is used than the name in the array is set to the changed name:

var MyObj = function (name) {
    this.namesIndex=this.names.length;
    this.names.push(name);
    this.objName = name;
}
MyObj.prototype.names=[];
MyObj.prototype.setName = function (name){
  this.objName=name;
  this.names[this.namesIndex]=name;
}
var o1=new MyObj(1);
var o2=new MyObj(2);
o2.setName("2 changed");

console.log(o2.names);
HMR
  • 37,593
  • 24
  • 91
  • 160
  • Actually, using the prototype is probably a good practice here... but this doesn't talk to my issue with using local/instance variables within a callback. It turns out the instance variable problem was due to a typo. – Smern May 16 '13 at 14:38
  • Good to know you solved it, still not sure what you're after but if you'd like the name change to "trigger an event" you can use the mediator pattern and have other objects or functions listen to this event. http://addyosmani.com/largescalejavascript/ (under mediator pattern) Or simply call this.nameChanged passing the name variable (this.objName is known by the object already). – HMR May 16 '13 at 14:48
  • The above is just an example/poc. I'm working on an ACL XUI generic dialog model that will handle the opening, closing, and other basic functions of all dialogs which will be customized (via some callbacks and properties) for their specific implementation. Each will be something like `var sd = new Dialog("SpecificDialog"); sd.setXuiFile="/foo/bar.xml"; sd.doOnOpen = function() {...}; sd.open()` – Smern May 16 '13 at 14:52
  • I don't think using the prototype for the `names` array is a good idea here. Since there are cases when no instance is created, you always would need to access it explicitly as `MyObj.prototype.names`. The property on the constructor function, `MyObj.names`, was perfectly fine. – Bergi May 16 '13 at 14:55
  • Thanks @Bergi, saves me some time I might have spent looking into it. – Smern May 16 '13 at 14:59
  • MyObj.myObjs will be undefined unless at least one object is created though. You can declare it outside of the function method if you'd like it to be "statically" available. To smerny: if you want to have listeners for certain events it's best to use an event system kind of design. I like mediator as you can add and remove functions from certain events and trigger/publish dummy events for testing. Since you're likely to re use it make it a seporate function. – HMR May 16 '13 at 15:04
  • MyObj.myObjs doesn't matter if there are no objects and it's only needed within MyObj itself, so it's fine if its undefined at that point. There are event listeners within MyObj (Dialog, my real -not example- implementation) already, I'm just adding specific functionality to them for different instances of it. – Smern May 16 '13 at 15:09