String.replaceで知らなかった件

User がノート欄にやたら長いリンク先をそのまま張り付けて、SharePointのリストの表示が著しく損なわれるという現象が自分の管理するサイトコレクションで何件か発生している。 ユーザー教育を行ってリンクの説明を別途短文入力してください、というのは簡単だが、だまって従ってくれるような従業員ばかりではない。 そこで長いリンクを見つけたら強制的に短い文字列に置き換えてしまう、という仕掛けをJavaScriptで書いてしまえばよいのだと思いつき、 試してみた。 結果、以下のような関数を記述し、これを描画時に呼び出す形で解決した。

function shortenAnchor(text) {

    if (text===null) {
        return "";
    }
    let testPattern = /(<a.*?href=.+?>)(.*?)(<\/a?>)/g;

    function replacer(match, p1, p2, p3) {
        if((p2.length)>30) {
            p2 = p2.slice(0, 25) + '...';
        }
        return [p1, p2, p3].join('');
    }

    return text.replace(testPattern, replacer);
}

このコードで何をやっているか。 まずtestPatternを正規表現(RegExp)で記述する。 RegExpで a tag elements をグローバルにトラップする。トラップしたエレメントはタグの前の部分、中身のテキスト部分、タグの後ろの部分にグループわけされ、それぞれの文字列は$1,$2,$3として利用可能になる。( a tag element 全体のマッチは$&)。 そして、あとは String.replaceを使ってマジックをおこすわけだが、Implementationを試行錯誤している最中に 二つ目のパラメーターが実は関数でもよいのだ、というのを本日初めて知ったわけで、それを使ってうまく書くことができた。 ここに関数を使うと、以下の引数が この順番で使えるようになる。 まずは TestPatternそのもののマッチ $&, それぞれグループ分けした部分のマッチ $1,$2,$3。 よって match,p1,p2,p3として利用可能にしておく。実はこの後ろにIndexなどのパラメーターも続くのが、今回は必要がないので無視。 
この replacer という関数ではこの中身の文字列$2(p2)の長さが30文字以上あった場合、最初の25文字以下を切り捨て点三つ…を最後に追加するというもの。 この関数自体を二つ目の引数として使う。 Testしてみたら一発で完動したので感動(おやじギャグです)

普通はString.replaceというと

"this is a pen".replace("this","that") //=>"that is a pen"

という簡単な使い方は知っていたし、 最初のパラメーターを正規表現にすれば $& $1 etc., などのパラメーターを使ってかなり柔軟な置き換えなどができることも知っていて使ってはいたのだが、まさか関数まで使えるとは思っていなかった。 これは素晴らしい、と思った次第