変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

2025年4月14日

[javascript]コードは短い方が良い(かもしれない

const divMatchPattern = document.getElementById('matchPattern');
const txtMatchPattern = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
divMatchPattern.value = txtMatchPattern.toString();

というコードがあって、

id=matchPatternなオブジェクトが無い場合があるので

const divMatchPattern = document.getElementById('matchPattern'); if(divMatchPattern){
const txtMatchPattern = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
divMatchPattern.value = txtMatchPattern.toString(); }

とすると読みにくいから「保存(Ctrl+S)」すると

const divMatchPattern = document.getElementById('matchPattern');
if(divMatchPattern){
    const txtMatchPattern = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
    divMatchPattern.value = txtMatchPattern.toString();
}

となって長くなるから1行でも短くしようと捻って

for (const divMatchPattern of document.querySelectorAll('#matchPattern')) {
  const txtMatchPattern = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
  divMatchPattern.value = txtMatchPattern.toString();
}

とか

document.querySelectorAll('#matchPattern').forEach((divMatchPattern) => {
  const txtMatchPattern = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
  divMatchPattern.value = txtMatchPattern.toString();
});

とか

try {
  document.querySelector('#matchPattern').value = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";
} catch() {}; ※とミスっても原因が~になるパターン

とか最後には

document.querySelector('#matchPattern') {左辺がnullじゃなければ右辺も続けて評価する演算子} .value = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";

があったらいいなぁとか思う。

Null合体演算子(??)が近いけど、基本は || なので

const 結論=(Aプラン) ?? (Bプラン) ?? (冗談ではない)

的に

const ストーリィ展開=(なんだかわかないけど) ?? (なんかわかった) ?? もうどうなってもいいや

の様に使うものなので、

document.querySelector('#matchPattern') ?? .value = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";

は期待通りには動かない。

「.」を「.$.」にすると左辺がnullだったらもうどうなってもいいやドット演算子があったらいいな

document.querySelector('#matchPattern').$.value = "/(\\/\\*[\\s\\S]*?\\*\\/|\\/{2,}・・・/g\n";

一応、try ・・・ catchで包まれていたらthrowするけど

try {
   document.querySelector('#matchPattern').$.value = "/(\\/・・・"; ※中で throw(undefined)する
   ※Throw.resume()で、ここに戻る
} catch (ex, stack) {
  if(ex) console.log(ex) ※ ex が undefinedなので
  else stack.resume();   ※ throwした直後に戻る。
                         ※ 状況は stack.stackを読む
                         ※  行番号とかカラムとかnullな処理箇所が判ると助かる
}

だといいな。

あとドット演算子(.)とアロー演算子(->)の違いの記事を読んだ感想。

昔のC言語では、メモリがとっても少なかったので作りがとても簡素で

簡素な構文解析に落とし込まないとメモリに入らない

というゲーム制作みたいな理由。

変数は基本的にアドレス+オフセット+サイズとして考えるポインタ変数でプリミティブな型変数はオフセットが0でサイズがプリセットで決まっているポインタ変数。

このため、構造体のドット演算子もポインタ変数のアロー演算子も「この変数の構造(オフセット)を見てオフセットを計算してね」という意味では同じだから一緒で良かったハズ。

しかし、基本な皆中身がポインタ変数なので、内部構造であるハズのポインタ変数を明示的に使われるとコンパイラは

  • 普通の変数は梱包済み
  • ポインタ変数は開梱済み

を切り分けて処理するのも面倒なので

※2通りの変数の種別があると毎度毎度似た様な処理が4通り必要になるのでウザい

仕方なく演算子(という表現上で人が指示する様に)を分けた様に思える。

昔のソースって概ね一本の長さが80列×25行程度に収まってたのもあるけどね。



[xterm.js]ssh接続その4

段々複雑になってきたので

xterm.jsの画面からは

  1. 画面からWebSocketで何か送信する
    • {
      • ssh : {
        • logon : {
          • username : xxxx
          • etc.
    • }…}
  2. ホスト側で受信
  3. JSONデータをアドオンのエイリアス(ssh)で配分する
    • { ssh : ・・・
    • sshエイリアスなアドオンのjsonRequestを呼び出す
  4. アドオンは受け取ったJSONデータから機能を実行する
    • { logon : ・・・
    • 処理名(logon)を読み取ってlogon処理を実行する
    • logon処理
      • { username : xxxx, …}
  5. 処理名が何か出力したらWebSocketで返信する

にすると後付けが楽な気がしたので、NodeJsで動く部分をアドオン化してみたら更に複雑になった。

├── package.json
├── package-lock.json
├── index.js
├── lock_file.js
├── tree.txt
├── web_socket_entity.js
└── addon
  ├── package.json
  ├── base_addon.js
  ├── js_yaml
  │├── package.json
  │└── js_yaml.js
  └── ssh_client
    ├── package.json
    ├── package-lock.json
    └── ssh_client.js

アドオンフォルダ(./addon)にプロジェクトごとコピーする方式。

AddonManagerのsetupで、アドオンフォルダ(./addon)の中のフォルダにある

package.jsonのmainかexportをからモジュールのjsファイルを見つけて

エイリアス付きでtypeListsにリストアップするダケなのにとっても長い。

/**
 * アドオンマネジャクラス
 */
export class AddonManager extends BaseAddon {
 ・・・省略・・・
  /**
   * リスト
   */
  typeLists = {};
 ・・・省略・・・
  /**
   * コンストラクタ
   */
  constructor() {
    super();
  };
  /**
   * ./addonディレクトリィのパッケージを検索
   * @param (*) pathAddonsDir
   */
  setup = (pathAddonsDir, allAddonInfo) => {
    this.allAddonInfo = allAddonInfo;
    return new Promise((resolve, reject) => {
      fs.readdir(pathAddonsDir, { encoding: 'utf-8', withFileTypes: true, recursive: false },
        (err, dirents) => {
          /**
           * error
           */
          if (err) {
            console.error(err);
            reject(err);
            return;
          }
          /**
           * ディレクトリィのみに絞込む
           */
          dirents = dirents.filter((d) => d.isDirectory());
          /**
           * とりあえず配列分ループ
           */
          const arP = dirents.map((dirent) => {
            return new Promise((resolve, reject) => {
              const addonPath = `${pathAddonsDir}/${dirent.name}`;
              console.log(addonPath);
              // dirent配下のpackage.jsonを読む
              const packageJsonPathname = `${addonPath}/package.json`;
              const packageJsonText = fs.readFileSync(packageJsonPathname);
              const packageJson = JSON.parse(packageJsonText);
              const mainFile = packageJson.main || packageJson.exports;
              if (mainFile !== undefined) {
                const mainFilePathname = `${addonPath}/${mainFile}`;
                import(mainFilePathname)
                  .then((module) => {
                    // 動的に読み込まれたモジュール
                    const addonModule = new module.default;
                    // アドオンリストに追加
                    this.setAddonList(addonModule.addonAlias, addonModule);
                    resolve(true);
                  })
                  .catch((ex) => {
                    console.error(ex);
                    reject(ex);
                  });
              } else {
                console.error(`addonSetup : ${packageJsonPathname} not found main or exports`);
              }
            });
          });
          Promise.all(arP)
            .then((values) => {
              resolve(true);
            });
        }
        // end of for (const dirent of dirents)
      );
    });
  };
 ・・・省略・・・
};

アドオンを呼び出す時は

AddonManager::getAddonObject({アドオンのエイリアス})でオブジェクトを取得

  /**
   * アドオン・オブジェクトを取得
   * @param {*} alias 
   * @returns addon
   */
  getAddonObject = (alias) => {
    const addonInfo = this.typeLists['addon'][alias];
    if (addonInfo) {
      const object = new addonInfo.constructor(this.allAddonInfo[alias]);
      return object;
    } else {
      return undefined;
    }
  };

画面からの要求を{アドオンのオブジェクト}::jsonRequest(WebSocket, json)から

アドオンオブジェクトを作成し実行させている。

  /**
   * JSONリクエスト処理
   * @param {*} webSocketEntity 
   * @param {*} json 
   * @param {*} addonObjectList 
   */
  jsonRequest = (webSocketEntity, json, addonObjectList) => {
    const addonModules = this.typeLists['addon'];
    for (const alias in json) {
      const addon = addonObjectList[alias] ?? this.getAddonObject(alias);
      if (!addonObjectList[alias]) { addonObjectList[alias] = addon; }
      if (addon) {
        try {
          addon.request(webSocketEntity, json[alias]);
        } catch (ex) {
          console.log(`AddonManager::jsonRequest : unknown addon alias  '${alias}'`);
        }
      } else {
        console.log(`AddonManager::jsonRequest : unknown addon alias  '${alias}'`);
      }
    }
  };

レスポンスはアドオンオブジェクトにWebSocket宛に送信してもらった。

  /**
   * クライアントに返信する処理
   * @param {string} type 
   * @param {string} commandName 
   * @param {any} data 
   */
  sendClient = (type, commandName, data) => {
    //console.log(`${commandName} : ${data}`);
    const responce = {};
    responce[type] = data;
    const blob = new Blob([JSON.stringify(responce)], { type: "application/json" });
    this.connect.send(blob);
  };

やっと画面側もtype毎に処理を分けないといけない事に気が付く。

めんど




top