Jope
Jope

Reputation: 47

JavaScript object with internally referenced properties

I'm trying to create a global object with a series of 'slave' properties that are derived from the value of one 'master' property in the same object - something like:

var x = 5;
var myObj = {
    master : 17,
    slave1 : this.master + x,
    slave2 : (this.master / 2.2) + 1,
    slave3 : Math.floor(this.slave2)
    //etc.
};

I realize this is horribly wrong as is, but the concept is there. What I'd like to do is have all these 'slave' properties updated as myObj.master is updated. What's the cleanest way to do this? Does each slave value need to be explicitly set by some event handler that fires when myObj.master is changed...?

Upvotes: 0

Views: 762

Answers (6)

Randy the Dev
Randy the Dev

Reputation: 26730

If you are not targeting Internet Explorer users, try using getters and setters.

var x = 5;
function myObj()
{
    this.master = 17;
    this.getSlave1 = function() {return this.master + x;},
    this.getSlave2 = function() {return (this.master / 2.2) + 1;},
    this.getSlave3 = function() {return Math.floor(this.getSlave2());}
}

myObj.prototype = {
    get slave1() {
        return this.getSlave1();
    },
    get slave2() {
        return this.getSlave2();
    },
    get slave3() {
        return this.getSlave3();
    }
};   

In action:

window.onload = function()
{
    o = new myObj();
    document.write("Master: "+o.master+"</br>");
    document.write("Slave 1: "+o.slave1+"</br>");
    document.write("Slave 2: "+o.slave2+"</br>");
    document.write("Slave 3: "+o.slave3+"</br></br>");
    o.master = 42;
    document.write("Master: "+o.master+"</br>");
    document.write("Slave 1: "+o.slave1+"</br>");
    document.write("Slave 2: "+o.slave2+"</br>");
    document.write("Slave 3: "+o.slave3+"</br>");
}

produces the output:

Master: 17
Slave 1: 22
Slave 2: 8.727272727272727
Slave 3: 8

Master: 42
Slave 1: 47
Slave 2: 20.09090909090909
Slave 3: 20

This code will never work in any version of Internet Explorer, past, present or future. However, it the slave values can still be accessed using the getSlaveX() functions.

Upvotes: 4

Casey Chu
Casey Chu

Reputation: 25463

You could simulate getters using valueOf and toString, but it's a little unwieldy.

function makeValueOf(fn) {
    return {
        valueOf: fn,
        toString: fn
    };
}

function makeObj(master, x) {
    var self = {};

    self.master = master;
    self.slave1 = makeValueOf(function () { return self.master + x; });
    self.slave2 = makeValueOf(function () { return self.master / 2.2 + 1; });
    self.slave3 = makeValueOf(function () { return Math.floor(self.slave2); });

    return self;
}

var myObj = makeObj(6, 2);
alert(myObj.slave1 + 5); // 13

Upvotes: 0

ilovefigs
ilovefigs

Reputation: 776

You don't want to expose the slaves and allow them to be set since that would erase their link to master. So they should be private.

var x = 5;
var MyObject = function() {
    var master = 17;
    this.setMaster = function(newMaster) {
        master = newMaster;
    };
    this.getMaster = function() {
        return master;
    };
    var slaves = {
        slave1 : function() { return master + x },
        slave2 : function() { return (master / 2.2) + 1 },
        slave3 : function() { return Math.floor(this.slave2()) }
    };
    this.getSlave = function(slave) {
        return slaves[slave]();
    };
};

var o = new MyObject();
o.getSlave("slave1"); // 22
o.getSlave("slave3"); // 8
o.setMaster(3);
o.getSlave("slave1"); // 8
o.getSlave("slave3"); // 2

Upvotes: 0

Christian C. Salvad&#243;
Christian C. Salvad&#243;

Reputation: 827694

Another option can be to use a constructor function to build your object, e.g:

var x = 5;

function MyObj(master) {
  this.master = master;
  this.slave1 = this.master + x,
  this.slave2 = (this.master / 2.2) + 1,
  this.slave3 = Math.floor(this.slave2)
}

var myObj = new MyObj(17);

In the above example I use a master argument and a reference to the x global property, if you don't have x available in that scope, you could provide also an argument for it.

You can also build your object in several steps:

var myObj = {
    master : 17
}, x = 5;

myObj.slave1 = myObj.master + x;
myObj.slave2 = (myObj.master / 2.2) + 1;
myObj.slave3 = Math.floor(myObj.slave2);

Upvotes: 1

Matti Virkkunen
Matti Virkkunen

Reputation: 65156

David's answer is one option, but if you want to cache the results instead of recalculating them every time, do it the other way round:

var x = 5;
var myObj = {
    setMaster: function(val) {
        this.master = val;
        this.slave1 = this.master + x;
        // ...
    }
};

myObj.setMaster(17);

Upvotes: 2

Quentin
Quentin

Reputation: 943981

You would probably be best off using functions.

var x = 5;
var myObj = {
    master : 17,
    slave1 : function() { return this.master + x },
    slave2 : function() { return (this.master / 2.2) + 1 },
    slave3 : function() { return Math.floor(this.slave2()); }
    //etc.
};

var example = myObj.slave3();

Upvotes: 2

Related Questions