Reputation: 423
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
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
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
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