技術とか戦略とか

SIerで証券レガシーシステムを8年いじってからSESに転職した普通の文系SEによる技術ブログ。

JavaScript:Strictモードとは

JavaScriptには「Strictモード」と呼ばれるモードが用意されています。
Strictモードにすることで、バグに気付かずに実行が継続されるのを防ぎやすくなり、高速化することもあります。
 
詳しくはMozillaのページ(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode)に書かれていますが、この記事では簡単に紹介したいと思います。
 
----
 
Strictモードに設定するためには、以下の2つの方法があります。
(export句を使用した方法もありますが、この記事では割愛します)
 
1.スクリプト全体で定義
スクリプトの最初に「'use strict';」を記述することで、そのスクリプト全体がStrictモードになります。
 
・サンプルコード
'use strict'; // スクリプトの最初で定義、スクリプト全体がstrictモード
console.log("Hello world!");
 
2.関数全体で定義
関数の最初に「'use strict';」を記述することで、その関数全体がStrictモードになります。
 
・サンプルコード
function hoge() {
 'use strict'; // 関数の最初で定義、関数全体がstrictモード
 console.log("Hello world!");
}
// 関数の外はstrictモードではない
hoge();
 
----
 
Strictモードに設定することで、以下のような挙動変更が行われます。
また、この記事では紹介しませんが、
 
1.バグになりやすい誤りがある場合に異常終了させる
例えば、定義済みの変数を参照しようとして変数名の記述を誤った場合、Strictモードの場合は異常終了させることができます。
 
・サンプルコード(非Strictモード)
// 'use strict';
let hoge = "definition";
hage = "Hello world!";
console.log(hoge);
 
・実行結果(非Strictモード)
C:\tmp>node hello.js
definition

C:\tmp>
 
・サンプルコード(Strictモード)
'use strict';
let hoge = "definition";
hage = "Hello world!";
console.log(hoge);
 
・実行結果(Strictモード)
C:\tmp>node hello.js
C:\tmp\hello.js:3
hage = "Hello world!";
   ^

ReferenceError: hage is not defined
  at Object.<anonymous> (C:\tmp\hello.js:3:6)
  at Module._compile (internal/modules/cjs/loader.js:1137:30)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
  at Module.load (internal/modules/cjs/loader.js:985:32)
  at Function.Module._load (internal/modules/cjs/loader.js:878:14)
  at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
  at internal/main/run_main_module.js:17:47

C:\tmp>
 
2.セキュリティーの強化
JavaScriptでは、必ずオブジェクトを用意する必要があり、自分でオブジェクトを定義しない場合は「グローバルオブジェクト」と呼ばれるブラウザや実行環境が用意したオブジェクトが使用されます。
また、キーワード「this」を使用することで、記述箇所を動かしているオブジェクトの情報を取得することができます。
「this」によりグローバルオブジェクトを参照された場合、セキュリティー上の問題が発生する可能性があるため、Strictモードではグローバルオブジェクトが参照されるようなケースで「this」キーワードは「undefined」を返すようになります。
 
・サンプルコード(非Strictモード)
//'use strict';
function hoge() {
 console.log(this); // 関数を呼び出しているオブジェクトを参照
}
hoge(); // グローバルオブジェクトの関数を実行
let hello = {}; // 独自のオブジェクト定義
hello.hoge = hoge; // 独自のオブジェクトに関数を定義
hello.hoge(); // 独自のオブジェクトの関数を実行
 
・実行結果(非Strictモード)
C:\tmp>node hello.js
Object [global] {
 global: [Circular],
 clearInterval: [Function: clearInterval],
 clearTimeout: [Function: clearTimeout],
 setInterval: [Function: setInterval],
 setTimeout: [Function: setTimeout] {
  [Symbol(nodejs.util.promisify.custom)]: [Function]
 },
 queueMicrotask: [Function: queueMicrotask],
 clearImmediate: [Function: clearImmediate],
 setImmediate: [Function: setImmediate] {
  [Symbol(nodejs.util.promisify.custom)]: [Function]
 }
}
{ hoge: [Function: hoge] }

C:\tmp>
 
・サンプルコード(Strictモード)
'use strict';
function hoge() {
 console.log(this); // 関数を呼び出しているオブジェクトを参照
}
hoge(); // グローバルオブジェクトの関数を実行
let hello = {}; // 独自のオブジェクト定義
hello.hoge = hoge; // 独自のオブジェクトに関数を定義
hello.hoge(); // 独自のオブジェクトの関数を実行
 
・実行結果(Strictモード)
C:\tmp>node hello.js
undefined
{ hoge: [Function: hoge] }

C:\tmp>
 
3.将来(最新)のJavaScriptの仕様に対応する
例えば、キーワード「private」は、2021/02/21現在でいくつかのブラウザやNode.jsでは使用可能です。
非Strictモードではこのキーワードを変数名として使用できますが、Strictモードではエラーにできます。
 
・サンプルコード(非Strictモード)
// 'use strict';
let private = "Hello world!";
console.log(private);
 
・実行結果(非Strictモード)
C:\tmp>node hello.js
Hello world!

C:\tmp>
 
・サンプルコード(Strictモード)
'use strict';
let private = "Hello world!";
console.log(private);
 
・実行結果(Strictモード)
C:\tmp>node hello.js
C:\tmp\hello.js:2
let private = "Hello world!";
^^^

SyntaxError: Unexpected strict mode reserved word
  at wrapSafe (internal/modules/cjs/loader.js:1053:16)
  at Module._compile (internal/modules/cjs/loader.js:1101:27)
  at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
  at Module.load (internal/modules/cjs/loader.js:985:32)
  at Function.Module._load (internal/modules/cjs/loader.js:878:14)
  at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
  at internal/main/run_main_module.js:17:47

C:\tmp>

ブラウザで表示される文字列をJavaScriptのURLを読ませて置換する

ブラウザで表示される文字列について、お気に入りからJavaScriptを読みこませることで置換することができます。
例えば、強調表示したいような場合にこれを活用できます。
 
例として、mozillaJavaScriptのページを強調表示してみましょう。
https://developer.mozilla.org/ja/docs/Web/JavaScript
 
今回の例では、「JavaScript」と「ECMAScript」を強調表示します。
 
1.お気に入りにJavaScriptを登録する
Chromeの場合は以下のような手順になります。
 
・ブックマークマネージャーを開く

f:id:akira2kun:20210214180437j:plain
 
 
・右クリックで新しいブックマークを作成する

f:id:akira2kun:20210214180525j:plain
 
・ブックマークのURLにJavaScriptを記述する

f:id:akira2kun:20210214180541j:plain
javascript:(function(){document.body.innerHTML=document.body.innerHTML.replace(/JavaScript/g,'[JavaScript]');document.body.innerHTML=document.body.innerHTML.replace(/ECMAScript/g,'[ECMAScript]');})()
 

2.任意のページでブックマークを開く
今回の例では、このようなページが

f:id:akira2kun:20210214180622j:plain

 
ブックマークを開くことでこのように強調表示されます。

f:id:akira2kun:20210214180647j:plain

 

----
 
このように手軽にブラウザの表示の文字列置換が可能なのですが、この方法では大量の文字列置換ができないことに注意する必要があります。
少なくとも、筆者の環境(OS:Windows8.1、ビット数:64、メモリ:8GB、プロセッサ:Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz 2.40GHz、Chromeバージョン: 88.0.4324.150)では、1000通りの文字列置換を行うことはできませんでした。
(恐らく、ブラウザ毎に設けられているURLの文字数制限によるものです)
これが問題になるのであれば、Chrome拡張機能を開発する等の別の方法を考えた方が良いです。
 
また、文字列置換によりリンク先が変わってしまうことにも注意が必要です。
(逆に、これを利用して、意図的にリンク先を変えることはできそうです)

Google翻訳を利用した英文コミュニケーション

国内で開発作業に従事している場合でも、国外の技術者とのやりとりが必要になるケースがあります。
特に理系大卒の技術者は英語力に自信が無い場合が多い(理系大卒の新卒時点でのTOEICの平均スコアは400点台)のですが、現在はGoogle翻訳という文明の利器があります。
高校の基礎レベルの文法を理解していれば、Google翻訳を使いつつチャットでのコミュニケーションを成り立たせることができます。
 
通訳を通してコミュニケーションを取るでも良いのですが、通訳を通すとどうしてもスピード感が失われたり認識相違が発生しやすくなったりするので、可能な限り直接やりとりするのが望ましいです。
 
----
 
googleで「日本語 英語」と検索すれば、以下のようにGoogle翻訳の画面を表示することができます。

f:id:akira2kun:20210214172419j:plain

----
 
多くの場合、Google翻訳は日本語を英語に正しく翻訳してくれるのですが、入力する日本語を工夫したり翻訳結果を手直したりした方が望ましい場合があります。
具体的には、以下のような場合に注意する必要があります。
(ここで挙げる以外にも、稀に文法的に誤った翻訳が出力される場合があります。その場合は、翻訳結果を自分で修正する必要があります。)
 
1.主語が省略される場合
日本語は主語が省略される場合が少なくないのですが、英語の場合は原則として主語を省略しません。
そのため、主語のない日本語をGoogle翻訳にかけると、主語が補われます。
 
例えば、
「今日は18:00にリリース作業を行う予定です。」

「We are planning to release it at 18:00 today.」
と翻訳されます。
 
しかし、話の前後で、「We are」(私たち)ではなく「I am」(私)が正しい場合があります。その場合は自分で翻訳結果を修正する必要があります。
また、日本語を入力する時点で主語を省略しないという手段も有効で、例えば
「私は今日は18:00にリリース作業を行う予定です。」
であれば
「I plan to release it at 18:00 today.」
という翻訳になります。
 
2.1文1文で表現を統一したい場合
同じ意味の文章でも、1文1文で異なる表現を用いて翻訳される場合があります。
文章の前後で表現に一貫性を持たせるために、同じ意味の文章なのであれば同じ表現を用いるように翻訳結果を修正した方が、認識相違は少なくなるでしょう。
 
例えば、
「私達は1つのサーバーが止まるケースを対策しなければなりません。私達はトラフィック過多の対策も必要です。」

「We have to deal with the case where one server goes down. We also need measures against excessive traffic.」
と翻訳されます。
 
「対策」という言葉が「deal with」と「measures against」と異なる表現で翻訳されているので、どちらかの表現に統一した方が良いでしょう。
また、日本語を入力する時点で使ってほしい表現を英語で指定する方法もあり、例えば
「私達は1つのサーバーが止まるケースを対策しなければなりません。私達はトラフィック過多をdeal withするのも必要です。」
であれば
「We have to deal with the case where one server goes down. We also need to deal with over-traffic.」
と翻訳されます。
 
----
 
一方、英語から日本語に翻訳する場合は、不自然であったり意味が通じなかったりする翻訳結果になることが少なくないように思います。
 
団体から出している正式な文章であれば問題無いのですが、チャットでやりとりする文章では、くだけた表現が使われたり、文法的に正確ではない表現が使われたりすることがります。
例えば、これは私の趣味(対戦ゲーム)で外国人のプレイヤーとやりとりしている中で実際に使われた文章なのですが、
「I played badly I'll probably lose this one」

「私はひどく遊んだ私はおそらくこれを失うでしょう」
と翻訳されました。
これは「私は悪手を打った。私は恐らくこのゲームで負けるだろう。」と翻訳するのが正しいのですが、ピリオドで文が区切られていないため、また「this one」を正しく翻訳することが難しいため、このような翻訳結果となってしまっています。
ある程度の読解力がないとこのようなケースに対応できないこともあるのですが、技術者の場合は英語のエラーメッセージを読んだり海外のドキュメントを読んだりせざるを得ないことが多いので、実務に従事し続けていれば自然に読解力は身につくと思います。
 
その他、専門用語が出てくる場合や、略語(スラング)が使われる場合(「asap」のようなビジネスで使い古されている表現なら対応できますが、そうではない場合は対応できない場合が多い)は、Google翻訳では対応が難しいです。
この場合は、わからない表現だけWebで調べたり、言葉の意味をチャットで質問したりすると良いでしょう。

サクラエディタの置換機能を使った簡単な独自ソート

サクラエディタの機能として昇順・降順ソートが用意されていますが、それ以外の独自の条件でソートを行いたい場合、置換機能を上手く使うことでソートできる場合があります。
改行コードを一時的に置き換えて1行のファイルにするのと、参照機能で並び替えを行うのがポイントです。
マクロとして保存すれば、繰り返し実行することもできます。
 
本当はjavaC#等のプログラムでソートするべきだとは思いますが、障害対応等で急ぎで作業する必要がある場合に使えるテクニックです。
 
----
 
例えば、下記のCSVファイルについて、2項目目が"END"のレコードを一番最後のレコードに持って行きたいとします。
 
1,DAT
2,DAT
3,END
4,DAT
5,DAT
 
その場合、下記のように置換すると、上手くソートできます。
(改行コードはCRLFとします)
 
1.改行コードを独自の文字(ファイル中に出てこない任意の文字)に置き換える
置換前:\r\n
置換後:@
 
1,DAT@2,DAT@3,END@4,DAT@5,DAT@
 
2.参照機能により、"3,END@"を一番後ろに移動する
置換前:(.+@)(.+,END@)(.+@)
置換後:${1}${3}${2}
 
1,DAT@2,DAT@4,DAT@5,DAT@3,END@
 
3.2の操作について、"END"のレコードが一番最初にきた場合の考慮でもう一度置換
(2の操作だと"END"のレコードが一番最初にきた場合に置換条件を満たさない)
置換前:(.+,END@)(.+@)
置換後:${2}${1}
 
1,DAT@2,DAT@4,DAT@5,DAT@3,END@
 
4.独自の文字を改行コードに戻す
置換前:@
置換後:\r\n
 
1,DAT
2,DAT
4,DAT
5,DAT
3,END

テレワークにおけるコミュニケーションのコツ

コロナ渦の影響で、テレワークが流行っています。
テレワークではコミュニケーションの取り方が変わるため、チャットでのやりとりに慣れていない技術者がコミュニケーションの取り方に悩むという姿を目にします。
 
先日、相談に乗る機会があったので、それを元にうまくコミュニケーションを取るコツを書いていきたいと思います。
 
1.姿が見えない環境であることを意識する
 テレワークでは相手の姿を見ずにコミュニケーションを取ることが多いです。
 そのため、表情やしぐさを読み取り円滑にやりとりするということができません。
 友好的な態度、意欲的な態度、切迫した状況、等はチャット上で示す必要があります。
 
 友好的な態度は、顔文字や絵文字を使い表情を豊かにすることで示すことができます。
 例えば、Slackでは顔文字や絵文字が登録されています。
 登録されている顔文字や絵文字であれば、使って問題無いと思います。
 雰囲気次第では、ネットスラングを使っても良いかもしれません。
 これらのことは、TwitterのようなSNSで慣れておくと勉強になると思います。
 
 意欲的な態度は、文字で示すしかありません。
 対面のコミュニケーションとは異なり、文字に起こさないと察してくれません。
 仕事が欲しい場合は、手が空いたから仕事が欲しい、と明確に書く必要があります。
 そこで作業が無いと言われた場合は、自分で作業を提案する必要があります。
 (ドキュメントの改善、自分が携わるシステムの勉強、等)
 
 切迫した状況を伝えるには、現在の状況や作業の期限を明確に書く必要があります。
 音声でチャットしたい、と書くだけでも暗に急ぎであることを示すことができます。
 音声チャットは、相手の時間を取らせる代わりに、早く要件を終わらせられます。
 
2.どれが自分の書き込みなのかを認識しやすいようにする
 リアルのコミュニケーションでは顔や声で個人を認識できます。
 しかし、文字だらけのチャットでは、すぐに個人を認識できない場合があります。
 これはコミュニケーションを難しくさせ、時に認識齟齬の原因にもなり得ます。
 
 そのため、チャットツールのアイコンやアバターを設定すると良いです。
 アイコンやアバターにより、視覚的に個人を認識することができるようになります。
 
3.スルーされても気にしない
 リアルのコミュニケーションでは、声をかければ何かしら反応が来ます。
 しかし、テレワークでは、チャットに書き込んでも長時間反応がないことがあります。
 これは不安に思うかもしれませんが、多くの場合は問題ありません。
 システム開発は、作業の性質上、作業に没頭する時間が必要です。
 作業に没頭している時はチャットに気付きにくくなります。
 そのため、長時間反応がなければ、作業に没頭しているだけだと思いましょう。
 
 本当に急ぎの時は、チャットとは別ルートで通話を申し込むと良いです。
 逆に言うと、作業に没頭する時も、別ルートの申し込みはチェックするべきです。

カプセル化によるルールの強制

オブジェクト指向を適用すると、ソースコードの重複した記述を排除でき、生産性や品質を向上することができます。
それとは別に、他の開発者にルールを強制できるメリットもあります。
カプセル化を例にして説明するのがわかりやすいので、今回はカプセル化を例に挙げてルールを強制するメリットを書いていきます。
 
----
 
例えば、自分は商品の金額を計算するPriceCalcクラスを作成していて、他の開発者はPriceCalcクラスを利用するUserクラスを作成しているとします。
自分としては「このロジックで金額計算して欲しい」というのがあるのですが、他の開発者にそのロジックの実装をさせるようにしてしまうと、意図が上手く伝わらなかった時に誤ったロジックで計算されてしまいます。
この状況をソースコードで表すと下記のような形になり、PriceCalcクラスは計算に必要な変数をまとめるだけ、PriceCalc側でその変数を参照して独自に業務ロジックを実装するような形となります。
 
public class PriceCalc {

  public ArrayList<Integer> costs = new ArrayList<Integer>();
  public int taxRate = 0;
  :
  : 色々なメンバ変数定義
  :

}
 
public class User {

  PriceCalc priceCalc = new PriceCalc();
  :
  : priceCalcオブジェクトのメンバ変数を使った複雑な業務ロジック
  :
  
}
 
----
 
それでは困るので、業務ロジックもPriceCalcクラス側に実装することにします。
変数とメソッドをひとまとまりにして一つのクラスとして提供することで、Userクラス側ではメソッドを呼び出すだけであるべき業務ロジックに基づいた計算を行うことができ、Userクラス側で業務ロジックを意識する必要が無くなります。
 
しかし、メンバ変数がpublicだと、Userクラス側でその変数を参照して独自に業務ロジックを実装することもできてしまいます。
自分としてはそれでは困るので、「変数を参照して独自に業務ロジックを実装しないでください」と伝えるのですが、それが上手く伝わらないと独自に業務ロジックを実装されてしまう可能性があります。
自分一人でプログラムを作っている場合は全ての変数をpublicにした方が便利だったりもしますが、複数人で開発している場合は意思疎通がうまく行かなかった場合に意図通りではないコーディングをされてしまうリスクがあります。
 
public class PriceCalc {

  public ArrayList<Integer> costs = new ArrayList<Integer>();
  public int taxRate;
  :
  : 色々なメンバ変数定義
  :
  
  public void registNewCost(int cost) {
    this.costs.add(cost);
  }
  :
  : メンバ変数の設定用メソッド
  :
  
  public int getPrice(int index) {
    :
    : 複雑な業務ロジック
    :
  }

}
 
public class User {

  PriceCalc priceCalc = new PriceCalc();
  :
  : PriceCalcクラスのメソッドを使った設定
  :
  int priceOk = priceCalc.getPrice(index);
  
  // しかし用意したメソッドを使用せずに独自で計算できてしまう
  int priceNg = priceCalc.costs.get(index) * priceCalc.taxRate;
  
}
 
----
 
それを防ぐためには、変数のアクセスレベルをprivate等にして、変数に自由にアクセスできないようにするのが有効です。
こうすることで、変数を参照して独自に業務ロジックを実装しようとされた場合に、コンパイルエラーとして機械的に失敗させることができます。
これが、カプセル化によるルールの強制であり、意思疎通がうまく行かなかった場合に意図通りではないコーディングをされるリスクを減らすことができます。
 
public class PriceCalc {

  private ArrayList<Integer> costs = new ArrayList<Integer>();
  private int taxRate;
  :
  : 色々なメンバ変数定義
  :
  
  public void registNewCost(int cost) {
    this.costs.add(cost);
  }
  :
  : メンバ変数の設定用メソッド
  :
  
  public int getPrice(int index) {
    :
    : 複雑な業務ロジック
    :
  }

}
 
public class User {

  PriceCalc priceCalc = new PriceCalc();
  :
  : PriceCalcクラスのメソッドを使った設定
  :
  int priceOk = priceCalc.getPrice(index);
  
  // 下記はコンパイルエラー
  // int priceNg = priceCalc.costs.get(index) * priceCalc.taxRate;
  
}

JavaScript:matchメソッドで文字列抽出を行う

JavaScriptのmatchメソッドは戻り値に特徴があり、戻り値を利用して文字列抽出もできるようになっています。
今回は、文字列抽出の方法を紹介していきます。
 
----
 
matchメソッド(String.match)では、引数で与えた文字列が含まれるかどうかを返すメソッドです。
引数で与える文字列には、正規表現を使用することも可能です。
 
引数で与えた文字列が含まれない場合には、以下のようにnullが返却されます。
 
【サンプルコード】
・test.js
const str = 'hoge1fuga2';
const ret = str.match(/piyo/);
console.log("■戻り値全体を参照");
console.log(ret);
 
【実行結果】
c:\tmp>node test.js
■戻り値全体を参照
null
 
----
 
含まれる場合は、以下のように色々な情報が返却されます。
配列で返却され、一致した箇所を抜きだした文字列が添字0の要素に格納されます。
また、indexキーには一致した文字列が何文字目に存在するか、inputキーには走査対象とする元々の文字列が格納されます。
groupsキーについては後述します。
 
【サンプルコード】
・test.js
const str = 'hoge1fuga2';
const ret = str.match(/fuga/);
console.log("■戻り値全体を参照");
console.log(ret);
console.log("■一致した文字列のみ参照");
console.log(ret[0]);
 
【実行結果】
c:\tmp>node test.js
■戻り値全体を参照
[ 'fuga', index: 5, input: 'hoge1fuga2', groups: undefined ]
■一致した文字列のみ参照
fuga
 
----
 
matchメソッドの引数では、gオプションを指定することが可能です。
gオプションを指定すると、一致する文字列を見つけても走査を続け、一致する文字列を全て抜き出すという動きになります。
戻り値の形式もgオプション指定時には変更され、以下のように一致した全ての文字列が添字0の要素、添字1の要素…に格納されます。
 
【サンプルコード】
・test.js
const str = 'hoge1fuga2';
const ret = str.match(/[0-9]/g);
console.log("■戻り値全体を参照");
console.log(ret);
console.log("■1つ目の一致した文字列のみ参照");
console.log(ret[0]);
 
【実行結果】
c:\tmp>node test.js
■戻り値全体を参照
[ '1', '2' ]
■1つ目の一致した文字列のみ参照
1
 
----
 
matchメソッドの引数で使用できる正規表現には、キャプチャグループというものが存在します。
キャプチャグループとは、"()"で囲った箇所をグループ化するというもので、抽出条件には影響しませんが、抽出結果をグループ分けすることができます。
キャプチャグループが存在する場合は、添字1の要素に1つ目のキャプチャグループ、添字2の要素に2つ目のキャプチャグループ…といった具合に、戻り値でキャプチャグループの箇所を抽出することができるようになります。
キャプチャグループを使用することで、柔軟な文字列抽出が可能になります。
 
【サンプルコード】
・test.js
const str = 'hoge1fuga2';
const ret = str.match(/(fuga)([0-9])/);
console.log("■戻り値全体を参照");
console.log(ret);
console.log("■2つ目のキャプチャグループのみ参照");
console.log(ret[2]);
 
【実行結果】
c:\tmp>node test.js
■戻り値全体を参照
[
 'fuga2',
 'fuga',
 '2',
 index: 5,
 input: 'hoge1fuga2',
 groups: undefined
]
■2つ目のキャプチャグループのみ参照
2
 
----
 
キャプチャグループには名前を付けることができます。
"(...)"を"(?<hoge>...)"とすることで、名前を付けることができます。
"<"と">"で囲った箇所が、キャプチャグループの名前になります。
名前付きキャプチャグループを使用している場合は、戻り値のgroupsキーで抽出が可能になります。
 
【サンプルコード】
・test.js
const str = 'hoge1fuga2';
const ret = str.match(/fuga(?<digit>[0-9])/);
console.log("■戻り値全体を参照");
console.log(ret);
console.log("■名前付きキャプチャグループのみ参照");
console.log(ret.groups.digit);
 
【実行結果】
c:\tmp>node test.js
■戻り値全体を参照
[
 'fuga2',
 '2',
 index: 5,
 input: 'hoge1fuga2',
 groups: [Object: null prototype] { digit: '2' }
]
■名前付きキャプチャグループのみ参照
2