我的问题与javascript中自执行函数的目的是什么?非常相似,但它涉及用户脚本(特别是针对GreaseMonkey)。
我发现有些用户脚本是按照这种模式分发的,有些则不是。
具有 IIFE 模式的脚本示例:(来源)
// ==UserScript==
// (...)
// ==/UserScript==
(function(){
// if <condition>
document.location.href += '?sk=h_chr';
// ...
})();
没有它的脚本示例:(来源)
// ==UserScript==
// (...)
// ==/UserScript==
window.location.href = "https://www.facebook.com/?sk=h_chr";
此外,我还发现 TamperMonkey 的“New script”模板遵循它,而 GreaseMonkey 和 ViolentMonkey 的模板则不然。
那么问题是,IIFE 模式在编写用户脚本时有用吗?
strict
模式,并且我使用 let
而不是 var
。无论如何,据我所知,用户脚本中定义的函数和变量在全局页面范围内不可用。
谢谢。
一般来说,不会; IIFE 模式对于包装整个用户脚本很少有用(请参见下面的边缘情况)。这让人回想起很多年前,当时一些引擎(短暂地)默认情况下不包装脚本。
@unwrap
指令,脚本引擎现在都会忽略它。
以下是使用 IIFE 模式的一些原因:
strict
模式的唯一方法。Parsing error: 'return' outside of function
警告如果同时使用:(1)脚本范围的return
和(2)外部LINTer。考虑这个测试脚本:
// ==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;
}
在所有情况下,用户脚本都是有作用域/包装的。该页面看不到代码,也看不到像
cantSeeMeInConsole
这样的变量。@grant none
模式下,脚本页面冲突仍然可能发生。
额外的隔离适用,具体取决于:(a) 用户脚本引擎、(b) 浏览器和 (c)
@grant
模式。canSomestimesSeeMe
。
'use strict';
放在顶部,将整个用户脚本切换到严格模式。在相关注释中,如果脚本不使用
@run-at
设置,则使用 $(document).ready()
或其简写没有意义。
用户脚本中定义的函数和变量在全局页面范围内不可用
这不是真的。
用户脚本的工作方式是通过脚本注入(是的,这基本上是一种攻击)。用户脚本访问页面中的变量和函数的唯一方法是将用户脚本公开给页面 - 因此页面可以访问用户脚本。
因此,在用户脚本中使用 IIFE 的主要原因是避免弄乱页面上运行的脚本。
某些脚本注入系统可能在您背后的IIFE中透明地执行您的用户脚本(这就是nodejs对模块所做的事情 - 是的,它不是一个用户脚本系统,但它是执行此操作的软件示例)。在这种情况下,您不需要自己手动编写 IIFE 代码。我个人不知道哪个扩展执行此操作,哪个扩展不执行此操作,因此为了安全起见,我倾向于包含 IIFE。
如果您的代码没有定义任何新变量或函数,您也可以不使用 IIFE,因为您的代码中没有任何内容可以覆盖现有代码。