0

I have this function outside of class in a global scope.

function toggleDescriptor(key, descriptors = 0){
    let _a, _b, _c;
    if (descriptors) [_a, _b, _c] = descriptors;
    else [_a, _b, _c] = ["writable", "enumerable", "configurable"];

    if(this[key] == undefined){
        __defprty(this, key, {[_a] : true, [_b] : true, [_c] : true});
    }
    else{
        __defprty(this, key, {[_a] : false, [_b] : false, [_c] : false});
    }
}

And I call this in a class like this :

class Application {
    constructor(state){
        this.interface = new Interface(this);
        this.interface.state = state;
        toggleDescriptor("interface"); //this not works...
    }

    run(){
        this.interface.state.handle();
    }
}

However, this in toggleDescriptor contains window not Application class. I guess it always set this as window object. But I want to keep this function outside so that other class can use it without redundantly having this function. So, how can I make toggleDescriptor indicating the Application when it's called in the Application class?

Peter
  • 301
  • 4
  • 17
  • 2
    Possible duplicate of [Set "this" variable easily?](https://stackoverflow.com/questions/456967/set-this-variable-easily) – Igor Oct 29 '18 at 16:58

2 Answers2

3

This is where bind, call, apply come in to play.

They will keep this this :)

I think is what you'd need here is toggleDescriptor.call(this, "interface");

There's lots of great resources online, but the very short version is:

bind: toggleDescriptor.bind(this, "interface") will bind this to toggleDescriptor without calling toggleDescriptor - good for situations where you want to reference the function but not call it yet (e.g. window.addEventListener('click', toggleDescriptor.bind(this, "interface"))

call: toggleDescriptor.call(this, "interface") calls toggleDescriptor with this as the current this

apply: toggleDescriptor.apply(this, ["interface"]) is similar to call, but you pass your arguments in as an array

And, as mentioned in the comments above by user @[Alkis Mavridis], you can just use this in the toggleDescriptor function, instead of _obj = () => {this} if you prefer. I have though, sometimes (rarely) found some unexpected behaviour when I mix using arrow functions and bind - I think because arrow functions help manage scope (what this is) in a way that I don't fully understand.

Michael S
  • 726
  • 1
  • 10
  • 23
  • I thought accessing this directly would reference the window. But I guessed wrong:( Thank you for the answer! – Peter Oct 29 '18 at 17:23
  • I think, alternately, you can use `() => toggleDescriptor("interface")`, (instead of `call`) and the arrow function will handle the appropriate `this` for you. However, as I mentioned in my answer I don't fully understand how the arrow function handles `this` (all I know is that makes things work sometimes, lol) – Michael S Oct 29 '18 at 17:26
  • BTY do you think that toggleDescriptor would Ok to be used like that? I’m wondering if anyone use rapper function for just defining property even though they have getter and setter. I doubt that getter and setter dose everything that object.definproperty does. – Peter Oct 29 '18 at 17:37
  • Yh, I also don’t know much about arrow function. Just think it as a short cut and keeping it on the outer context. – Peter Oct 29 '18 at 17:46
2

Have you tried

const myApp = new Application(); //or however you acuire this object
toggleDescriptor.call(myApp, "interface"); //"this" inside toggleDescriptor will be myApp

See here for more details on call function.

I also think that you do not need the _obj variable at all. Just access this.

Alkis Mavridis
  • 1,090
  • 12
  • 28
  • He would need to replace `()=>` with `function()` – slebetman Oct 29 '18 at 17:02
  • I think he does not need the "let _obj" at all... He just needs to directly access this, if he uses call(). the _obj was probably a hack that he tried in order to get "this" working. – Alkis Mavridis Oct 29 '18 at 17:03
  • 1
    Yes I was trying to get ‘this’ in a hacky way. But It seems your answer should work. I cannot test it now though :( Thank you for the reply! – Peter Oct 29 '18 at 17:10
  • It's a bit while after I asked this. But the thing is just using `this` without binding does not work :( It always has a reference to `window` object. – Peter Oct 31 '18 at 03:17
  • 1
    Even when you call your function via .call()? Just for info, the first parameter that you pass to call() will be the "this" inside the called function. So if you write toggleDescriptor.call(123, "interface"), this inside toggleDescriptor will be 123. You can pass literally whatever as "this". In this case, you do not need any boxing/unboxing logic to achieve this. The reason that you get window as this when you call the plain function is that you do not have anything before it, such as something.toggleDescriptor(). This "somehitng" would be this. If nothing existst, window is being used. – Alkis Mavridis Oct 31 '18 at 09:20
  • 1
    yes, It works fine if I use `.call(this,"interface")`. But like the code I edited and as like you just mentioned, `window.this` is being called if the function is not a member of a class or object no matter where it's being called from. Okay, Thank you! I guess I'm just wandering in fearful freedom of javascript :) – Peter Oct 31 '18 at 15:09
  • You are right. It needs a bit of time until you get use to it. After, it is just a second nature. As a rule, "this" is always the thing before your dot operator. If there is no dot operator, "this" will be the global object (window for browsers). Same thing for accessing properties by the way (alert is the same as window.alert). Finally, you can override the "this" behavior when you use call, apply or bind functions. By using them you can force "this" to be whatever you want. – Alkis Mavridis Oct 31 '18 at 16:47