技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

JavaScript:オブジェクトの中身をコピーする方法(deepcopyライブラリ使用)

JavaScriptにもJavaC#と同じように参照型変数が存在し、オブジェクトや配列、関数が参照型変数です。
そして、JavaC#と同じように、単純に「=」で代入するだけでは参照先(オブジェクトのメモリ領域を示すポインタ)しかコピーできず、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。
(このようなコピーを「シャローコピー(浅いコピー)」と呼びます)

これを避けたい場合には、参照しているメモリの中身を丸ごとコピー(新たにメモリ領域を確保し書き込み)する必要があります。
(このようなコピーを「ディープコピー(深いコピー)」と呼びます)
JavaScriptでは標準でディープコピーを行うための関数が用意されていないので、自力で実装するか、外部のライブラリを使用するかする必要があります。
今回は、deepcopy(https://www.npmjs.com/package/deepcopy)というライブラリを使用します。
(なお、ディープコピーをサポートするライブラリは他にもあり、用途に応じて使い分けた方が良いです。JSONシリアライズ・デシリアライズする方法もありますが、関数やundefinedがコピーされない等の問題があるので注意が必要です。)
 
以下、サンプルコードです。
Node.jsを使用して検証します。
 
【事前準備】
・Node.js command prompt を起動
・作業フォルダ(今回は"C:\tmp\")に移動
cd c:\tmp\
・deepcopyライブラリをインストール
npm install deepcopy
 
【サンプルコード】
サンプルコードは作業フォルダ直下に作成します。
 
・ShallowCopy.js
let item1 = {id:1, name:"Sword"};
let item2 = item1;
item2.id = 2; // オブジェクトのコピー
item2.name = "Shield";
console.log(item1);
console.log(item2);
 
・DeepCopy.js
const deepcopy = require('deepcopy'); // コピー用ライブラリ読込
let item1 = {id:1, name:"Sword"};
let item2 = deepcopy(item1); // オブジェクトのコピー
item2.id = 2;
item2.name = "Shield";
console.log(item1);
console.log(item2);
 
【実行結果】
c:\tmp>node ShallowCopy.js
{ id: 2, name: 'Shield' }
{ id: 2, name: 'Shield' }

c:\tmp>node DeepCopy.js
{ id: 1, name: 'Sword' }
{ id: 2, name: 'Shield' }

c:\tmp>

HelloWorldを書く意義

職業柄、実務で使った経験のない言語やフレームワークをある日突然現場で使うようになることが多いです。
そのような場合、HelloWorldをプライベートで書くだけで、現場の実務に挑むようなこともあるのですが、HelloWorldを書くだけでも大分違うと感じます。
 
この記事では、私の実感を元に、HelloWorldを書く意義について書いていきたいと思います。
 
1.開発環境を正しく構築できたことの確認ができる
HelloWorldが動くということは、開発環境を正しく構築できたということを意味します。
プライベートのPCに開発環境を構築できていれば、勉強が必要な文法が出てきた時にすぐに試すことができるので、キャッチアップの速度が早まります。
この利点は、Webで入門者向けの手順を探してそれをそのまま実施するだけでも得られるメリットです。
 
2.言語やフレームワークが持つ特徴や利点を垣間見える
言語やフレームワークの特徴を下調べした後、1つ1つの手順や記述内容の意味を考えることで、言語やフレームワークが持つ特徴や利点を垣間見ることができる場合もあります。
HelloWorldだけでは意味のあることはできませんが、記述内容を少しずつ付け加えていくことで、意味のある作業やアプリケーション作成を行うことができるようになります。
言語やフレームワークが持つ特徴や利点を経験から知っていれば、意味のある作業やアプリケーション作成を行う上での基礎力を身につけることができます。
 
例えば、以下は私がC#のHelloWorldを行った時の記事です。
Visual Studio Communityの構成を意識することで、構成マネージャーやNugetといった応用的な話を理解しやすくなりましたし、Windows標準でコンパイラが用意されていることを知ることで簡単なツールをC#で作れるようにもなりました。
https://cyzennt.co.jp/blog/2020/04/18/c%E3%81%A7%E3%81%AEhelloworld%EF%BC%88%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%EF%BC%89/
 
また、以下は私がVue.jsのHelloWorldを行った時の記事です。
変数の値を監視するという少し難しい方法でHelloWorldを試すことで、MVVMモデルの利点を垣間見ることができましたし、その後の実務で使われるコードの理解にも役立ちました。
https://akira2kun.hatenablog.com/entry/2021/01/03/120000

npmで公開されているパッケージをローカルに落とさず使用する

npmで公開されているパッケージは、UNPKG(https://unpkg.com/)で公開されています。
UNPKGで公開されているパッケージをscriptタグ等で指定することで、ローカルにライブラリを落とすことなくライブラリを使用することができるようになります。
インターネット接続していることが前提となりますが、Webシステムでは便利なサービスです。
(このようなサービスをCDNサービスと呼びます)
 
以前に試したVue.js(https://akira2kun.hatenablog.com/entry/2021/01/03/120000)も、インストールすることなく以下のような形で使用することができます。
(今回は触れませんが、バージョン指定も可能です)
 
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - Hello world!</title>
</head>
<body>

<div id="app">
 <input type="checkbox" id="checkbox1" v-model="checked1">
 <label for="checkbox1">
  {{ message }}
 </label>
</div>

<script src="https://unpkg.com/vue"></script>
<script>
 var app = new Vue({
  // elで指定した値はdivタグのidと対応
  el: '#app',
  // 変数の初期値定義
  data: {
   message: '',
   checked1: false
  },
  // 変数の値を監視するイベントを定義
  watch: {
   checked1: function(newVal, oldVal) {
    this.message = (newVal) ? 'Hello World!' : '';
   }
  }
 })
</script>
</body>
</html>

Jestを試してみた

Jestとは、JavaScriptのテスト用のフレームワークです。
ここでは、Node.jsがインストールされていることを前提に、Jestを試してみます。
公式ドキュメントであるGetting Started(https://jestjs.io/docs/ja/getting-started)を参考に、説明が割愛されている箇所を補いながら説明していきます。
 
【前提】
・WindowsOSで作業を行います。
・Node.jsを事前にインストールし、"Node.js Command Prompt"でコマンド発行します。
・今回の確認では、C:\tmp\ で作業を行います(以下「作業フォルダ」)。
 
【インストール】
・cdコマンドで作業フォルダに移動します。
cd C:\tmp\
 
・npmコマンド(ライブラリのインストール用のコマンド)を発行しJestを取得します。
npm install --save-dev jest
 
・コマンドを発行すると、以下のように出力されます。
C:\tmp>npm install --save-dev jest
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN saveError ENOENT: no such file or directory, open 'C:\tmp\package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^2.1.2 (node_modules\jest-haste-map\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.1: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN enoent ENOENT: no such file or directory, open 'C:\tmp\package.json'
npm WARN tmp No description
npm WARN tmp No repository field.
npm WARN tmp No README data
npm WARN tmp No license field.

+ jest@26.6.3
added 511 packages from 351 contributors and audited 512 packages in 92.123s

23 packages are looking for funding
run `npm fund` for details

found 0 vulnerabilities


C:\tmp>
 
・作業フォルダ直下に、以下のようなフォルダが作成されれば成功です。
C:\tmp>dir node_modules\jest
ドライブ C のボリューム ラベルは  です
ボリューム シリアル番号は  です

C:\tmp\node_modules\jest のディレクト

2021/01/06 21:45 <DIR> .
2021/01/06 21:45 <DIR> ..
2021/01/06 21:44 <DIR> bin
2021/01/06 21:44 <DIR> build
1985/10/26 17:15 1,086 LICENSE
2021/01/06 21:45 <DIR> node_modules
2021/01/06 21:45 2,084 package.json
1985/10/26 17:15 551 README.md
3 個のファイル 3,721 バイト
5 個のディレクトリ バイトの空き領域

C:\tmp>
 
※なお、yarnコマンドでも取得できます。
 
【実行準備】
・作業フォルダに、"package.json"を作成します。内容は以下の通りです。
{
 "scripts": {
  "test": "jest"
 }
}
 
※これで、npmコマンドの引数に"test"を指定すると、コマンド"jest"が発行されるようになります。
 
【テスト対象のスクリプト作成】
・作業フォルダに、"sum.js"を作成します。内容は以下の通りです。
function sum(a, b) {
 return a + b;
}
module.exports = sum;
 
【テストコード作成】
・作業フォルダに、"sum.test.js"を作成します。内容は以下の通りです。
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
 expect(sum(1, 2)).toBe(3);
});
 
【実行】
・npmコマンドによりテストを実行します。
npm test
 
・以下のように、作業フォルダ直下のテストコードが実行されれば成功です。
C:\tmp>npm test

> @ test C:\tmp
> jest

PASS ./sum.test.js (7.241 s)
√ adds 1 + 2 to equal 3 (10 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 23.654 s
Ran all test suites.

C:\tmp>

JSONフォーマットとは

JSONとは、データ構造を記述するフォーマットの一種です。
CSV等とは異なり、階層構造を記述することができます。
階層構造を表現できるフォーマットとしてはXMLも挙げられますが、JSONフォーマットはXMLよりも簡易的に記述できる、JavaScriptとの親和性が高い、といった特徴があります。
電文でのデータ通信で多く使われる印象があります。
 
以下では、JSONについて、フォーマットの簡単な説明と、JSONの生成・読込を行う簡単なサンプルコードを取り上げます。
 
----
 
JSONのフォーマットの簡単な説明】
■データの開始と終了
開始は"{"、終了は"}"で示されます。
 
■データの構造
以下のように、変数名と値を":"で区切って表記します。
変数名はダブルクォーテーションで囲います。
(値については後述します)
{ "height" : 170 }
 
変数名と値のペアが複数ある場合は、ペアを","で区切って表記します。
{ "height" : 170, "weight" : 58.8 }
 
人に読ませることを意識する場合、以下のように改行を入れてわかりやすくすることが多いです。
{
 "height" : 170,
 "weight" : 58.8
}
 
■値の種類
JSONでは以下の値を使用することができます。
 
・数値
数値を記述することで、数値としてみなされます。
{ "height" : 170 }
{ "weight" : 58.8 }
 
・文字列

ダブルクォーテーションで囲うことで、文字列としてみなされます。
{ "str" : "hoge" }
 
・true/false
trueと記述することで、真としてみなされます。
falseと記述することで、偽としてみなされます。
{ "flag1" : true }
{ "flag2" : false }  
 
・null
nullと記述することで、nullとしてみなされます。
{ "flag3" : null } 
 
・オブジェクト
"{"と"}"で囲うことで、オブジェクトとしてみなされます。
これにより、階層構造の記述が可能になります。
{
 "name": {
  "firstName":"miku",
  "lastName":"hatsune"
 }
}
 
・配列
"["と"]"で囲うことで、配列としてみなされます。
{"numList": [ 1, 2, 3 ]}
 
文字コード
文字コードUTF-8(BOM無し)を使用します。
 
■その他詳細
RFC8259(https://tools.ietf.org/html/rfc8259)がJSONの正式な仕様となりますので、こちらをご参照ください。
 
----
 
JSONの生成・読込を行うサンプルコード】
ここでは、JavaScript(Node.js)でのサンプルコードを提示します。
以下のように、JSONの生成・読込を行うための機能が用意されており、それを利用することで容易に生成・読込が可能になります。
 
■生成のサンプル
・コード(stringify.js)
let worker = {
 skill: "singing",
 name: {
  firstName: "miku",
  lastName : "hatsune"
 }
};
let json = JSON.stringify(worker)
console.log(json);
 
・実行結果
C:\tmp>node stringify.js
{"skill":"singing","name":{"firstName":"miku","lastName":"hatsune"}}

C:\tmp>
 
■読込のサンプル
・コード(parse.js)
let json = '{"skill":"singing","name":{"firstName":"miku","lastName":"hatsune"}}';
let worker = JSON.parse(json);
console.log("skill: " + worker.skill);
console.log("firstName: " + worker.name.firstName);
console.log("lastName: " + worker.name.lastName);
 
・実行結果
C:\tmp>node parse.js
skill: singing
firstName: miku
lastName: hatsune

C:\tmp>

メインフレームのプリンターで使われる制御コードの概要

メインフレームのシステムでは、プリンターを用いて帳票やハガキを印刷することが多いです。
この際、プリンターに読み込ませるためのバイナリの制御コードを用いて、文字の形式や改行・改ページ等をコントロールします。
プリンターに読み込ませるファイルには制御コードが埋め込まれており、制御コードの意味がわかっていないとバイナリエディタやダンプ結果を通してファイルを見る際に困ることがあります。
(出力される帳票やハガキを見ることができれば良いのですが、印刷には費用や時間を要するので、手軽に見ることはできません)
 
制御コードで代表的なものは、「シフトアウト・シフトイン」と、「改行指示」です。
 
「シフトアウト・シフトイン」は、1バイト文字の箇所と2バイト文字の箇所を明示するための制御コードであり、2バイト文字が始まる直前にシフトアウト文字を入れ、2バイト文字が終わった直後にシフトイン文字を入れます。
(2バイト文字の前後にシフトアウト・シフトインを入れないと、その箇所が1バイト文字として扱われ、文字化けします)
以下は、文字コードにEBCDIK/KEISを使用する場合の例です。

f:id:akira2kun:20201212165445j:plain
 
「改行指示」は、その名の通り改行を示す制御コードです。
プリンターに読み込ませるファイルは基本的にバイナリなのでテキストエディタで開いても改行されませんが、改行指示の制御コードを探して、そのコードを通常用いられる改行コード(CRLF等)に置換することで、テキストエディタで開いた時になんとなく内容を読み取れるようになります。
 
その他、「改ページ指示」「文字の大きさ指定」「バーコード印字」等の制御コードが存在します。
どのような制御コードが存在するのか、制御コードとして具体的にどのようなコードを指定すれば良いのか、はプリンターごとに決まりがあり、その資料も基本的には社外に持ち出せないので、具体的なことは記事中に書くことができません。
しかし、そのようなコードが存在するということを知っているだけでもテストが容易になりますし、もし知る必要があれば現場の有識者に質問して教えてもらうと良いでしょう。

Vue.jsインストール手順とHelloWorld(Windows、2020年12月)

Vue.jsとはJavaScriptGUIフレームワークです。
MVVM(Model-View-ViewModel)を採用しており、画面(View)と内部状態(Model)を分離して画面の制約にとらわれないようにすることで、画面で入力された内容をリアルタイムに内部状態に反映させたり、内部状態の変化をリアルタイムに画面に出力したりすることができます。
 
今回は、WindowsOSのPCにインストールし、実行確認をする手順を掲載します。
実行確認の「Hello World」に関しては色々書き方がありますが、今回はMVVMの良さを実感できる書き方で「Hello World」を試します。
 
■前提
・OS
 Windows8.1
 
・実施日
 2020年12月30日
 
・Vue.jsのバージョン
 2.6.12
 
■インストール手順
1.Vue.jsの公式サイトにアクセスする。
 https://jp.vuejs.org/index.html
 
2.「はじめる」を左クリックする。

f:id:akira2kun:20201230202159j:plain


3.「インストール」を左クリックする。

f:id:akira2kun:20201230202223j:plain


4.「開発バージョン」を右クリックし「名前を付けてリンク先を保存」をクリック。
 ※FireFoxの場合。
  リンク先はjsファイルなので、ブラウザの機能でファイルとして保存する。

f:id:akira2kun:20201230202243j:plain


5.任意のフォルダにvue.jsを保存。
 ※今回は"C:\tmp"に保存。

f:id:akira2kun:20201230202307j:plain

 
■実行確認(Hello World)手順
1."vue.js"を保存したフォルダに、"hello.html"という名前のファイルを作成する。
 
2.「hello.html」に下記を入力して保存。

<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - Hello world!</title>
</head>
<body>

<div id="app">
 <input type="checkbox" id="checkbox1" v-model="checked1">
 <label for="checkbox1">
  {{ message }}
 </label>
</div>

<script src="./vue.js"></script>
<script>
 var app = new Vue({
  // elで指定した値はdivタグのidと対応
  el: '#app',
  // 変数の初期値定義
  data: {
   message: '',
   checked1: false
  },
  // 変数の値を監視するイベントを定義
  watch: {
   checked1: function(newVal, oldVal) {
    this.message = (newVal) ? 'Hello World!' : '';
   }
  }
 })
</script>
</body>
</html>

 
3.「hello.html」をブラウザから開く。
 ※今回はChromeで開きます。
 
初期状態では以下のように何も表示されませんが

f:id:akira2kun:20201230202338j:plain

 

チェックボックスをチェックすると「Hello World!」と表示されます。

f:id:akira2kun:20201230202421j:plain


ボタンをクリックしてイベントを飛ばしたり、リロードしたりすることなく、画面で変更された内部状態をリアルタイムに画面上に反映することができています。