10

Is this possible or am I barking up the wrong tree here?

var data = 'one';
function fnc(){
    this.out = function(){
        return data;
    }
}
var instance = new fnc();

alert(instance.out);
data = 'two';
alert(instance.out);

// I know that this would achieve that, but that's not what I would like to know.

alert(instance.out());
data = 'two';
alert(instance.out());

Update:

The object which fnc is supposed to represent is actually a Sarissa dom document. Here is a more elaborate version of fnc(), dom_doc(). The accepted answer below has been integrated into the function below.

function get_doc(dom_node) {
    var doc;
    if (navigator.userAgent.indexOf("MSIE") >= 0) {
        doc = new ActiveXObject("Msxml2.DOMDocument.3.0");
        doc.loadXML(document.getElementById(dom_node).text);
    }
    else {
        doc = Sarissa.getDomDocument();
        doc = (new DOMParser()).parseFromString(document.getElementById(dom_node).textContent, "text/xml");
        // runs XSLTProcessor in modern browsers as if it was trasformNode
        doc.transformNode = function (stylesheet) {
            var processor = new XSLTProcessor();
            processor.importStylesheet(stylesheet);
            return new XMLSerializer().serializeToString(processor.transformToDocument(this));
        }

        // allows modern browsers to extract xml the way the legacy IEs did
        var getXML = {};
        getXML.toString = function(){
            return new XMLSerializer().serializeToString(doc);
        };
        doc.xml = getXML;
    }
    return doc;
}

Demo: JSFIDDLE

UdayKiran Pulipati
  • 6,579
  • 7
  • 67
  • 92
Serhiy
  • 2,505
  • 3
  • 33
  • 49
  • 1
    Yes, it is possible. It's called *getter*s. – Bergi May 09 '13 at 22:43
  • Not the best dupe, but I found [Javascript getters and setters for dummies?](http://stackoverflow.com/questions/812961/javascript-getters-and-setters-for-dummies) – Bergi May 09 '13 at 22:44
  • @Bergi yep, thank you, I was unaware of getters – Serhiy May 09 '13 at 22:45
  • @Bergi Do dupes get removed? If so, idk if that's a great idea, without knowing the word "getter" I spent far too much time Googling around without much luck. – Serhiy May 09 '13 at 22:48
  • 1
    @Serhiy Usually, questions closed as duplicates are kept as signposts for people coming to them via Google. – bfavaretto May 09 '13 at 22:53
  • @Jari I'm working with a legacy app, XML data islands, and Sarissa. I set up as just a quick sample so as to not get into... doc.xml = new XMLSerializer().serializeToString(doc); – Serhiy May 09 '13 at 22:55
  • @bfavaretto I'm not sure this is still a duplicate of getters. The non getter solution worked best for me. – Serhiy May 09 '13 at 23:28
  • @Serhiy There is nothing in the code at the jsfiddle mentioning `dom_doc()` or `fnc`. Also, please post the code here so the context is clearer. Where is the code that is using the result of `get_doc()?` – Ruan Mendes May 09 '13 at 23:45
  • @Serhiy I have a feeling that bfavaretoo's suggestion didn't work because you're using IE8? In IE 8 you can only add getters to DOM nodes http://kangax.github.io/es5-compat-table/#Object.defineProperty – Ruan Mendes May 09 '13 at 23:48
  • @JuanMendes No actually Chrome v26, for IE I'm just using ActiveXObject("Msxml2.DOMDocument.3.0"), also update the question from JS fiddle stuff – Serhiy May 09 '13 at 23:52
  • 1
    @Serhiy Now that I'm seeing your actual code, why not just `doc.xml = new XMLSerializer().serializeToString(doc);`? – bfavaretto May 10 '13 at 01:46
  • 1
    @bfavaretto The xml is manipulated via setAttribute and such. This is what I had first tried and noticed that the changes are not reflected, only the original xml string is returned. – Serhiy May 10 '13 at 02:51
  • @Serhiy Can you post the code that is calling `get_doc(dom_node)`? I still don't understand why you can't use getters – Ruan Mendes May 10 '13 at 03:27
  • @JuanMendes what do I care I got my answer... haha j/k, here's the fiddle to illustrate what's going on. I couldn't get getters nor prototype to work in this context http://jsfiddle.net/Sh6du/1/ – Serhiy May 10 '13 at 06:24
  • @bfavaretto I made an example of my last comment to you in this fiddle, just uncomment line 23 http://jsfiddle.net/Sh6du/1/ – Serhiy May 10 '13 at 06:25

3 Answers3

9

You seem to be talking about a getter method. If that's what you mean, you can use this:

var data = 'one';
function fnc(){
    Object.defineProperty(this, 'out', {
        get : function(){ return data; }
    });
}
var instance = new fnc();

alert(instance.out);

http://jsfiddle.net/zztYd/1

This is not compatible with older browsers (see the compatibility table at the bottom of this page).

Also, it's a little weird to use a getter to fetch a global variable. Usually you use that to get the value of a private variable on your instance object, and in that case you could only modify it with a corresponding setter.

bfavaretto
  • 71,580
  • 16
  • 111
  • 150
4

The alternative to bfavaretto's solution is to use the literal syntax for an object. Has almost the same level of support as Object.defineProperty().

Also, you should call Object.defineProperty only once, not with every instantiation.

I'm also going to provide a more realistic example, that is, the property gets calculated, instead of just referencing a global.

http://jsfiddle.net/mendesjuan/zztYd/3/

function Pair(a,b){
    this.a = a;
    this.b = b;
}

Pair.prototype = {
    get sum(){ 
        return this.a + this.b; 
    }
};

var pair = new Pair(1,2);
alert(pair.sum);
pair.a = 5;
alert(pair.sum);

The benefit of this is that you can change the implementation to be storage instead of calculation and you won't have to change how it's called.

function Pair(a,b){
    this.a = a;
    this.b = b;
    this.sum = a + b;
}

Pair.prototype = {
    setA: function(a){ 
        this.a  = a;
        this.sum = this.a + this.b; 
    }
};

var pair = new Pair(1,2);
alert(pair.sum);
pair.setA(5);
alert(pair.sum);

Notice that you do have to now call setA so that sum can be recalculated. Or you could use a setter.

function Pair(a,b){
    this.a = a;
    this.b = b;
    this.sum = a + b;
}

Pair.prototype = {
    set a(a) {
        this.a  = a;
        this.sum = this.a + this.b; 
    }
};

var pair = new Pair(1,2);
alert(pair.sum);
pair.a = 5;
alert(pair.sum);
Community
  • 1
  • 1
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • +1 for an actually useful example of a getter - and for that comment with the page number on the ecma spec :) Just one thing: this syntax is clearly simpler, but Object.defineProperty doesn't necessarily have to be called with every instantiation; it could be used on the constructor's prototype just like you did here. – bfavaretto May 10 '13 at 02:00
  • 1
    @bfavaretto Fixed the text so it doesn't imply that `Object.defineProperty` doesn't necessarily have to be called with every instantiation – Ruan Mendes May 10 '13 at 13:28
3

jsFiddle Demo

These types of workarounds really bypass convention though. It should not be that much of a hamper to simply use () on your functions. Using () is expected, readable, best practice, and the industry standard.

Not sure why smartcaveman decided to remove his answer of using toString but that is a viable approach although somewhat hackish.

var data = 'one';
function fnc(){
 var getData = {};
 getData.toString = function(){
   return data;
 };
 this.out = getData;
}

var instance = new fnc();

alert(instance.out);//one
data = 'two';
alert(instance.out);//two

var s = instance.out;
alert(s);//two
Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • I hope it wasn't my fault for going nope when his solution didn't work. This however is exactly what I need, and changed the answer to this one, as the other two methods didn't quite work within the context of the actual situation. – Serhiy May 09 '13 at 23:17
  • smartcaveman's answer only works if what you are using is automatically calling `toString()` for you, like `alert`. It's not what the OP is asking – Ruan Mendes May 09 '13 at 23:19
  • I disagree that the standard is using a method. JavaScript always had property getter/setters, it's just we (JS developers) couldn't create our own. Simplest example is a setter like `document.cookie="..."`, it doesn't just set a property, it calls a method under the hood. – Ruan Mendes May 09 '13 at 23:26
  • OP here, here is the context in which this is the only answer that worked, and as to why I accepted this answer over the others, in case anyone is interested. http://jsfiddle.net/kqFP5/ – Serhiy May 09 '13 at 23:27
  • @Serhiy You may be the OP, but this is not the answer to the question you asked, it's the workaround for the problem you're facing, two different things. This question should be useful to everyone, not just the problem you were trying to solve. That's the spirit of Stack Overflow. It's also not clear what your jsfiddle is proving – Ruan Mendes May 09 '13 at 23:30
  • @JuanMendes It's the answer with the code that most resembles the language in the title of the question. It's also the one that worked for my use case. It may be a work around, but it does exactly what I thought I asked. I didn't ask for a getter or prototype lesson... I know I'm a dummy... :P – Serhiy May 09 '13 at 23:33
  • @Serhiy I'm just explaining to you that the spirit of SO is not to fix your problem specifically, but to answer the question in a way that is useful to others. Now others may think this is the right solution, but it's only right for your case – Ruan Mendes May 09 '13 at 23:35
  • 2
    @JuanMendes - I believe you are misrepresenting the "spirit" when you are making these statements. Please read this post on meta: http://meta.stackexchange.com/a/5235/178816 and make a post on meta with regards to your issue on the "spirit of SO" before you take on the full mantle of what stackoverflow is or isn't or begin dictating what the OP should or should not accept. Note the part which says: "The bottom line is that you should accept the answer that you found to be the most helpful to you, personally." – Travis J May 09 '13 at 23:37
  • @JuanMendes good call, I'll edit the question to provide the context as well. I just figured boiling the problem down made it easier to digest. I may have boiled it down too much. – Serhiy May 09 '13 at 23:38
  • @TravisJ Serhiy understood what I meant: the question and answer are out of balance, fixing the question is the right thing to do. From the same post "Make sure that besides working for you, the answer is really good practice. Sometimes after the answer gets accepted, another comes in, uncovering the fact that previous one was in fact a bad hack." This isn't a bad hack, but it's still a workaround. Without context from the OP, it's unclear why they can't just use getters – Ruan Mendes May 09 '13 at 23:39