はじめに
JavaScriptにおけるreplace関数は、単純な文字列置き換えの他、replacer用のfunctionを指定することで、柔軟な動作を実現することができます。
今回は、replace関数を使ってプレースホルダの置換処理を書いてみます。
プレースホルダとは
JavaScriptのテンプレート文字列や、シェルにおける${var}など、変数の中身を展開(置換)することのできる文字列をプレースホルダと呼びます。
今回は、任意のデータを文字列上に展開できるようなプログラムを書いてみましょう。
やりたいこと
ユーザーが入力した文字列に変数を展開する。
設計
大まかなデータと方式の設計を行いましょう。
変数はJSONとしてMapで持つ
let variables = {
"name": "John",
"age": 20
}
プレースホルダ文字列は、「{{key}}」とする
よくあるやつです。
let placeHolderString = "I'm {{name}}. {{age}} years old.";
実装
何も考えずにやるなら、以下のようなコードが考えられます。
let variables = {
"name": "John",
"age": 20
}
let placeHolderString = "I'm {{name}}. {{age}} years old.";
let replaced = Object.keys(variables).reduce((acc, key)=>acc.replace("{{" + key + "}}", variables[key]), placeHolderString);
console.log(replaced); // -> I'm John. 20 years old.
変数のキーに対してeachを掛け、順次replace処理を施しています。
問題点
以下のような過大な変数群を、ひとつのプレースホルダに適用する場合を考えてみましょう。ついでにパフォーマンスも計測しておきます。
let variables = {};
for(let i = 0; i < 100000; i++){
variables[`user-${i}`] = Math.random().toString(36).slice(-8);
}
console.time();
let placeHolderString = "hello, {{user-1000}}.";
let replaced = Object.keys(variables).reduce((acc, key)=>acc.replace("{{" + key + "}}", variables[key]), placeHolderString);
console.log(replaced); // -> hello, hhn5ylan.
console.timeEnd(); // -> default: 91.742919921875ms
ちなみに、
let replaced = Object.keys(variables).reduce((acc, key)=>acc.replace("{{" + key + "}}", variables[key]), placeHolderString);
は、以下のコードと同義です。
let replaced = placeHolderString;
Object.keys(variables).forEach(key=>{
replaced = replaced.replace("{{" + key + "}}", variables[key]);
});
これらは、オブジェクトの中身を全て処理するため、かなり遅いです。
replacer(function)を使う
そこで、今回の本題であるreplacer(第二引数のfunction)を使ってみます。
let variables = {};
for(let i = 0; i < 100000; i++){
variables[`user-${i}`] = Math.random().toString(36).slice(-8);
}
console.time();
let placeHolderString = "hello, {{user-1000}}.";
let replaced = placeHolderString.replace(/\{\{(.*?)\}\}/g, (match, p1)=>{
return variables[p1];
});
console.log(replaced); // -> hello, i24g408u.
console.timeEnd(); // -> default: 0.427001953125ms
for文ループがなくなったため、91.7ms -> 0.42msと、かなり高速化しました。
まとめ
本例は、foreach(reduce)の場合も、replace functionを使う場合も、まったく同じ結果をもたらします。変数が少なければ問題になりませんが、処理ひとつひとつのコストを常に考えることで、将来起こりうるパフォーマンス問題を最小限に抑えることができます。
考え付いたまま、またはネットで拾ってきたソースコードをそのまま貼り付けて使っていたりすると、このような問題を容易に引き起こします。熟考とまでは行かずとも、意識したプログラミングを行うことが大切です。