LostInCyberSpace
LostInCyberSpace

Reputation: 423

Function call - this == Window and not undefined

Ok guys. I have this basic code which returns a jQuery-esque function called Q.

q=new function(){
    var u,q;
    q=function Q(slf){
        console.log('slf    ⇒',slf);
        ex=q_ex.call(slf);
        console.log('ex ⇒',ex);
    };
    return q;
    function q_ex(){
        console.log('this   ⇒',this);
        return (this!==u && this!==null);
    };
};

If I was to call q();. The resulting console display would be:

slf     ⇒ undefined
this    ⇒ Window {external: Object, chrome: Object, document: document, speechSynthesis: SpeechSynthesis, caches: CacheStorage…}
ex      ⇒ true

Now the astute among you will realize that I require this to be undefined and not Window!

Am I missing something here, or does this default to Window if it has the value null or undefined?

Upvotes: 2

Views: 707

Answers (3)

LostInCyberSpace
LostInCyberSpace

Reputation: 423

Ok guys for anyone watching, I have found my answer. But first, I will clarify a little on the code...


q=new function(){

The reason for using new function is that I had never really used/practiced with self-executing - anonymous functions before and this was the only way I knew how!


q(x) === function Q(x){...}

q is purposefully global!


u is shorthand for undefined, var u === var u=undefined..


It's true I can write this !== undefined, but q_ex() will be used, in future, for multiple object types, from strings to numbers etc...


This level of 'same-name variables' is quite acceptable for it's purpose!


q=function Q(slf){

The reason behind naming the function is purely astetic, and is generally removed once coding is tested.


ex=q_ex.call(slf);
// ex is not declared with var so this will create a global variable, bad bad bad

Well spotted! Though this is a some-what simplified version of the code that is causing the problem, as such ex doesn't need declaring here for the problem to arise.


One last thing... I understand there seems to be no reason for the outer casing of the 'Q' function, but if you look closer... My only global variable is 'q'! If not for the outer casing: u, q, q_ex and any future object would be global and this is NOT the requisite.


Ok then, for my answer...

'use strict';

Function.prototype.call
Strict mode


The solution to my woes...

var q=(function(){
    var u,q;
    q=function Q(slf){
        console.log('slf    ⇒',slf);
        var ex=q_ex.call(slf);
        console.log('ex ⇒',ex);
    };
    return q;
    function q_ex(){
        'use strict';
        console.log('this   ⇒',this);
        return (this!==u && this!==null);
    };
})();

q();

slf     ⇒ undefined
this    ⇒ undefined
ex      ⇒ false

Thanks for all your help guys, I can now move on with the bigger picture!

I will try to pull myself away from using new function and replace it with (function(){})().

Hope this helps.

Upvotes: 0

Taavi
Taavi

Reputation: 165

Yes, this tends to default to Window, hence why using new is dangerous. The answer to why you get undefined when printing slf needs a bit more code samples to explain properly, I'll do that when I get home as I'm on my phone at the moment

Edits: First off I made a bunch of comments to your original code to highlight some problems I found

// Dont use new, its not needed here, use closures
// Also q is not declared here so it will become a global variable, bad bad bad
q=new function(){
    // Both declarations are unneeded as you can just return q straight away
    // and u never gets a value assigned to it so why declare it?
    // If you want to compare with undefined then just write this !== undefined
    // Also, refrain from using the same names in nested structures if you value your sanity
    var u,q;

    // Use either q = function(...){...} or function Q(...){...}
    q=function Q(slf){
        console.log('slf    ⇒',slf);
        // ex is not declared with var so this will create a global variable, bad bad bad
        ex=q_ex.call(slf);
        console.log('ex ⇒',ex);
    };

    // No need to write the function and then return it.
    // Use return(function(...){...}) instead.
    return q;

    // Just a personal preference but I'd put this before the return
    // statement to make it more readable and also use q_ex = function(...){...}
    function q_ex(){
        console.log('this   ⇒',this);
        return (this!==u && this!==null);
    };
};

Below is how I'd write it

var q = (function(){ // Closure
  var q_ex; // Declarations

  q_ex = function() { // Internal function
    console.log('this   ⇒', this);
    return (this!==undefined && this!==null);
  };

  return(function(slf) { // Returned function
    console.log('slf    ⇒', slf);
    var ex = q_ex.call(slf);
    console.log('ex ⇒', ex);
  });
})(); // Call in the end to get returned function

q() // Returns the same as yours did
q(5) // A numeric object with the value of 5 is now set as 'this'

The way you wrote yours seems to work exactly the same as mine but I cant exactly understand it. Could just be that I'm too deep in the closure religion.

Anyhow, the problem that you had stems from the fact that you run q(), with no parameters. If you ran q(5) you'd see that this becomes 5. This is because if the this parameter is not provided then it defaults to Window. This is the reason new is often dangerous. Say you wrote an object that uses this inside it to store values and thus requires to be created with new. Now if someone created it and forgot to use new you end up polluting the global namespace by attaching all your variables to the Window object (Window === global in browser). A good way around that risk is to write your objects using closures, that way, how ever they are created they still work the same. You can still access the variables you want from your closing function by just using their name or if you want to group them up somehow just create a single "master" object in your closing function and add everything else into that.

Also, 'use strict' basically disallows some bad practices. Read up on it and use it, makes life easier. Also, use jsHint or any other JavaScript quality checker to help you keep your code clean.

Hope that helped.

Upvotes: 1

TbWill4321
TbWill4321

Reputation: 8666

I was able to get something working by changing some code around:

var q = (function() {
  'use strict';
  
  var u;
  
  function q_ex() {
    console.log('this   ⇒',this);
    return (this!==u && this!==null);
  }
  
  return function Q(slf) {
    console.log('slf    ⇒',slf);
    var ex = q_ex.call(slf);
    console.log('ex ⇒',ex);
  }
})();

q({ foo: 'bar' });
q();

Upvotes: 1

Related Questions