0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

HTMLから、不意に何度呼ばれても安心なJavaScriptを書いてみる

Posted at

やりたいこと

外部からGETなどで読み込んだHTMLなどに、すでに読み込んだjavascriptが含まれていることがある。
そのときに例外が発生してしまい、処理が中断されるような状況を回避したい。

回避できる問題

  • 変数や関数の再定義を回避する
  • 公開したくない処理を隠蔽することで、意図せず他の処理に影響を与えることを防ぐ
  • 値をグローバルに公開したいとき、スクリプトが呼び出されるたびに初期化されることを防ぐ

実装例

sample.js
// 無名関数を即時実行することで、グローバルスコープに何も残さないようにしている (後述)
(() => {

  /**
   * 初回だけ初期化して、意図せず値が失われることを防ぐ
   * 例としてこの型を使っているが、実際には何でも良い
   *
   * @type { Set<any> }
   */
  const sharedData = (() => {

    // すでにデータが存在する場合はそれを返す
    if (window.__sharedData) {
      return window.__sharedData;
    }

    // 共有データを保持するためのSetを作成
    const sharedData = new Set();

    // グローバルスコープに共有データを登録
    // windowの中に存在しない名前をつけて、衝突を避けること。
    window.__sharedData = sharedData;

    return sharedData;

  })();


  // 外部ファイルに公開する関数群

  function addValue(...value) {
    value.forEach(v => sharedData.add(v));
  }

  function getSharedData() {
    return sharedData;
  }

  // 関数をグローバルスコープに登録
  window.addValue = addValue;
  window.getSharedData = getSharedData;


  /**
   * 公開しない関数
   */
  function hiddenFunction() {
    console.log('公開しない関数が実行されました');
  }

  hiddenFunction();

})();

実行結果

sample.html
<script src="./sample.js"></script>
<script>
addValue("hoge", 1);
console.log(getSharedData());
// => Set(2) {'hoge', 1}
</script>

<!-- 無名関数で実行しているため、定義済みなどのエラーは発生しない -->
<script src="./sample.js"></script>
<script src="./sample.js"></script>
<script src="./sample.js"></script>
<script src="./sample.js"></script>
<script src="./sample.js"></script>
<script src="./sample.js"></script>
<script>
addValue("fuga", 123);
console.log(getSharedData());
// => Set(4) {'hoge', 1, 'fuga', 123}
</script>


<script>
// console.log(sharedData);
// => ReferenceError: sharedData is not defined

// hiddenFunction();
// => ReferenceError: hiddenFunction is not defined
</script>

コンソールには以下のようなログが出力される。

VM555 sample.js:48 公開しない関数が実行されました
index.html:4 Set(2) {'hoge', 1}
VM557 sample.js:48 公開しない関数が実行されました
VM558 sample.js:48 公開しない関数が実行されました
VM559 sample.js:48 公開しない関数が実行されました
VM560 sample.js:48 公開しない関数が実行されました
VM561 sample.js:48 公開しない関数が実行されました
sample.js:48 公開しない関数が実行されました
index.html:17 Set(4) {'hoge', 1, 'fuga', 123}

無名関数を即時実行する理由

初期化をする関数に名前を付けた場合、その時点でグローバルスコープに関数が登録される。
例として以下のようなinit()を定義した場合、既存のwindow.initを上書きすることになる。

function init() {
  // 初期化処理
}

window.init();  // 動作する
init();         // もちろん動作する

このような問題を回避するために、即時実行関数を使っている。

(() => {
  // 初期化処理
})();

// windowオブジェクトに何も登録されない
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?