0
CurrentUser = function (){}
CurrentUser.prototype = {
    record: undefined
}
CurrentUser.prototype.is = {
    get _parent() {return CurrentUser.prototype},

    get signedIn() {
        return !!this._parent.record
    }
}
User = new CurrentUser
User.record = {name:'Joe'}
User.is.signedIn //false

What I'm trying to do here is create a new User, give him some data (record) and based on that determine that he is signedIn (if his record has data - he is signedIn).

But what happens in reality is that User.is.signedIn getter is accessing CurrentUser.prototype instead of accessing User instance. And as CurrentUser.prototype.record is undefined - User.is.signedIn returns false.

avalanche1
  • 3,154
  • 1
  • 31
  • 38

4 Answers4

2

You seem to be looking for

CurrentUser = function (){}
CurrentUser.prototype = {
    record: undefined,
    get is() {
        var parent = this;
        return {
            get signedIn() {
                return !!parent.record;
            }
        };
    }
};
var user = new CurrentUser
user.record = {name:'Joe'}
user.is.signedIn // true

However I would really recommend to avoid this. Put a simple isSignedIn() method on the prototype and be done with it.

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

I ended up doing this (saw Bergi's answer after I've found a solution)

CurrentUser.prototype = {
    record: undefined,
    get is() { return {
        _parent: this,
        get signedIn() {return !!this._parent.record}
    }}
}

User = new CurrentUser
User.record = {name:'Joe'}
User.is.signedIn //true

We make is getter return an object that holds a reference to CurrentUser.prototype via assigning this to _parent. And this object in turn has its own getters that when accessing _parent gain access to CurrentUser.prototype. Voila!
Btw, if you have many other methods\getters inside the is getter - you can refactor it out into a standalone object and then add it to CurrentUser.prototype via Object.defineProperty().

PS
Many advised against chaining class methods but nobody could say why.
I like my code to be as close to human language as possible - and User.is.signedIn just looks better to me than User.isSignedIn; I also put many other is-related checks inside the is method - so they don't clutter up User namespace.

avalanche1
  • 3,154
  • 1
  • 31
  • 38
  • Why not to use this? Because it's inefficient to create so many objects on the fly. Natural language can be useful on occasion (e.g. in a testing framework) but actually your code should reflect your data structures, not human languages. There is no clutter on the `User` namespace (if there is, all it does mean is that you should split up the class, not introduce punctuation in the method names), and the `.is` property on its own is pretty meaningless. Why not also use `User.is.signed.in` and `User.is.signed.out`? There's a good reason we pull them together. – Bergi Nov 11 '17 at 10:45
  • https://www.google.ru/search?q=code+micro+optimization First 5 articles advise for code readability versus micro optimizations – avalanche1 Nov 11 '17 at 10:50
  • Your solution is the same as @Bergi's, It would have been better if you edited his answer with the code explanation. – doubleOrt Nov 11 '17 at 13:12
  • @avalanche1 I would argue that it's not a *micro* optimisation but a pretty substantial one. Als the readability of the call (`user.is.signed.in` vs `user.isSignedIn`) doesn't differ a lot, while the readability of the property/method declaration takes a decent hit. – Bergi Nov 11 '17 at 13:51
0
 class CurrentUser {
   constructor(){
    this.record = null; 
   }

   get isSignedIn(){
     return !!this.record;
   }

 }

const user = new CurrentUser;
console.log( user.isSignedIn );

user.record = {what:"ever"};
console.log(user.isSignedIn);

Simply put the getter inside the prototype itself and not into a subobject.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • @avalanche1 no you don't want to. Theres no advantage of it, just disadvantages – Jonas Wilms Nov 10 '17 at 16:41
  • "one can easily read the “chainable” code, effortlessly type it, and comfortably understand it." - quote from http://javascriptissexy.com/beautiful-javascript-easily-create-chainable-cascading-methods-for-expressiveness/ – avalanche1 Nov 10 '17 at 16:47
  • @avalanche1 *chaining **methods*** is a different thing – Jonas Wilms Nov 10 '17 at 16:52
0

Nope, no way.

Workaround 1 (using the call method to tweak the this binding rules):

    function Foo() {
    this.x = 10; 
    }
    
    Foo.prototype.bar = {
    baz: function() {
    return this.x; 
    } 
    };
    
    var y = new Foo();
    
    console.log(y["bar"]["baz"].call(y));


Workaround 2 (we add a reference to the instantiated object to Foo.prototype.bar upon instantiation):

function Foo() {
Foo.prototype.bar._this = this;
this.x = 10;
}

Foo.prototype.bar = {
baz: function() {
return this._this.x; 
} 
};

var y = new Foo();

console.log(y["bar"]["baz"]());

See this question, or this one.

doubleOrt
  • 2,407
  • 1
  • 14
  • 34