ブラウザサイドJavaScript

document.currentScriptを活用し、グローバル汚染を完全に?回避する

※workerではこの方法は使えません。

グローバル汚染って?

一般的に言われるものは概ね、windowにプロパティが作成される現象。
プロパティなので、基本的には書き換えが可能。
バグの原因になっている場合に追い難い。

スクリプトコンテキスト上でも同様の事が起こり得る。
グローバルにlet宣言した変数が、単純に間違って代入されることにより書き換えられるリスクがある。

基本的な対処

各スクリプトはそのファイル内で即時実行やブロックスコープ内だけで完結させる。
各スクリプト間のやり取りはwindow.appのような唯一作成したグローバルオブジェクトを経由させる方法。
あるいはスクリプトコンテキスト上にconstでオブジェクトを作成し、これを用いる方法。

sample1.js
//レガシー 
(function(){
 window.app = window.app || {};
 window.app.a = function(){/*do something*/};
})();
//ECMAS5以降
const app2={};
{
 app2.a = function(){/*do something*/};
};

ただ、この場合もやはり、window.appやapp2の衝突は発生する可能性が捨てきれない。
そもそも、グローバル汚染の本質は名称の衝突であり、結果的にこれを回避する事だけが最終目的です。
もう少し安全な方法がありそうなものです。

具体的な方法

各スクリプト間のやり取りはdocument.currentScriptを経由させます。

sample2.js
//レガシー 
(function(){
 document.currentScript.app = { a:function(){/*do something*/} };
})();
//ECMAS5以降
{
 document.currentScript.app2.a = function(){/*do something*/};
};

利用する側はwindowからではなく、このスクリプトに該当するスクリプトエレメントにアクセスし、
スクリプトエレメントのappプロパティから必要な値を読みだせば良い、と言う具合。

sample.html
<script id="sample" src="sample2.js"></script>//呼び出される側には適当なアクセッサを付けておく
<script>//呼び出し側スクリプト
//レガシー 
( function(){
 var a = document.getElementById("sample").app.a;
 a(); //do somethingされる。
})();
</script>
<script>
//ECMAS5以降
{
 const { a } = document.querySelector("#sample").app;
 a(); //do somethingされる。
}
</script>

エレメントを独自拡張するのは良い事とは言えないかもしれません。
しかし、windowを独自拡張するくらいなら、こういう方法も有効かもしれません。
大半のブラウザでimportとexportが使えるようになれば、こういう小技のアドバンテージも小さくなる事でしょう。

利用する上で考慮すべきこと

この例では独自に拡張したプロパティ名としてappやapp2を使っていますが、将来的なHTMLScriptElementの拡張の可能性を考慮に入れるならば、敢えてもう少し複雑で冗長なプロパティ名を利用した方が良いかもしれません。
また余りにレガシーな環境ではdocument.currentScriptが動かないかもしれません。ご注意を。