If I use strict mode the following code does not work. It fails on the this.bar = 'foobar'; line. Why is this so? How can I make an object property in strict mode?
<html>
<body>
<script>
"use strict";
var foo = (function () {
this.bar = 'foobar';
return this;
}());
alert(foo.bar);
</script>
</body>
</html>
edit:
Thanks to James Allardice for pointing out the problem.
I was erroneously thinking that the self executing function was creating an object but it isn't. I needed to do one of the following instead:
"use strict";
var foo = new function () {
this.bar = 'foobar';
};
alert(foo.bar);
or (this one JSLint likes better)
"use strict";
var foo = (function () {
var obj = {};
obj.bar = 'foobar';
return obj;
}());
alert(foo.bar);
In strict mode, this will not refer to the window. In your example, removing the strict mode directive will cause this to refer to window.
Since in strict mode, this in your example is undefined, you get an error. That's because you can't set properties on something that is undefined.
From MDN (emphasis on the parts relevant to your situation):
First, the value passed as this to a function in strict mode isn't
boxed into an object. For a normal function, this is always an object:
the provided object if called with an object-valued this; the value,
boxed, if called with a Boolean, string, or number this; or the global
object if called with an undefined or null this...
Automatic boxing is a performance cost, but exposing the global object in browsers is a security hazard, because the global object provides access to functionality "secure" JavaScript environments must restrict. Thus for a strict mode function, the specified this is used unchanged
Related
I have a piece of JavaScript code:
var o = {
name: "aaaa",
f: function () {
console.log(this);
console.log(this.name);
}
};
var m1 = o.f;
m1();
console.log(window.non_existent_property);
As I understand, the property name shouldn't matter too much. If I change the name of property name of object o to something like a everywhere, I should have the same result. However, this is not the case. See below image. I'm running the code on FireFox 75.0. Could anyone explain what is happening here please?
The issue is not with the property of o in the object - the property name there is actually completely irrelevant, because you're losing the calling context when you assign the function to a standalone variable:
var m1 = o.f;
m1();
The above code means that m1 is called with no calling context, which means that this inside the m1 function will either be the global object (in sloppy mode), or undefined (in strict mode).
Since you're in sloppy mode, this.name references window.name, which is a reserved property that always exists on window and must always be a string. It defaults to the empty string.
Here's another snippet demonstrating the same sort of issue:
var foo = 'foo';
var o = {
f: function () {
console.log(this.foo);
}
};
var m1 = o.f;
m1();
foo is a property of the window, since a global variable named foo was declared with var, so this.foo references window.foo.
After using 'use strict' statement in my js file it does not allow me to use javascript this after ist level
'use strict'
module.exports = {
a: function() {
var self = this //works fine upto this level
var name = 'atul';
function b() {
this.name = name; //gives me error as can not set property name of undefined
}
}
}
this and Javascript:
this references to the global object by default. ("window" on browsers)
this references to the calling object example:
var x = {name: "x", alphabet: function(){return this;}};
x.alphabet(); // in this line, x is the calling object
this will show you the object itself.
So when you do:
...
a: function() {
var self = this //works fine upto this level
var name = 'atul';
function b() {
this.name = name; //use strict is trying to tell you something
}// you won't have a calling object for this function.
}
...
use-strict says: this function is not even an object property or method. Since it's not a method it's this will point to the global object and lead to error prone development.
If you wish to use your code in this particular way.
module.exports = {
a: {
name: 'atul';
b: function (name) {
this.name = name; // now you have a as the property which can be called by an object a.
//To be used as OBJECT.a.b();
}
};
};
this is undefined as it is not autoboxed to an object in strict mode.
First, the value passed as this to a function in strict mode is not forced into being an object (a.k.a. "boxed"). For a normal function, this is always an object: either the provided object if called with an object-valued this; the value, boxed, if called with a Boolean, string, or number this; or the global object if called with an undefined or null this. (Use call, apply, or bind to specify a particular this.) Not only is automatic boxing a performance cost, but exposing the global object in browsers is a security hazard, because the global object provides access to functionality that "secure" JavaScript environments must restrict. Thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this will be undefined
Read more at MDN
Why does this function run and not initialize global XYZ with the return value?
"use strict";
XYZ = (function(){
var obj = {'a':1,'b':2,'c':3};
console.log("about to return:");
console.log(obj);
return obj;
})();
console.log(XYZ); // shows undefined
jsfiddle
What seems odd is that the first two console.log return sensible output, and then Chrome throws Uncaught ReferenceError: XYZ is not defined
When using window.XYZ explicitly, this works fine:
"use strict";
window.XYZ = (function(){
var obj = {'a':1,'b':2,'c':3};
console.log("about to return:");
console.log(obj);
return obj;
})();
console.log(XYZ); // shows a:1, b:2, c:3
Either will work if "use strict;" is removed. But don't do that -- keep reading.
Strict mode prevents XYZ = ... from becoming a new global variable.
John Resig explains in his overview of strict mode:
An attempt to assign foo = "bar"; where foo hasn’t been defined will
fail. Previously it would assign the value to the foo property of the
global object (e.g. window.foo), now it just throws an exception. This
is definitely going to catch some annoying bugs.
The code with window.XYZ = IIFE() works in strict mode because here the assignment is to a property of an existing object, window.
Using window.XYZ might be "good enough" for many applications, but you may encounter a platform (such as Meteor) where using window.XYZ "short circuits" dependency management in the package manager. In such a case, strict mode can be enabed inside the IIFE but not at the page level.
To use strict mode with a singleton IIFE assigned to a new global, only turn on strict mode inside the IIFE on its first line.
XYZ = (function() {
"use strict";
var obj = {
'a': 1,
'b': 2,
'c': 3
};
console.log("about to return:");
console.log(obj);
return obj;
})();
console.log(XYZ);
Warning: "use strict;" as the 2nd line does not enable strict mode:
Don't do this:
XYZ = 0;
"use strict";
XYZ = ...
You can test whether strict mode is enabled by looking at this inside a function where this is not explicitly set. When strict mode is true, this will be null when unset, but when strict mode is false, this will be window.
See also: What does "use strict" do in JavaScript, and what is the reasoning behind it?
I have an object and I need it to run the content of the page. I need to wrap it with strict mode. I tried to return it to use in global scope, but it didn't solve my issue.
(function($) {
"use strict";
var MyObj = {
// some code here.
}
return MyObj;
})(jQuery);
console.log(MyObj); // <= undefined
How can I access to this object from anywhere of my project?
What you have there is an immediately-invoked function expression (IIFE) which is specifically designed to keep things separate from the external scope. Since you're returning MyObj, all you have to do to make it available in the global scope is this:
var MyObj = (function($) {
"use strict";
var MyObj = {
// some code here.
}
return MyObj;
})(jQuery);
console.log(MyObj);
The other way to do this would be to put MyObj in the global space explicitly. In a browser, the global object is called window, so you could, inside your IIFE, say window.MyObj = { /* some code here */ }. I would discourage this approach, though, because it's not clear to the outside observer that that's happening (i.e., you would have to rely on documentation). The better approach is to return the objects you want to expose, and the caller can choose to put those in global space.
Using use strict, you can only put an Object in int the global namespace explicitly.
For example, you could use window.myObj = MyObj in your function.
Another possible way to achieve your goal is by passing the global object to your function (it could be window in the browser or global in nodejs).
(function($, global) {
"use strict";
var MyObj = {}
global.myObj = MyObj;
})(jQuery, window);
Moreover, as Ethan suggested, since you return MyObj, you could simply assign the result of the invocation to a variable.
var myObj = (function($) {
"use strict";
var MyObj = {}
return x
})(jQuery);
myObj; //now defined in the global scope
I recently read somewhere (I am really sorry I can't provide the source) that you can use this.varname to access globals in replacement to window.varname to save 2 chars
var myVar = "global";
function myFunc() {
var myVar = "notGlobal";
alert( this.myVar ); //global
}
It seems to work, but I want to know if:
it is safe to use in old browsers
it is cross-browser compatible
it will fail under some weird circumstances
I don't think I'd do it, but it's completely cross-browser compatible if this is referring to the global object (window). Whether it is will depend on how the function in question was called (at global scope, this does indeed refer to the global object), and whether the code in question is in "strict mode" or not. (In strict mode, this does not refer to the global object. Kudos and upvotes to Esailija for pointing that out.)
In non-strict code:
So at global scope:
console.log(this === window); // true if not in strict mode
And similarly, if you have a function you call directly:
function foo() {
console.log(this === window);
}
foo(); // Logs "true"
But, in JavaScript, this is set entirely by how a function is called. So we could call foo setting this to something else:
var obj = {};
foo.call(obj); // Now it logs "false", `this` === `obj` during the call
Similarly:
var obj = {};
obj.f = foo;
obj.f(); // Also logs "false", `this` === `obj` during the call
So in conclusion, at global scope (not in any function call), yes, this is reliably pointing to the global object, and if you control how the function gets called and you call it without setting this to anything else (via call or apply, or by using it from an object property a'la obj.f above), then, again, it will reliably refer to the global object. This is covered by sections 10.4.1 (Entering Global Code) and 10.4.3 (Entering Function Code) of the specification. This is how it's been from the very beginning, I believe; certainly for the last 15 years, so you're unlikely to find a non-compliant environment.
More reading:
Mythical methods
You must remember this
It will not work in modern browsers if someone slaps "use strict" above your code as this will be undefined inside the function.
<script type="text/javascript">
"use strict";
...
function test(){
console.log(this);
}
test(); // undefined
</script>
Note that you can save much more characters just by assigning window to some variable at the top of your code and using that as it will be shortened to some one character variable by a minimizer.
(function(global){
global.foo();
global.bar();
})(window);
would be minimized to (without whitespace):
(function(a){
a.foo();
a.bar();
})(window);
Where as this would not be touched as it's a keyword.
this always has a value. And if this hasnt been overridden somehow, then it will be window where all your globals are.
It will work in all JS implementations, but beware! This is more brittle, and it won't always point to a global variable.
For instance:
var myVar = "global";
var obj = {
myVar: "property",
fn: function() { return this.myVar; }
}
console.log(obj.fn()); // "property"
This fails because in this context this is that object. But when executing a function that is NOT a property of an object, this will default to window, like your example.