I've been toying with node.js lately and I ran into a weird behavior about the usage of this in the global scope of a module.
this is bound to module.exports in the global scope:
console.log(this === exports); // -> true
But this is bound to global in a method scope:
(function() { console.log(this === global); })(); // -> true
This also lead to this confusing behavior:
this.Foo = "Weird";
console.log(Foo); // -> throws undefined
(function() { this.Bar = "Weird"; })();
console.log(Bar); // -> "Weird"
I guess that the solution is to never use this in the global scope and explicitly use extends or global instead, but is there a logic behind all this or is it a bug or limitation in node.js?
The "logic" behind that is, that the value of this always depends on how a function is invoked.
In your case, you have a self-executing anonymous function, there, this always references the global object (non strict mode) or undefined (ES5 strict).
If you want to access the "outer" this value, you could either store a reference before executing that function, like
var outerScope = this;
(function() { outerScope.Bar = "Weird"; })();
console.log(Foo); // -> throws undefined
or re- .bind() the functions scope yourself, like
(function() { this.Bar = "Weird"; }).bind(this)();
In working on a simple CommonJS modules implementation, I had to think about what to do with this in the global scope of the module; it's not addressed by the spec.
I also set it up as the exports object at first, because I thought that would be useful, but later found some code I needed to "modulize" that was using this to get a handle to the global object, so I changed this back to the global object to provide as close of an environment to "normal" as possible for module code.
We can only guess at why node is set up the way it is (or ask the author), but my guess is it was done simply because it seemed like a useful idea, similar to the way you can give the module object an exports property in node and have it reflected in the module's actual exports (this behavior also isn't part of the spec, but doesn't go against it either).
As for the part of your question about this referencing global in functions, as the other answers explain, that's just the way this works; it's not a node-specific behavior, it's a weird javascript behavior.
I don't know if this is the exact intention of Node.js team, but I would be surprised if it was not. Consider this example ran in the dev console of a browser (e.g. chrome):
var x = function(){console.log(this)}
a = {}
a.x = x
a.xx = function(){x()}
a.x()
>> Object
a.xx()
>> DOMWindow
x()
>> DOMWindow
As you can see executing a method without specifying its context sets the context to be the global one. In this case the DOMWindow object.
When you are inside a module your context is the module, but executing a method in it without specifying a context with .call or .apply or obj. will use the global context, global, instead of the local one, module.exports.
Related
I am beginner at JavaScript and I am trying to understand the Global window object in JavaScript. So, is it ok if I just imagine that any code that I write in the console like var text = "Hello"; console.log(text) is put inside window object like this Window{var text = "Hello"; console.log(this.text)} with this referencing window object. Is it ok if I consider it like that or it is not correct? Thank you
Is not pretty safe to make assumptions about the global object or the default this context, as it can vary from one javascript runtime to another, and some features like the strict mode also change this behavior.
Keep in mind that javascript not only runs in the browser -and global in node.js for instance does not work as in the browser-, and that there are a lot of different browser implementations out there.
Also, while var does write to global by default in some environments, const and let doesn't.
In node, functions called freely with no previous reference won't call them from global, but will fail instead. This heavily affects also front-end code since much of javascript for the browser nowadays is pre-compiled in a node environment via webpack etc.
So, succintly: is usually difficult to assume things about global, window and default this bindings and get them right. It is probably safer to assume that you don't have a default global object available and always refer window explicitly, as:
config.js
window.config = {foo: 'bar'}
window.someGlobalFunction = function() {...}
user.js
// do:
const elen = new User(window.config);
window.someGlobalFunction();
// don't
const elen = new User(config);
someGlobalFunction();
"is it ok to use the global object?"
Of course it is. If you know about the pitfalls and use the global object by purpose and not by accident.
(function() {
name = 12; // Note: This *is* a global variable
console.log(typeof name); // string. WTF
})();
The pitfalls are:
1) All undeclared (!) variables, and global variables declared with var automatically get part of the global object. That is bad, as that happens without any good reason, so you should avoid that (use strict mode, and let and const).
2) All scripts you run do have the same global object, so properties can collide. Thats what happens in the example above, name collides with the global window.name getter / setter pair that casts the number to a string. So if you set properties in the global object, make sure that the name is only used by you, and not by others (the browser, libraries, other codepieces you wrote ...)
If you know about these pitfalls and avoid them, you can and should use the global object by purpose, if, and only if, you plan to share a certain function / variable between different scripts on the page, so if it should really be globally accessible.
let private = 1;
window.shared = 2;
Yes, any function or variable can be accessed from the window object example:
var foo = "foobar";
foo === window.foo; // Returns: true
function greeting() {
console.log("Hi!");
}
window.greeting(); // It is the same as the normal invoking: greeting();
I wrote a simple object destructuring function that declares variables in given scope through assigning properties to the scope object
Can I use this function in production and if not what are the pitfalls of using such a function?
function destructure(obj, scope) {
scope = scope || this;
Object.keys(obj).forEach(key => {
if (scope[key] === undefined) {
scope[key] = obj[key];
} else {
throw new Error(key + ' variable is already declared.');
}
});
}
var o = {
one: 1,
two: 2,
three: 3,
four: 4
};
destructure(o);
console.log(one); // 1
console.log(two); // 2
console.log(three); // 3
console.log(four); // 4
Are there any pitfalls of declaring variables through this keyword?
Yes: It doesn't really work.
There might be a misunderstanding about how this and scope work.
About scope
You can only declare variables by adding a property to an object is when the environment is backed by an actual object. This is only the case for two situations:
Global scope, which is (partially) backed by the global object (window in browsers)
The with statement
Examples:
// Global scope
window.foo = 42;
console.log(foo);
// with statement
var obj = {};
with (obj) {
obj.bar = 21;
console.log(bar);
}
Since you mention that you are using Node, I want to emphasize that function scope and module (which are just functions in Node anyway atm) scope are not backed by objects, thus there is no way to dynamically declare variables.
About this
The value of this depends on how a function is called. It could basically refer to any value.
Now, calling a(n unbound) function the "normal way", i.e. as f(), has this arbitrary behavior that this will refer to the global object, e.g. window in browsers. Of course being the global scope, any variable added there is accessible every else, but that's not necessarily a good thing.
So, while your solution may seem work, it really only works coincidentally when you run your code in global scope.
About strict mode
Strict mode changes some of the behaviors that were deemed incorrect or dangerous. Most importantly it changes how this inside normal function calls (f()) behaves. Instead of referring to the global object, it will simply be undefined. Thus if your function was declared in strict mode, your code would actually throw an error.
The with statement is also forbidden in strict mode.
With ES2016, new constructs such as classes and modules are strict by default, so you can see that that's the direction where the language is developing and you should not rely on behavior that doesn't work in strict mode.
declares variables in given scope through assigning properties to the scope object
No, that's not how a variable declaration works. Also, the only "scope object" that is available to JS - ignoring the with statement - is the global object (which you happened to use in your proof of concept), and you don't want to mess with that.
Can I use this function in production?
Absolutely not.
If you're sold on the idea, you probably can hack it with eval, but really you just should use real destructuring like everyone else:
var {one, two, three, four} = o;
This is the programmatic approach to:
this["one"] = 1
this["two"] = 2
this["three"] = 3
this["four"] = 4
While this theoretically fine, you may run into trouble by relying on this. It would be better to directly specify global, so that you don't run into trouble with .bind() and friends.
function destructure(obj, scope) {
scope = scope; // Remove the this
Object.assign(scope, obj);
}
destructure(o, global) // Explicitly declare global scope
As for memory leaks, performance, etc. (which I feel this question is more about), you have to remember that everything that you assign to global never gets Garbage Collected. You should really only add functions to global, and that's only if you really have to.
Why not use just Object.assign?
function destructure(obj, scope) {
scope = scope || this;
Object.assign(scope, obj);
}
When I ran console.log(this) in node it returns empty object
console.log(this) // return { }
But when I used IIFE in node
(function printThisObject(){
console.log(this); // return the global object
})();
Can someone explain this to me ?
Because NodeJS runs your code in a module, and this references the object it creates for your module's exports (which is also the exports property on the module variable it provides you). (As they don't really mention that in the module documentation, I suspect using it is probably not a great idea — use exports instead.)
But your code calling the IIFE calls it with this referring to the global object, because in loose (non-strict) mode, calling a normal function not through an object property calls it with this set to the global object. (In strict mode, this would be undefined there.)
I have a custom binding handler and want to modify it to IIFE. I have been reading on internet about IIFE but could not able how to change my custom handle into IIFE. So how can I change following binding handler into IIFE
define(['knockout', 'jquery'], function(ko, $) {
ko.bindingHandlers.er = {
init: function(el, va) {
console.log($(el).find('.n').text());
$(el).find('.edit').hide().end().find('.detail').show();
$(el).find('.ebtn').on('click', function() {
$(el).find('.edit, .detail').toggle();
});
$(el).find('.ubtn').on('click', function() {
$(el).find('.edit, .detail').toggle();
});
}
};
});
It's quite unclear what you're asking here :(.
Immediately-Invoked Function Expression (IIFE) is an anonymous function that executes immediately. In your case, if you can't be more specific, it's really hard to guess where do you want to place the iife.
I normally put my whole code into an iife in order to take advantage of the function scope and use it as a closure, thus minimising the global object pollution and it generally looks like this :
(function(global,undefined){
// the code goes here
// global === window
})(this);
In your case, you can do the same :
(function(global,undefined){
define(['knockout','jquery'],function(ko,$){
// ...
});
})(this);
Update :
Here are some explanations for those who need them.
The following pattern is used:
to prevent global variable leaks such as var foo = bar; (after which window.foo === foo)
to prevent the undefined value to get overwritten. Let's say you want to check if something is undefined. you have 2 ways to do it :
typeof foo === 'undefined'
foo === undefined
the second one is shorter but is often not preffered because one can simple overwrite the undefined variable's value : undefined = 13;.
By not proviiding an actual value for the undefined argument in the iife, you're actually resetting the undefined variable to undefined.
to generalize the global object
some say that the access to the global variable is faster than to the window object due to the fact that the js engine first looks into the lower scope
if you're running the code in a different environment than the browser and you have calls to the window object, your code might break because the global object is different from environment to ennvironment (adobe pdfs, node js, rhino ... ). This can be solved by remaping the global object to a variable (in our case global), by reffering it to the this object from the global scope
While looking at code on github, I found the following:
(function() {
}).call(this);
This is clearly a self invoking anonymous function. But why is it written this way? I'm used to seeing the canonical variant (function() {})().
Is there any particular advantage to using .call(this) for a self invoking anonymous function?
Edit: It looks like some commonjs environments set this to a non-global value at the top level of a module. Which ones, and what do they set this to that you might want to preserve?
By default, invoking a function like (function(){/*...*/})() will set the value of this in the function to window (in a browser) irrespective of whatever the value of this may be in the enclosing context where the function was created.
Using call allows you to manually set the value of this to whatever you want. In this case, it is setting it to whatever the value of this is in the enclosing context.
Take this example:
var obj = {
foo:'bar'
};
(function() {
alert( this.foo ); // "bar"
}).call( obj );
http://jsfiddle.net/LWFAp/
You can see that we were able to manually set the value of this to the object referenced by the obj variable.
.call(this) (was actually just () until I changed it) ensures your top level this to be consistent through strict mode, --bare option and/or the running environment (where top level this doesn't point to global object).
By using:
> (function() {
> ...
> }).call(this);`
then this in the scope of the code (probaby the global object) is set as the function's this object. As far as I can tell, it's equivalent to:
(function(global) {
// global references the object passed in as *this*
// probably the global object
})(this);
In a browser, usually window is (or behaves as if it is) an alias for the global object.
C={
descript: "I'm C!<br>",
F: function() {
//set this to the caller context's 'this'
(function() {
document.write(this.descript);
}).call(this);
//set this to 'window' or 'undefined' depend the mode
(function() {
document.write(this.descript);
})();
//member function's 'this' is the object self
document.write(this.descript);
}
}
window.descript="I'm window!<br>";
C.F();
(function() {}).call(this); could set the this in the anonymous to the caller context this, in above is C.(function() {})(); will set this to window or undefined depend the mode.
Self-invoking function are useful to execute its content immediately when the script is loaded. This is convenient to initialize global scope elements.