I'd like to import jQuery inside an IIFE scope like this:
(function (window, document) {
"use strict";
/* Content of jquery.min.js goes here */
/* Code that uses $ goes here */
}(window, document));
Now, I'm running into a strict mode violation as soon as this code runs, possibly because jQuery itself doesn't conform:
In strict mode code, functions may be declared only at top level or
immediately within another function.
Is there a way to load jQuery just inside a single function scope, while still retaining strict mode for the rest of the code that relies on it?
Answered here: https://github.com/jquery/jquery/issues/1779
jQuery isn't meant to be run with "use strict" in effect. See the comment at the top of the unminified file for the reason, or look at http://bugs.jquery.com/ticket/13335 for more information. You are not gaining anything by attempting to force jQuery into strict mode against its will, doing so prevents interoperability with any third-party libraries that are not use-strict compatible.
Related
My question is quite similar to What is the purpose of a self executing function in javascript?, however it concerns userscripts (specifically for GreaseMonkey) instead.
I see that some userscripts are distributed with this pattern, and some are not.
Example of script with the IIFE pattern: (source)
// ==UserScript==
// (...)
// ==/UserScript==
(function(){
// if <condition>
document.location.href += '?sk=h_chr';
// ...
})();
Example of script without it: (source)
// ==UserScript==
// (...)
// ==/UserScript==
window.location.href = "https://www.facebook.com/?sk=h_chr";
In addition, I also found that the "New script" template from TamperMonkey follows it, while the templates from GreaseMonkey and ViolentMonkey do not.
The question is, then, is the IIFE pattern any useful when writing userscripts?
Specially, if my script is in strict mode, and I use let instead of var. In any case, as far as I know, functions and variables defined in userscripts are not made available in the global page scope.
Thanks.
In general, no; the IIFE pattern is seldom useful for wrapping a whole userscript (see edge cases below). That's a throwback to many years ago when some engines (briefly) did not wrap scripts by default.
In fact, if you include the obsolete #unwrap directive, the script engines will all now ignore it.
Here are some reasons to use the IIFE pattern:
It was the only way to enforce strict mode in old versions of Violentmonkey (2018 or earlier) for the whole script.
It can squelch a harmless Parsing error: 'return' outside of function warning if you use BOTH: (1) A script-wide return and (2) an external LINTer.
Some old Greasemonkey versions would also warn about this, while still working perfectly.
(I thought there was a 3rd edge case. But got interrupted and can't remember what it was.)
Consider this test script:
// ==UserScript==
// #name _Scope and Strict-Mode Demo script
// #match https://stackoverflow.com/*
// #unwrap
// #grant none
// ==/UserScript==
/* eslint-disable no-multi-spaces, curly */
'use strict';
if (location.pathname.includes("/users/") ) {
console.log ("Terminating script early.");
return; // In external LINTers, this will cause a harmless warning.
}
var cantSeeMeInConsole = "neener neener";
window.canSomestimesSeeMe = "Howdy";
console.log (`In Strict mode: ${bInStrictMode() }; \`cantSeeMeInConsole\`: ${cantSeeMeInConsole}`);
function bInStrictMode () {
var inStrict = false;
var dummyObj = {};
Object.defineProperty (dummyObj, 'foo', {value: "bar", writable: false } );
try { dummyObj.foo = "fee"; }
catch (e) { inStrict = true; }
return inStrict;
}
Run on Firefox and Chrome.
Safari and Opera should give same results.
Microsoft Edge probably gives same results. (But I don't care much if it doesn't.)
Run using Tampermonkey, Violentmonkey, and Greasemonkey 4.
Script scoping:
In all cases, the userscript is scoped/wrapped. The page can't see code, nor variables like cantSeeMeInConsole.
Beware that script page conflicts can still occur in #grant none mode.
Script sandboxing:
Additional isolations apply, depending on: (a) the userscript engine, (b) the browser, and (c) the #grant mode.
For example, using Greasemonkey, or changing the grant mode kills the page's ability to see canSomestimesSeeMe.
Strict mode:
Placing 'use strict'; up top like that switches the whole userscript into strict mode.
Additionally, in Tampermonkey's advanced options, you can set "strict mode" to [Default/Always/Disabled] for all scripts.
In a related note, if the script does not use #run-at settings, there is no point in using $(document).ready() or its shorthand.
functions and variables defined in userscripts are not made available in the global page scope
This is not true.
The way userscripts work is via script injection (yes, it's basically an attack). The only way userscripts can get access to variables and functions in the page is to expose userscripts to the page - thus the page has access to the userscript.
Therefore the main reason to use an IIFE in userscripts is to avoid messing up scripts running on the page.
Some script injection system may transparently execute your userscript in an IIFE behind your back (this is what nodejs does with modules - yes, it's not a userscript system but it is an example of software that does this). In such cases you don't need to manually code the IIFE yourself. I personally don't know which extension does this and which does not so I tend to include the IIFE just to be safe.
If your code does not define any new variables or functions you also can get away with not using an IIFE because there is nothing in your code that can override existing code.
I've anecdotally noticed that use strict appears to be more common like this:
(function() {
'use strict';
...
Than this:
'use strict';
(function() {
...
The vanilla JS implementation of TodoMVC does this, for example.
Is there a reason for this?
Edit: I'm aware of the whole-file versus function-block distinction. TodoMVC is a good example to demonstrate why this placement is strange to me, because it doesn't rely on any external libraries, so the whole "play nice with non-strict third parties" doesn't apply here.
Declaring it in local scope will enforce function block to be considered under strict-mode by browser.
You can have non-strict observation for other code outside of the IIFE
Inside IIFE:
(function() {
"use strict";
a = 100;
})();
b = 200;
For entire script:
"use strict";
(function() {
try {
a = 100;
} catch (e) {
console.log(e + '');
}
})();
b = 200;
As highlighted in docs,
If you are using strict mode for entire script, it isn't possible to blindly concatenate non-conflicting scripts. Consider concatenating a strict mode script with a non-strict mode script: the entire concatenation looks strict! The inverse is also true: non-strict plus strict looks non-strict. Concatenation of strict mode scripts with each other is fine, and concatenation of non-strict mode scripts is fine. Only concatenating strict and non-strict scripts is problematic. It is thus recommended that you enable strict mode on a function-by-function basis (at least during the transition period).
You can also take the approach of wrapping the entire contents of a script in a function and having that outer function use strict mode. This eliminates the concatenation problem but it means that you have to explicitly export any global variables out of the function scope.
I’m guessing it’s because that placement is sure to enable strict mode even if multiple JS files are concatenated together as a build step (to allow organizing of code without the performance penalty of multiple HTTP requests). With placement of "use strict" at the beginning of the file, there could be a problem with these files:
foo.js
function doThing() {
console.log("done");
}
main.js
"use strict";
(function() {
document.getElementById('thingy').addEventListener("click", doThing);
})();
If the above files were concatenated with foo.js first, the "use strict" inside main.js would have no effect. This possibility could be avoided by putting the "use strict" inside the function.
I don’t know how common concatenation of JS is any more, and I don’t know whether the newer require method or import keyword lets you place "use strict" anywhere you want, but maybe the placement of "use strict" inside the function caught on while simple concatenation was popular, and people saw no reason to change the convention after using it for so long.
If you using:
'use strict';
(function() {
...
It will apply use strict mode to all file.
Opposite, when you using use strict in function like this:
// Non-strict code...
(function(){
"use strict";
...
// Define your library strictly...
})();
// Non-strict code...
That might be helpful if you have to mix old and new code.
Why Strict Mode At All
Strict mode eliminates certain permissive language parsing and execution characteristics deemed less desirable from a language design point of view. These non-strict mode language characteristics were deemed prudent as default behavior for backward compatibility. The synopsis and details appear here.
ECMA-262 7.0 Strict Mode Code
ECMA-262 7.0 Strict Mode in detail
It is not unambiguously defined when and if these older language characteristics from the early Netscape days will be deprecated or whether strict mode will become default behavior across browsers at some point, but the stricter model is likely to produce less ambiguous and risky source. If you wish to improving maintainability, portability, and extensibility in coding practice and code bases, then strict mode is a good option.
Syntax
"use strict";
Scope of Declaration
The scope of is dependent on whether you place the declaration inside or outside a function and applies to all statements following the declaration within the closure's scope.
Use of the declaration inside the function is the wrong way to do it if all of the file or stream is already compatible with the stricter mode or can be made so with ease. The redundancy is unnecessary, so placing the declaration on the top of the file or beginning of the stream is the recommended use.
Sometimes only some of the functions comply with the stricter rules. In such cases the less desirable function scope of the declaration can be used to encourage finer coding practices at least in the functions that already comply.
NOTE For those coming here from "What's the benefit of using "function() 'use strict'” in every file?"
You can place
"use strict";
on the top of the code sequence or inside the function, so it is only one line of code per file or per function.
The other lines of code have other purposes in the code here.
Self-invoking function
Factory invocation
I'm writing a JavaScript-Software that is able to load and work with plugins.
These plugins can also be written by other users.
Now I want to modify my plugin-manager, that it sets the "use strict"-option for all the modules - is this possible?
This is how the plugins are currently written:
"use strict"; //I want to add this command automatically by the plugin-manager
window.pluginName = {
"version": "0.1",
"attribute": function(){},
...
}
How Plugins are being loaded:
I make an ajax-Request to the js-File (which contains exacly one plugin). When the file is ready, I access it via "var newPlugin = window[pluginName];"
Is is possible to automatically use strict-mode, or do I have to rely that the plugin-writer uses this flag for debugging?
Strict mode is defined in variable scope, so the only way to set "use strict" for each plugin is to put the declarative in the functions at the top, or in some function that encompasses all the plugin definitions.
The only other way to affect all plugins would be to define it globally, and that wouldn't be a good idea unless you're absolutely certain that all JavaScript that will run will work properly in strict mode.
"use strict"; seems awesome, and we'd really like to use it at our shop. However, we just want it so that we (the developers) can find strictness-issues; we very much DO NOT want to make our site break for our actual customers when it was working fine before.
Now, we could just use some server-side logic to achieve this:
{% if debug %}<script>"use strict";</script>{% endif %}
... except that "use strict" operates on a file-by-file basis, so that won't actually work (well, unless we start server-side processing all of our JS files).
So, my question is: do all the things "use strict" checks for get checked when the page loads, or is it possible for "use strict" to find errors after the page has loaded? If it's the former, we can just use "use strict" and stop worrying, because we'll load our site in development before loading it on live. However, if it's the latter we seem to be out of luck, as we can't test every possible runtime condition (and again, we don't want to make errors for our users when there were no errors before).
It's the latter. While beeing in strict mode, an Javascript interpreter may throw error messages at runtime, which would not get thrown in non-strict mode.
On the other hand, most of these errors are "good errors", which means, they will actually help not breaking your code.
For instance
function foo() {
"use strict";
bar = true;
}
foo();
This will throw
"ReferenceError: assignment to undeclared variable bar"
in strict mode, which is a good thing. In non strict mode, we would just have created a global variable called bar, which is probably not what we wanted. There are plenty of other situations where strict mode prevents the programmer from doing something stupid/bad/unwanted and throws error messages. But again, you want to have those errors instead of some wierd bugs.
Have a further read on MDN
If I understand you correctly, yes, it's definitely possible for strict mode to catch errors after the page has loaded. For example:
'use strict';
setTimeout(function() {
undefined = 42; // Causes a TypeError
}, 1000);
// Click here for a demo.
What you can do is pretty simple: you should be minifying your JavaScript when you move to production anyway. Just make sure that the 'use strict' gets removed during that minification process.
Failing that, just try to make sure your code is strict-error-free. Strict mode usually comes into play with regards to semantics, not because of something odd the user input or even because of syntax. It shouldn't be too terribly difficult to catch all the cases. (But minifying and removing 'use strict' is a much better solution.)
At work I have to deal with a lot of (sometimes awful) JavaScript that vendors expect us to drop into our websites. Some of them add arbitrary, un-namespaced, names to the global scope. I've been toying with different ideas about sandboxing these programs so they don't have direct access to the global scope, but I haven't been able to think of anything that might actually work. (I thought of evalling the fetched code, but I'm convinced that's a bad idea.)
What solutions are there to keep the global scope clean while still using arbitrary 3rd party JavaScript?
Edit: #kirilloid's comment tells me I haven't been clear about how this code is delivered. I'm not usually given code to put into our website. Most of the time, I'm just given a URL and asked to create a script tag that points to it.
Your options are pretty limited. If the code is presented to you in the form of a <script> tag you can't modify, you can stop trying now.
If you can modify the script, you can wrap the code in a closure; this will stop any variables they declare with var being published in the global scope (but you'll still get problems with implicit globals).
(function () {
var aGlobalVariableThatUsedToCauseACollision = 4; // no longer collides!
anotherGlobalVariableThatUsedToCauseACollision = 4; // Mmmmm.
}());
Probably an infeasible option; you could use "use strict" which prevents them using implicit globals, but if the code is as awful as it seems, this won't help you.
An additional change (and prehaps the best) you could make could be to wrap your own code in a closure, and keep local copies of the important window properties (undefined) etc in there; this way you prevent other scripts affecting yours (this is what libraries such as jQuery do).
(function (jQuery, $, undefined) {
alert(undefined == 4); // false!
}(jQuery, $));
undefined = 4;