Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the Immediately-Invoked Function Expression (IIFE) pattern really necessary when writing userscripts?

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.

like image 973
Marc.2377 Avatar asked Jun 22 '19 23:06

Marc.2377


People also ask

Why use immediately invoked function expression?

An Immediately-invoked Function Expression (IIFE for friends) is a way to execute functions immediately, as soon as they are created. IIFEs are very useful because they don't pollute the global object, and they are a simple way to isolate variables declarations.

What is an IIFE immediately invoked function expression )? Can you give an example?

An Immediate-Invoked Function Expression (IIFE) is a function that is executed instantly after it's defined. This pattern has been used to alias global variables, make variables and functions private and to ensure asynchronous code in loops are executed correctly.

What does IIFE mean in JavaScript?

An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.

What is IIFE pattern?

The Immediately Invoked Function Expressions (IIFE) Pattern is a technique used to hide scope involving wrapping code inside a function that is immediately called. This technique allows developers to create an object in their scope without polluting the outer scope.


1 Answers

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.

like image 162
Brock Adams Avatar answered Oct 16 '22 20:10

Brock Adams