
ティラノ2作目進捗+工夫的な何かについて
処女作をビルダーで作ってたのに、時間開けて作り出した2作目でスタジオに移行してる件(単純に最新スクリプトが使える&個人的なコーディング能力UPのせい)
1.まず進捗
現在テストプレイヤー約1名にプレイ依頼中です。
まあ、ランダム要素絡む部分は全部私がデバッグでつぶしたんで、「純粋に好きにプレイしてください」と伝えております。
期日的にもね、別に2025年内リリースのつもりでおります故、そんな急いでないよ~と伝えております。
なお、現状確認してもらったバグは全部つぶしたので、処女作のリメイクをいきなりしたり、通算三作目をごりごり作ってたりするのだ。
2.内容
牢屋で目を覚ました記憶のない少女が、なんかよくわかんない胡散臭いやつとか、「こいつは信頼できそう」ってやつの手を借りて、自分の記憶を思い出すために進んでく、ポイントクリックADVなゲーム。謎解き要素とかもあるよ!



一周目は必ず固定エンドだけど、それ回収すると他エンドへ行くためのフラグが立ちます。
収集要素も何故かめちゃくちゃ用意してあって、BGMとー、背景とー、スチルとー、キャラとー、あと知識系補足とー……ノベコレさんのバッジが5までだと知らなくて作り過ぎた結果、ゲーム内バッジとなったやつら(同時にノベコレさん登録状況下でなくてもバッジを確認できるようにしてあるよ)
ちなみに、スチルは普通に全エンド回収すれば全部回収できる。背景は、ちょっと行動の順番を前後させないと一度では取得できないものがある。キャラはおまけまでやって初めてコンプになるかな?

これの上にクリッカブルオブジェクト配置する方式ですん。
なお、BGMと知識系補足がランダム要素絡む運ゲーでござる。
そしてBGMは全部絢芽ちゃん珠玉のクラシックピアノコレクションより使用しております、うふふ……マイナー小品の聴かせどころやでえ(つまり布教)って顔してるので無視していいです。いや、このゲームやったらエドワード・マクダウェルの名前はせめて覚えてほしい無視してどうぞ。
この収集要素やエンド回収フラグは「すべてシステム変数で管理してる=一回でも回収すれば全データに反映される」ので、その点は安心設計だと思っていただきたい……。
3.プログラミング的工夫(人によってはティラノスクリプトにおけるJavaScriptでの配列・JSON形式取り扱い講座)
とりあえずは収集要素のフラグ管理ですね、ごりっごりのJSONと配列で行っております。
以下は初回起動時の変数初期化処理に含まれる収集要素のフラグの初期化処理。
sf.collect_item={};
sf.collect_item.music= [...Array(42)].map(() => false);
sf.collect_item.back=[...Array(69)].map(() => false);
sf.collect_item.still=[...Array(24)].map(() => false);
sf.collect_item.tips=[...Array(211)].map(() => false);
sf.collect_item.chara=[...Array(20)].map(() => false);
sf.collect_item.badge=[...Array(15)].map(() => false);
sf.collect_item.chk={"music":false,"back":false,"still":false,"tips":false,"chara":false,"chk":false};
sfはティラノのシステム変数の格納場所なのでいいとしてそこはまずいいとして、上から順に
システム変数内に「collect_item」っていう名前の空のJSON({})を格納する(最初に「こいつJSON」ってしとかないとこの後の処理でエラーが発生する)
「collect_item」の中の「music(音楽)」って部屋の中に、「部屋が42個ある配列全部にfalse詰めて格納する([...Array(42)].map(() => false))ってやってます。まあ配列って0始まりだからこの場合、0~41番の部屋全部にfalseが入った配列を作っているのだ( 「sf.collect_item.music[0]」で0番目の部屋が見れる)
で、2と同じように、「collect_item」の中に「back(背景)」、「still(スチル)」、「tips(知識系補足)」、「chara(キャラ)」、「badge(バッジ)」をそれぞれ用意してる要素分の部屋数の配列にfalse詰めて格納してます(これでわかる知識系補足量のやばさ)
最後の「chk」は全要素収集チェック用の「どの要素が全回収されてるかフラグ」のオブジェクトなので、それぞれの収集要素に合わせて変更してます。
【ちょっと専門的な話わからない人向け】
配列とか、JSONオブジェクトがよくわかんないよって人に言うと……まあ、そもそもティラノの一時変数(tf.任意の名前)、セーブデータ変数(f.任意の名前)、システム変数(sf.任意の名前)が全部JSONではあるんだけど……
まず、「配列」というのは0始まりの番号で管理される部屋を持った変数で、JavaScriptではどの部屋にどの形式のデータを入れても良いものです。0番目の部屋に数値入れて、1番目の部屋に文字列入れて、2番目の部屋にJSONをぶち込んでもいいのだ。
なお配列という概念は多くのプログラミング言語に存在するが、この「JavaScriptでは一つの配列の部屋にどんな形式のデータぶっこんでもいい」が他のプログラミング言語でも共通なわけではないので、ご注意。
ただ、番号での管理だけだとわけわかめになりかねません。
そこで楽できるのが「JSON」。こいつも配列と同じように一つの変数に複数の部屋を持てます。ただし、配列と違って、部屋には「任意の名前」(専門的には「キー(key)」と呼ぶことが多い)を付けられます。
んで、参照する時は、たとえばJSONの変数の名前が「test」で部屋の名前が「a」なら、「test.a」か「test["a"]」で参照できる(後者は必ず文字列として扱う必要があるので、部屋の名前をダブルかシングルのクォーテーションで囲む必要があります)
……という説明をすると、最初に言った「ティラノの一時変数(tf.任意の名前)、セーブデータ変数(f.任意の名前)、システム変数(sf.任意の名前)がそもそもJSONである」、という話もピンとくるのではないけ?
そして、JSONと配列どちらにも共通することで、「入れ子にすることができる」。つまり、配列の部屋の中に配列作ったり、JSONの部屋の中にJSON作ったり、配列の部屋の中にJSON作ったり、JSONの部屋の中に配列作ったりできるわけです。
なので、
//-------------------------------------------------------------------------------
//配列変数arr宣言
var arr = [];
//-------------------------------------------------------------------------------
//配列内配列の扱い方--------------------------
//配列変数arrの中に0の部屋にa、1の部屋にb(以下略)が格納された配列を入れる
arr[0] = ["a", "b", "c", "d"];
//コンソールに配列変数arrの0の部屋の中の配列の0の部屋=aを表示させてみる
console.log(arr[0][0]);
//配列内JSONの扱い方--------------------------
//配列変数arrの1の部屋にJSON(aに1、bにtestを格納)を入れる
arr[1] = {"a":1,"b":"test"};
//コンソールに配列変数arrの1の部屋の中のJSONのaの部屋=1を表示させてみる
//この時、「console.log(arr[1].a);」でも同じ動きをする
console.log(arr[1]["a"]);
//-------------------------------------------------------------------------------
//JSON変数jobj宣言
var jobj={};
//-------------------------------------------------------------------------------
//JSON内JSONの扱い方------------------------------------------
//JSON変数jobjのtest1という名前の部屋に
//(宣言時に「{}」と空のJSONを格納しているので、存在してなくても書いた瞬間にできあがる)
//別のJSONを格納する。
jobj.test1={"test1_1":1, "test1_2":2,"test1_3":3,"test1_4":4};
//コンソールにJSON変数jobjのtest1の部屋の中の
//JSONのtest1_1の部屋=1を表示させてみる
console.log(jobj.test1.test1_1);
//以下の書き方でも同じ動きをするよ
console.log(jobj["test1"].test1_1);
console.log(jobj.test1["test1_1"]);
console.log(jobj["test1"]["test1_1"]);
//JSON内配列の扱い方-------------------------------------------------------
//JSON変数jobjのtest2という名前の部屋に
//(宣言時に「{}」と空のJSONを格納しているので、存在してなくても書いた瞬間にできあがる)
//配列を格納する。
jobj.test2 = ["a", "b", "c", "d"];
//コンソールにJSON変数jobjのtest2の部屋の中の
//配列の0の部屋=aを表示させてみる
console.log(jobj.test2[0]);
//以下の書き方でも同じ動きをするよ
console.log(jobj["test2"][0]);
以上のように扱えるってわけ(console.logの()内の書き方で対象の場所が参照できるってわけなので参考にしてくりゃれ)
ただ、JSONの中身を取り出しといて、後々取り出した先で値を変更するって場合にはちょっと注意が必要になります(後述するので忘れないでね)
【ちょっと専門的な話わからない人向けここで終わり】
まあ、この処理の意味がわかる人&説明を理解していただけた方は次にこう思うと思うんだ。
「お前、どうやって配列の部屋と要素紐づけてんの?」、と。
答えはばりくそシンプルです。Excel。

A列はデバッグの痕跡。B列が配列の部屋の番号と一致する。
この場合のExcelくんの便利なところは、見てもらうとわかるんだけど列にタイトルとかついてたり、E列とかなんかよくわかんない痕跡になってるわけなんですが(これもデバッグ用に作った連結文字列ではある)
コレクションルームでボタンを作って、さらにはそのボタンと紐づけてファイル情報を引っ張り出す時に使用するJSONやら配列やらの元を、Excelの文字列連結使ってそこまで苦労せずに作れるという。
なんなら、量が多いので、システム変数(sf)に「const(=定数)」って部屋作っといて(他にもこれで管理してるのはある)、ゲーム内ではそれで管理するという……
//backpage配列の番号がページ数と一致
sf.const.backpage[1][0]={y:"130",x:"55" ,"txt":"初期タイトル画面","file":"Title_NE.png","hint1":"","hint2":"","ex":"f.selback='0';f.selbackfile='Title_NE.png';f.selbtnnum=0","num":"0"};
sf.const.backpage[1][1]={y:"130",x:"345","txt":"地下牢","file":"Scene1.png","hint1":"","hint2":"","ex":"f.selback='1';f.selbackfile='Scene1.png';f.selbtnnum=1","num":"1"};
sf.const.backpage[1][2]={y:"130",x:"635","txt":"蝋燭の洞窟1","file":"Scene2-1.png","hint1":"","hint2":"","ex":"f.selback='2';f.selbackfile='Scene2-1.png';f.selbtnnum=2","num":"2"};
sf.const.backpage[1][3]={y:"130",x:"925","txt":"蝋燭の洞窟2","file":"Scene2-2.png","hint1":"","hint2":"","ex":"f.selback='3';f.selbackfile='Scene2-2.png';f.selbtnnum=3","num":"3"};
sf.const.backpage[1][4]={y:"195",x:"55" ,"txt":"鳥の森1","file":"Scene3-1.png","hint1":"","hint2":"","ex":"f.selback='4';f.selbackfile='Scene3-1.png';f.selbtnnum=4","num":"4"};
sf.const.backpage[1][5]={y:"195",x:"345","txt":"鳥の森2","file":"Scene3-2.png","hint1":"","hint2":"","ex":"f.selback='5';f.selbackfile='Scene3-2.png';f.selbtnnum=5","num":"5"};
sf.const.backpage[1][6]={y:"195",x:"635","txt":"鳥の森3","file":"Scene3-3.png","hint1":"","hint2":"","ex":"f.selback='6';f.selbackfile='Scene3-3.png';f.selbtnnum=6","num":"6"};
sf.const.backpage[1][7]={y:"195",x:"925","txt":"妖精の森","file":"Scene4.png","hint1":"","hint2":"","ex":"f.selback='7';f.selbackfile='Scene4.png';f.selbtnnum=7","num":"7"};
sf.const.backpage[1][8]={y:"260",x:"55" ,"txt":"船の上","file":"Scene5-1.png","hint1":"","hint2":"","ex":"f.selback='8';f.selbackfile='Scene5-1.png';f.selbtnnum=8","num":"8"};
sf.const.backpage[1][9]={y:"260",x:"345","txt":"船の上2","file":"Scene5-1-1.png","hint1":"1周目エンド後、再度ゲームをはじめから行うと……","hint2":"","ex":"f.selback='9';f.selbackfile='Scene5-1-1.png';f.selbtnnum=9","num":"9"};
sf.const.backpage[1][10]={y:"260",x:"635","txt":"学校1","file":"Scene5-2.png","hint1":"","hint2":"","ex":"f.selback='10';f.selbackfile='Scene5-2.png';f.selbtnnum=10","num":"10"};
というわけで上の背景管理Excel範囲内の定数部分が上。
二重配列で外側の配列(backpage自体)はページ数と一致するようにしてある(ので0を捨ててて1から格納)
yとxはglink表示位置、txtは収集済みだった場合のglink表示テキスト、fileは対応ファイル、hint1、hint2は未収集の時に表示するヒント(フラグで1と2切替)、exがglinkのexpにセットする内容……ってな感じになっております。
ね、簡単でしょ?(簡単でしょじゃない)
ただ、これ、回収時に都度JavaScriptかexpタグ使うのかってそいつはスマートじゃねーなと思ったので、マクロ化しました。
;-コレクション追加-------------------------------------------------------------------------
;[m_add_collect]
;-------------------------------------------
[macro name="m_add_collect" ]
[iscript]
let nm=mp.name;
let i=parseInt(mp.no,10);
sf.collect_item[nm][i]=true;
[endscript]
[endmacro]
その一瞬しか使わないから、ティラノ用一時変数のtf使うよりもその場しのぎのlet宣言変数でやった方が絶対負担少ないなって絢芽ちゃん思ったわけ(JavaScriptの変数スコープ=変数有効範囲問題のお話)(これはそこまで気にしなくていいのでスルーでOK)
マクロの引数にした内容はJavaScript上だと「mp.マクロで引き渡す時の名前」(つまりマクロの引数も実はJSONなわけだ)で参照できるから、このマクロの引数は「name」と「no」。
「name」で「sf.collect_item」のどの部屋か=どの種類の収集要素か、「no」でExcelで管理してたのと一致する配列の部屋の番号を渡してあげるわけ。
ただし、ティラノのマクロでの引数は基本文字列化してしまうので、配列の部屋番号はちゃーんと数値化してあげないといけなくなる。これをしてるのが「parseInt(mp.no,10)」で、「parseInt(数値に変換したい文字列, 基数(まあ普通10進数だろうから10でOK))」で文字列から整数にするJavaScript標準処理。
で、指定した収集要素種類の中の指定した部屋の内容をtrueで更新してあげるわけなのだ。
そしてコレクションルームでボタンを表示する際には「sf.collect_item」の対象の種類の対象の部屋がtrueになってれば、ボタンがちゃんと機能するように表示するってしてるわけ。
//背景ボタン表示処理
//システム変数内の定数領域から対象ページ情報取得
f.backbtn=JSON.parse(JSON.stringify(sf.const.backpage[tf.page]));
//取得したページ情報ループでぶん回す
for(let i in f.backbtn){
//要素回収してるかチェック。できてるかどうかでクリック時の遷移先変更&ボタン表示textを変更
if(sf.collect_item.back[parseInt(f.backbtn[i].num,10)]!=true){
f.backbtn[i].txt="?";
f.backbtn[i].trg="*back_list";
}else{
f.backbtn[i].trg="*show_back";
}
//1ページ辺りのボタン表示数多いのでタグ越しよりJavaScriptから直に呼びだした方が
//処理が速いのでglink処理を直呼び出し。
tyrano.plugin.kag.ftag.startTag("glink",
{
color:"mybutton",
storage:"back.ks",
size:18,
x:f.backbtn[i].x,
y:f.backbtn[i].y,
width:250,
text:f.backbtn[i].txt,
target:f.backbtn[i].trg,
exp:f.backbtn[i].ex,
clickse:"magazine1.ogg"
}
);
}
とりあえず、対象部分だけ引っこ抜いてきて、コメント追加した。
で、ここで、先の「ちょっと専門的な話わからない人向け」の「JSONの中身を取り出しといて、後々取り出した先で値を変更するって場合」の問題があるわけなんですにゃー。
ただ、それを解決してるのが以下の冒頭処理。
//システム変数内の定数領域からページ対象ページ情報取得
f.backbtn=JSON.parse(JSON.stringify(sf.const.backpage[tf.page]));
「tf.page」はコレクションの部屋でだけ使ってる「対象種類のコレクションの何ページ目にいるか」っていう数値を格納してるだけの変数なので、これが上での、sf.const.backpageへの格納処理と対応するのですにゃー。
で、ポイントは「JSON.parse(JSON.stringify(sf.const.backpage[tf.page]))」という太字部分の処理。
ここでは対象ページの内容をsf.const.backpageから取得してきて、回収してなかったら取得してきたデータの一部を書き換えているという処理が入っています。
これが、「JSONの中身を取り出しといて、後々取り出した先で値を変更するって場合」にひっかかるわけですにゃー。
【というわけでここからちょっと専門的な話】
ちなみにそもそも「JSON.parse(JSON.stringify(sf.const.backpage[tf.page]))」って何って話なんだけど、これはJavaScriptに備わってる二つの関数を使用してます。
実行順に説明するけど、一つが内側の「JSON.stringify()」。
これを使用する時には今回「sf.const.backpage[tf.page]」を入れてるように、stringifyの後の()の中にはJSONの変数を引数として入れる必要があります。
で、こいつは本来は戻り値がある関数なので、「var test=JSON.stringify(jobj);」みたいに、戻り値を変数で受け取る、という場合もあります。ただ今回はもう一個の方の関数に直接戻り値を流してるから、受け取る必要がない。
で、そもそもこの「JSON.stringify()」くんが何をするやつなのか、と申しますと、「引数として渡したJSONをJSON文字列化した結果を戻す」という効果を持っています。
「何言ってんだ」になったところで、JSONなるものの成り立ちにも関わってくるんだがね?
そもそも、PCやスマホなどの機器での格納された値とかの参照って、人間には読みにくい形でできています。究極に突き詰めたら0/1のバイナリデータだし、それを解読できる人間は一握りなのよ。
JSONというのは、「機器として読み取りやすい」、「人間として読み取りやすい」を両立した形式(しかもデータとしても軽い)という構想のもと作成された概念です。
故に、時折、文字列でやり取りされたりするんですな。データとして吐き出したり、逆にデータとして読み込ませる時とか。
なので、JSONを文字列として書く時のルールが存在していて、
一つのJSONは「{」で始まり、「}」で終わる。
部屋名(キー)は文字列なので半角のダブルかシングルのクォーテーションで囲わなければならない。
部屋名(キー)とそこに入れる値の間には半角コロン(:)がなければならない。
複数の部屋(キー)と値の組がある場合、それを半角カンマ(,)で区切らなければならない。
格納できる値は以下の種類、かつそれぞれのルールに則さねばならない。
文字列→半角のダブルかシングルのクォーテーションで囲わなければならない。
数値→クォーテーション等で囲ってはならない。
配列→配列の開始は「[」、終了は「]」。中身の一部屋当たりの値は半角カンマ(,)で区切り、その値はそれぞれのルール(ここに書いてある内容と同じ)に則さなければならない。
JSON→新規にJSONを作ることになるので開始は「{」、終了は「}」。他はJSONの基本ルールに従う
真偽値→trueまたはfalseのどちらか。クォーテーション等で囲ってはならない。
null→完全なる空(まあ、そんなに使うことない)。クォーテーション等で囲ってはならない。
とまあ、ざっくりこんな感じ。
で、「JSON.stringify()」くんは、既にソース上でJSONとなっている変数等を引数として、このルールに則した文字列としてのJSONに変換するのです。
ここで湧いた「え?わざわざなんでそんなことしてんの?」はもう一つの方とJSONの変数の受け渡しの特性の話をすれば解決するので、一旦冷蔵庫に保管しといてください。
さて、お次は「JSON.parse()」くん。
端的に言うと、こいつは「JSON.stringify()」くんと逆。
「JSON文字列を()の中に引数として渡すと、JSONとして扱えるようにして返してくれる関数」です。
ここに「"{'testa':'test', 'testb':0}"」っていう、さっき書いたJSON文字列規則に則ったJSON文字列があるならば、「JSON.parse()」くんは以下のように処理できます。
//JSON.parse()くんでJSON文字列をJSON化
var test = JSON.parse("{'testa':'test', 'testb':0}");
//testa参照してtestをコンソールに表示
console.log(test.testa);
//testb参照して0をコンソールに表示
console.log(test["testb"]);
「JSON.parse()」くんの結果を変数testに入れてるから変数testがそのままJSONになるんだね~。
で、今回の「JSON.parse()」くんは「JSON.stringify()」くんの戻り値を使った結果を戻してて、「f.backbtn」に入れてくれてるわけです。
つまり、私がこの「f.backbtn=JSON.parse(JSON.stringify(sf.const.backpage[tf.page]))」でやってることって、
「JSONをわざわざJSON文字列に変換して、もっかいJSONとして使えるようにしてから変数に格納する」
っていう、一見すると、とんでもねー手間なんです。
でもね、これが「JSONの中身を取り出しといて、後々取り出した先で値を変更するって場合」のキモなの。
では、この「f.backbtn=JSON.parse(JSON.stringify(sf.const.backpage[tf.page]))」処理を咬ませなかった場合、どうなっちゃうか、というと……
この後の処理で、その要素を回収していなかった時、もともと「sf.const.backpage[tf.page]」にも存在している「txt」の部屋の中身を「?」に書き換えていますね?
//要素回収してるかチェック。できてるかどうかでクリック時の遷移先変更&ボタン表示textを変更
if(sf.collect_item.back[parseInt(f.backbtn[i].num,10)]!=true){
f.backbtn[i].txt="?";
f.backbtn[i].trg="*back_list";
}else{
f.backbtn[i].trg="*show_back";
}
これの3行目の「f.backbtn[i].txt="?";」ね(iはループしてる時のカウンタでその時参照してる配列の部屋の番号が入ってます)
なんと、この書き換えが「sf.const.backpage[tf.page]」側の同じ配列番号の部屋に反映されちゃうんだなー、これが(白目)
Q.なんでこんなことが起きるんじゃわれ、そこの部屋直接は書き換えとらんじゃろ?
A.JavaScriptにおけるJSON変数の受け渡しの仕様のせい。
で、ここでね、「参照渡し」と「値渡し」という概念がね、関わってきます(普通は関数周りの概念なんだけどね)
まず、通常のJavaScript変数間の受け渡しは「値渡し」です。
変数の中身を別の変数の中に直接入れます。
なので、
//変数test1宣言
let test1=1;
//変数test2宣言&変数test1の値を格納
let test2=test1;
//test2に1加算して再格納
test2=test2+1;
//変数test1をコンソールに表示→結果は1が出る
console.log(test1);
//変数test2をコンソールに表示→結果は2が出る(test1に格納した後test2だけ加算して再格納したため)
console.log(test2);
通常は上記のように、別変数に格納した段階から「変数test1の中の1」と「変数test2の中の1」は、別々の存在として扱われます。
だから「変数test2に1を加算して再格納した結果、変数test2の中の1が2に変わっても、変数test1の中の1には影響しない」、「test2=test1」で行われるのは、純粋に変数test1の中身の「1」をコピーして渡すような処理で、これを「値渡し」と呼びます。
だから、JSONにも我々は同じ期待をしてしまうわけですが……JavaScriptにおけるJSON変数の受け渡しの仕様はそれを裏切ります。何故ならJavaScriptにおけるJSON変数の受け渡しは「参照渡し」だから……。
さて、「値渡し」は説明した通り、「渡す元の側の値をコピーして相手に渡す」ものです。
「参照渡し」は、「渡す元の側の値の場所を相手に渡す」ものです……うん、よくわからないよね、わかる、わかるよ……。
さて、通常、「変数の中身というものが処理中どこにいるか」、というと、「処理実行してるパソコンや端末のメモリ上」です。
「ノベコレみたいにブラウザ介してやってる場合、向こうのサーバなんじゃ?」と思う方もいるかもしれませんが、これはweb系エンジニアとしてはっきり言えます。
「JavaScriptはサーバ上ではなく、ブラウザ上で動くので、そのブラウザを動かしてるパソコンや端末側のメモリが変数を一時保存する対象領域」です。
で、パソコンや端末側は変数を一時保存する時、「メモリ上の何丁目何番地」として保存します。「参照渡し」はこの「メモリ上の何丁目何番地を渡す」という方式です。
なので、「参照渡し」で受け取った側の変数をいじると、「メモリ上の何丁目何番地」がそのままいじられるので、渡した側の変数も受け取った側と同じ内容に変わります。
……いや、JavaScriptでは「参照渡し」の恩恵は少ない方なんだけど、これが戻り値や変数の型に強い規制がかかってるプログラミング言語(それこそExcelのマクロ=VBAとか)だとめちゃくちゃ恩恵があるんですね。
まあ、何はともあれ、「JavaScriptにおけるJSON変数の受け渡しは参照渡し」なので、「JSON変数から取得してきた内容をいじくるけど元のJSON変数の中身は変えたくない」という場合、搦め手が必要となります。
そこで活躍するのが「JSON.parse(JSON.stringify(JSON変数名))」なわけです。
「JSON.stringify()」くんで一度JSON文字列化すると、もともとのJSON変数との接続が切れます。だって、JSON変数とJSON文字列は別物(後者はJSONとして解読できるただの文字列という形で内容の型から変わっちゃう)故、この時点で、JSON変数とJSON文字列はメモリ上の別々の場所にいることになるので。
で、そうしてできたJSON文字列を「JSON.parse()」くんで「もっかいJSONとして変換した値」を変数に放り込めば、すでに「JSON.stringify()」くん時点でもともとの変数のメモリ上番地とは切り離されているので、元のJSON変数から独立してるけど、格納時点では元のJSON変数と同じ内容が入った別の変数の出来上がりってなわけです。
なんでこんな搦め手まで知ってるか? お仕事で泥沼ったことがあるからだよ(吐血)なんなら当時JSON有識者は私ぐらいだったからね、はっはっはごふっ【専門的な話ここまで】
JSONと配列はね、使いこなせれば超便利!(特に関連フラグ制御とかまとめたりできる優れもの)なので(JavaScriptであればfor inでの繰り返しとかもできるし)、可能であれば使えると一皮も二皮も三皮も剥けると思います。何の皮かは知らんけど。
……技術的な話がクソ長くなったので、一旦この辺にいたします。
それでは~。