20200911のJavaScriptに関する記事は30件です。

Firefox上apex:actionFuncionを呼出時の注意事項

業務上に実現したいことは以下となります。


一覧にファイルリストがあって、行毎にダウンロードボタンがあります。
そのボタンをボタンを押下したら、あるオブジェクトの更新を行い、更新完了したら、ファイルをダウンロードする
上記の処理は非同期で行うことです。

以下の実装だと、chrome上に問題なく処理されます。

①オブジェクトが正しく更新されます。
②サーバ側の処理が処理完了したら、画面の非同期更新エリアがリフレッシュされます。
③ファイルが正しくダウンロードされます。
Firefox上③のみ有効、①②が効かない。

ファイルダウンロード1.v
<a onclick="fileDownload(id)" />
<apex:actionFunction name="testAction" action="testAction" reRender="testArea">
    <apex:param name="testId" assignTo="{!testId}" />
</apex:actionFunction>

<script>
function fileDownload(id) {
    // 他の業務処理を行う
    testAction(id);
    loaction.href="ファイルパス" + id;
}
</script>

以下のように変更すると、Firefox上に①②も効かないが、ボタンを連続的に押下する場合、①が効きます。

ファイルダウンロード2.v
<a onclick="fileDownload(id)" />
<apex:actionFunction name="testAction" action="testAction" reRender="testArea">
    <apex:param name="testId" assignTo="{!testId}" />
</apex:actionFunction>

<script>
function fileDownload(id) {
    // 他の業務処理を行う
    Function('return this')()['testAction'](id);
    loaction.href="ファイルパス" + id;
}
</script>

以下のように変更すると、ChromeもFirefoxも①②③正しく処理されます。

ファイルダウンロード3.v
<a onclick="fileDownload(id)" />
<apex:actionFunction name="testAction" action="testAction" oncomplete="doDownload()" reRender="testArea">
    <apex:param name="testId" assignTo="{!testId}" />
</apex:actionFunction>

<script>
var testId = null;
function fileDownload(id) {
    // 他の業務処理を行う
    Function('return this')()['testAction'](id);
    testId = id;
}
function doDownload() {
    loaction.href="ファイルパス" + testId;
}
</script>

まとめ(原因分析)

1、Salesforce非同期処理(apex:actionFunction)のName属性は通常意味のJSの関数だと理解していますので、
JSから直接呼出して問題がないかと思います。おそらくChrome上もその通りに解析されるから、問題なく処理される。
だがFirefox上にJS関数を経由し、Salesforce非同期処理(apex:actionFunction)を直接呼出すのは効かない。
原因がわからないが、Functionを利用してラッピングしたら、問題なく利用されます。
2、Firefox上に「loaction.href」を利用すると、「redirect」が変わってしまうから、非同期処理完了後の画面リフレッシュが効かなくなるかと思います。なので、ダウロード処理はoncompleteに移動すれば解決します。
※上記の分析は自分の考えだけですが、全部正しいわけではないが、上記のようにやっておけば、問題が解決できます!
もし本当の原因がわかる方はいらっしゃいましたら、是非教えて頂きたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】超基礎的なメソッドの紹介 その1

JavaScriptで行うDOM(Document Object Model、要はHTML等のマークアップ言語にアクセス・編集しやすい標準的に仕組み)の操作において、基礎的なメソッドを学んだため、備忘として記します。

1 HTML要素の取得(すべてはこれから始まる)
  getElementByIdメソッド、getElementsByTagNameメソッド

<h1 id = "heading1">朝の献立</h1>
<h1 id = "heading2">昼の献立</h1>
const select = getElementById(`heading1`); //「heading1」のidを持つHTML要素を取得し、定数selectに代入。
console.log(select); //「<h1 id = "heading1">朝の献立</h1>」をコンソールに出力。
const selects = getElementsByTagName(`h1`); //<h1>要素すべてを「配列の様に」取得(getElement"s"ByTagNameである点に注意)
console.log(selects[0]); //<h1>要素のうち1つ目をコンソールに出力。※文字列「朝の献立」を出力するわけではない点に注意。
console.log(selects[2]); //<h1>要素の3つ目は、undefined(定義されていない)とエラー。

2 テキストデータの取得
 textContentメソッド(ついでにfor文を用いた応用)

<h1 id = "heading1">朝の献立</h1>
<h1 id = "heading2">昼の献立</h1>
<h1 id = "heading3">夜の献立</h1>
const selects = getElementsByTagName(`h1`); //<h1>要素すべてを「配列の様に」取得(getElement"s"ByTagNameである点に注意)
console.log(selects[0].textContent); //「朝の献立」とコンソールに出力。

for (let i = 0; i > selects.length; i++) {
     console.log(selects[i].textContent);
} //「朝の献立」「昼の献立」「夜の献立」とコンソールに出力。※for文の条件式の注意点としては、.lengthでselects要素の数を取得している。

とりあえずここまで。
その1と書いたので、続けたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Electronをバージョンアップしたらjsのネイティブモジュールが読み込めなくなった話

Electron9にバージョンアップしたところ、
途中でアプリの画面が動かなくなってしまう(windowになにも表示がされない)障害が発生しました。

原因

  • Electron9から、appallowrendererprocessreuseのパラメーターがデフォルトで true になってた
  • これによって2回目に読み込まれた時に、jsのネイティブモジュールが読み込めなくなっていた

appallowrendererprocessreuse とは

(公式サイトより) Boolean値。trueのときは、レンダラープロセスが確実に再起動されるように、Electronが設定しているオーバーライドを無効にする。このプロパティは、レンダラープロセスで使用できるネイティブモジュールに影響する
とのこと。名前からして再利用しようとしてうまくモジュールを読み込めてない、というような状況。

対策(暫定対応)

  • ページの初期化処理とかをしている部分で
    • app.allowRendererProcessReuse = false;
  • これまでと変わらないように動かせた

今後

  • issue によると

    • Electron 6 で app.allowRendererProcessReuse オプションを追加
    • コンテキスト非対応のネイティブモジュールについて、Electron 7で最初の非推奨警告ランドを用意する
    • Electron 9 でapp.allowRendererProcessReuseのデフォルト値がtrueに
    • Electron 10でapp.allowRendererProcessReuseを変更する機能を廃止予定
    • Electron 11でapp.allowRendererProcessReuseを変更する機能を削除
  • 今後Electron11では完全にfalseに変更できなくなるので、別の対応が必要

    • 現在調査中
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RPGツクールMZでイベントコマンド「スクリプト」からプラグインコマンドを呼び出す方法

副題「あるいは、RPGツクールMZのプラグインコマンド周りをざっと調べてみた」

【目次】

  • 前置き
  • 結論
  • 読み解き
    • MVのプラグインコマンド振り返り
    • MZでの変更部分とそれへの対応
    • MVとMZで、スクリプトからのプラグインコマンド呼び出し方法が異なる理由

前置き

自身はあまり実感したことがないのですが、MVの頃から
「プラグインコマンドをスクリプトから呼び出したい」
という需要がぼちぼちあるようです。
また、MZの売りの一つが
「プラグイン周りの機能強化 > プラグインコマンドの刷新」
なわけですが、その辺を全然さわれてなかったので、学習がてら調査と整理をしてみました。

結論

まず結論。
MZでは以下の記述でイベントコマンド「スクリプト」からプラグインコマンドを呼び出せます。

const args = {/* ここにプラグインコマンドのパラメータを記述 */};
PluginManager.callCommand(this,(プラグイン名) , (コマンド名), args);

argsというオブジェクトにプラグインコマンドのパラメータをプロパティとして記述し、それをPluginManagerのcallCommandメソッドにプラグイン名、コマンド名と共に渡します。

例)
プラグイン名「test.js」のコマンド名「Test」をイベントコマンドスクリプトから呼び出す例です。
「test.js」は与えられた引数をconsole.logに出力するだけのプラグインであり、
パラメータ名は「testKey」としています。
img1.pngimg2.png
きちんと呼び出しできてますね。

方法だけ知りたい、という人はここまでです。
上のスクリプトをコピペしてこのページはそっ閉じしましょう。

読み解き

上記が何をしているのか、そしてMVからどう変わったのかを自身の備忘録も兼ねて以下に記します。

なお、ざっくりとしたイメージを掴むことを最優先とし、「Game_Interpreter」や
「PluginManager」の細かい挙動や仕組みは省略している点をご了承ください。

MVのプラグインコマンド振り返り

まずはMVの振り返りです。
MVにおいてイベントコマンド「スクリプト」からのプラグインコマンド呼び出しは、
いくつか方法はありますが、以下のコードがシンプルです。

var args = [/* ここにプラグインコマンドのパラメータを記述 */];
this.pluginCommand(command, args);

※argsは配列

イベントコマンド全般(「スクリプト」「プラグインコマンド」含む)は「Game_Interpreter」というクラスによって呼び出しが行われます。
たとえば「文章の表示」ならば「Game_Interpreter.prototype.command101」
「選択肢の表示」ならば「Game_Interpreter.prototype.command102」というように、
「command + コマンド番号」の形でメソッド名が与えられています。
(※参考:トリアコンタンさま RPGツクールMV プラグインコマンド集 リファレンス

そして今回のお題であるプラグインコマンドについては「356番」が与えられています。
以下がMVコアスクリプトの「command356」のコードです。

rpg_objects.js
// Plugin Command
Game_Interpreter.prototype.command356 = function() {
    var args = this._params[0].split(" ");
    var command = args.shift();
    this.pluginCommand(command, args);
    return true;
};

詳細は端折りますが、command356が呼び出されると、コマンド名とコマンドパラメータを取得し、
それを「this.pluginCommand」に引数として渡して処理を投げます。
この「this.pluginCommand」が核心部分です。

では、次に処理を投げられた「pluginCommand」について見てみましょう。

rpg_objects.js
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    // to be overridden by plugins
};

おおっとぉ、中身が空だ!!
コメントを直訳すると「プラグインでオーバーライドされます」ですね。
そう、MVにおいてはコマンドのチェックと実行開始はプラグイン側に委ねられていたのです。

一般的には以下のような要領で、各自でプラグインコマンドのコマンド名と引数を取得し、
switchで自身のコマンド名に該当するかを分岐し、実行を行っていました。

プラグイン側の記述
const _game_interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
    _game_interpreter_pluginCommand.call(this, command, args);
    switch (command) {
        case 'CommandName1':
            /* CommandName1の処理 */
            break;
        case 'CommandName2':
            /* CommandName2の処理 */
            break;
       /* 以下、プラグインコマンドの分だけ分岐 */
    };
};

簡単に流れを追うと、まずは他のプラグインと処理が競合しないようにエイリアスとしてそれまでの「pluginCommand」の定義を保存し、
改めて「pluginCommand」をオーバーライドしています。
その中で先ほどエイリアスを付けて保存しておいた関数を呼び出して他のプラグイン側の処理を実行、
続けてswitchにより自身のプラグインのコマンドをチェックし、該当するならばそれぞれの処理を行う、という流れです。
繰り返しとなりますが、MVではプラグインコマンドのチェックと実行開始はプラグイン側に委ねられていたのです。

【MVのプラグインコマンドのフローを整理】
プラグインコマンドが実行されると
 >「Game_Interpreter.command356」が呼ばれる
  >「Game_Interpreter.command356」から「Game_Interpreter.pluginCommand」が呼ばれる
   >ただし「Game_Interpreter.pluginCommand」は各自プラグイン側で記述

MZでの変更部分とそれへの対応

MZでは、公式サイトでもアピールされているように、プラグインコマンド周りが刷新されました。
エディター上での入力のし易さ、プラグイン実行側でのデータの取り扱い易さ、いずれも大きく向上しています。

MZでは、「PluginManager」という静的クラスの「registerCommand」メソッドがプラグインコマンドとその実行内容を事前に受け取り、「PluginManager」内に保持してくれるようになっています。

rmmz_objects.js
PluginManager.registerCommand = function(pluginName, commandName, func) {
    const key = pluginName + ":" + commandName;
    this._commands[key] = func;
};

「PluginManager.registerCommand」は第一引数にプラグイン名、第二引数にコマンド名、そして第三引数に「コマンド実行時に実行したい関数」を受け取ります。
するとPluginManagerの「_commands」プロパティに「プラグイン名とコマンド名を連結した物」をkeyとして、第三引数の関数が保存されます。

このメソッドをプラグイン側で呼び出してコマンド名や実行関数を登録しておけば、後はプラグインコマンドが実行された時点で保存されていた関数が呼び出されます。
(※「PluginManager.registerCommand」の記述例は公式の「プラグイン講座 > プラグインを作ってみる」内の「プラグインコマンドの定義」に詳しく解説されていますので、そちらも合わせて読んでおきましょう。)

次にプラグインコマンドが実行された際の流れを見ていきます。
プラグインコマンド実行の基本的な仕組みはMZもMVと同様で、Game_Interpreterクラスの「command + コマンド番号」メソッドで実行されます。
(※参考:トリアコンタンさま RPGツクールMZ スクリプトリファレンス

ただし、MZではプラグインコマンド実行時は「Game_Interpreter.command357」が呼び出されます。
ここポイント。「356」でなく「357」です。

rmmz_objects.js
Game_Interpreter.prototype.command357 = function(params) {
    PluginManager.callCommand(this, params[0], params[1], params[3]);
    return true;
};

これはほぼ、「PluginManager.callCommand」を呼び出すだけのメソッドです。
この「PluginManager.callCommand」が核心部分であり、冒頭の結論です。
MVでいう「Game_Interpreter.pluginCommand」に相当すると言って差し支えないでしょう。

rmmz_managers.js
PluginManager.callCommand = function(self, pluginName, commandName, args) {
    const key = pluginName + ":" + commandName;
    const func = this._commands[key];
    if (typeof func === "function") {
        func.bind(self)(args);
    }
};

先ほどの「PluginManager.registerCommand」と対になる処理ですね。
「PluginManager.registerCommand」で受け取っていた関数を、今度は呼び出しています。

つまり、MVだとプラグイン側で記述していたコマンドのチェックや実行開始の処理は、MZだとコアスクリプトで初めから用意されており、プラグイン側の記述は不要です。素晴らしい。
(※注:アノテーション除く)

【MZのプラグインコマンドのフローを整理】
プラグインコマンドが実行されると
 >「Game_Interpreter.command357」が呼ばれる
  >「Game_Interpreter.command357」から「PluginManager.callCommand」が呼ばれる

MVとMZで、スクリプトからのプラグインコマンド呼び出し方法が異なる理由

なお、MVでプラグインコマンドを受け取っていた「command356」は、MVとの互換性維持のためかMZにもそのまま残してあります。

rmmz_objects.js
// Plugin Command MV (deprecated)
Game_Interpreter.prototype.command356 = function(params) {
    const args = params[0].split(" ");
    const command = args.shift();
    this.pluginCommand(command, args);
    return true;
};

Game_Interpreter.prototype.pluginCommand = function() {
    // deprecated
};

ただし、コメントにもあるように「deprecated(非推奨)」となっていますし、実際にMZでイベントコマンド「スクリプト」から「pluginCommand」を呼び出しても何も起こりません。

何故か?

それは、何度も繰り返してきましたがプラグインコマンドのチェックと実行を担う「pluginCommand」は空だからです。

MZにおいて、イベントコマンド「スクリプト」から「this.pluginCommand」を呼び出したとしても、プラグイン側がpluginCommandメソッドの中身を記述していなければ、空の関数が呼び出されただけで何も実行されずに終わります

言い換えると、プラグインコマンドを使用するプラグインをMVとMZの両対応とするには
MZ向けの「PluginManager.registerCommand」による記述と、
MV向けの「Game_Interpreter.pluginCommand」による記述の
両方に備えておく必要がある、
ということです。

プラグインコマンドを使用するプラグインをMV、MZ両対応にするのは若干メンドーですね。
(その辺を共用化するプラグインを作れば需要があるかな?)


以上、イベントコマンド「スクリプト」からプラグインコマンドを呼び出す、という観点を取っ掛かりとして、
MZのプラグインコマンド処理を追ってみました。

忌憚ないツッコミ待ちだぁ! щ(゚Д゚щ) バッチコーイ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[MockDate]テストの時に日付を固定化する方法[JavaScript]

背景

Jestでテストを書いている時に日付の固定化をしたかったのですが、
今まで以下のような感じで、Date.newをmock化していましたが、書くのがめんどくさいなって思ってました。
https://qiita.com/bearII/items/d8a3bc0e83b14b36e64e

結論

以下のパッケージが良さげでした。
https://www.npmjs.com/package/mockdate

以下のように1行でモック化できてテストファイルがスッキリします。
before内でモック化し、after内でモックを解除するだけです。
momentもモックできているのが確認できます。

import MockDate from 'mockdate'
import moment from 'moment'

describe('mock date', () => {
  beforeEach(() => {
    MockDate.set('2000-11-22')
  })
  afterEach(() => {
    MockDate.reset()
  })
  it('new Dateがmock化されていることを確認', () => {
    expect(new Date().getFullYear()).toBe(2000)
    expect(new Date().getMonth()).toBe(10)
    expect(new Date().getDate()).toBe(22)
  })
  it('momentがmock化されていることを確認', () => {
    expect(moment().format('YYYY-MM-DD')).toBe('2000-11-22')
  })
})
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編takeRight関数の実相のアウトプット

takeRight関数の課題内容

   ↓
https://js-drills.com/blog/takeright/

take関数の取り組む前の状態

takeの逆なので簡単だと思っていた

take関数に取り組んだ後の状態

  • (1 + i)ここの部分の理解が少し追いつかなかったです。

tail関数の実装コード(答えを見る前)

実行できなかった

tail関数の実装コード(答えを見た後)

function takeRight(array, n = 1) {
  if(n === 0) {
    return [];
  }
  if(n > array.length) {
    return [...array];
  }

  const takenArray = [];
  for(let i = 0; i < n; i++) {
    // array [1, 2, 3]
    // 1 + i => 1, 2, 3
    // array.length =>
    //   3 - (1 + 0) = 2,
    //   3 - (1 + 1) = 1
    //   3 - (1 + 2) = 0
    const indexFromRight = array.length - (1 + i)
    takenArray.unshift( array[indexFromRight] );
  }

  return takenArray;
}

console.log( takeRight([1, 2, 3]) );
// => [3]

console.log( takeRight([1, 2, 3], 2) );
// => [2, 3]

console.log( takeRight([1, 2, 3], 5) );
// => [1, 2, 3]

console.log( takeRight([1, 2, 3], 0) ) ;
// => []
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編take関数の実相のアウトプット

take関数の課題内容

   ↓

take関数の取り組む前の状態

大体作る前からどうすればいいかわかっていた

take関数に取り組んだ後の状態

自力でできた

tail関数の実装コード(答えを見る前)

function take(array, n = 1) {
  if(n === 0) {
    return [];
  }
  if(n > array.length) {
    return [...array];
  }

  const takenValues = [];
  for(let i = 0; i < n; i++) {
    takenValues.push( array[i] );
  }

  return takenValues;
}

tail関数の実装コード(答えを見た後)

function take(array, n = 1) {
  if(n === 0) {
    return [];
  }
  if(n > array.length) {
    return [...array];
  }

  const takenValues = [];
  for(let i = 0; i < n; i++) {
    takenValues.push( array[i] );
  }

  return takenValues;
}

console.log( take([1, 2, 3]) );
// => [1]

console.log( take([1, 2, 3], 2) );
// => [1, 2]

console.log( take([1, 2, 3], 5) );
// => [1, 2, 3]

console.log( take([1, 2, 3], 0) );
// => []
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編tail関数の実相のアウトプット

tail関数の課題内容

   ↓
https://js-drills.com/blog/tail/

tail関数の取り組む前の状態

大体どういう風に組み立てればいいのかわかっていた

tail関数に取り組んだ後の状態

自力でできた

tail関数の実装コード(答えを見る前)

function tail(array) {
  const tailArray = [];
  for(let i = 1; i < array.length; i++) {
    tailArray.push( array[i] );
  }

  return tailArray;
}

tail関数の実装コード(答えを見た後)

function tail(array) {
  const tailArray = [];
  for(let i = 1; i < array.length; i++) {
    tailArray.push( array[i] );
  }

  return tailArray;
}

console.log( tail([1, 2, 3]) );
// => [2, 3]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編slice関数の実相のアウトプット

slice関数の課題内容

   ↓
https://js-drills.com/blog/slice/

slice関数の取り組む前の状態

大体どういう風に組み立てればいいのかわかっていた

slice関数に取り組んだ後の状態

自力でできた

slice関数の実装コード(答えを見る前)

function slice(array, start = 0, end = array.length) {
  const slicedArray = [];
  for(let i = start; i < end; i++) {
    slicedArray.push( array[i] );
  }

  return slicedArray;
}

slice関数の実装コード(答えを見た後)

function slice(array, start = 0, end = array.length) {
  const slicedArray = [];
  for(let i = start; i < end; i++) {
    slicedArray.push( array[i] );
  }

  return slicedArray;
}

const numbers = [10, 20, 30, 40, 50];
const slicedNumbers = slice(numbers, 1, 4);

console.log( slicedNumbers );
// => [20, 30, 40]

console.log( numbers );
// => [10, 20, 30, 40, 50]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LambdaでDynamoDBに複数レコードの書き込みをする(Python、JavaScript)

はじめに

DynamoDBに複数レコードを書き込み(更新)する必要があったので、実装してみた結果と注意的なものを書き留めたいと思い記事を投稿した
またPythonとNode.jsの両方での実装を載せようと思います

前提

テーブル

今回書き込みをするテーブルは以下を想定
テーブル名:Users
カラムと型(スキーマ自体に型はありませんが、登録するデータの型として便宜上決めておきます)は以下の通り

  • id(primary key):Number
  • name:String
  • address:String
  • friends: List[Map]

friendはid, name, addressのキーを持つMapの一覧を想定
今回は複雑めな型(Userテーブルのfriendのような型)であっても、書き込みができることも示しておきたいことの一つなので、そもそもテーブルの設計がベストであるかは一旦無視でいきます

Lambdaの準備

今回はLambda上で動くコードを掲載するので、Lambdaは各自で準備ができていることを前提とします

記事の流れ

全体のコード例 → 軽い解説
の手順で書いていき、最後に共通する補足事項を載せる

Pythonでの実装

import boto3
from boto3.dynamodb.conditions import Key

def update_users(table, users_friends):
   with table.batch_writer() as batch:
      for n in range(3):
        batch.put_item(
          Item={
                "id": n + 1,
                "name": "user" + str(n + 1),
                "address": "address" + str(n + 1),
                "friends": users_friends[n]
          }
        )

def lambda_handler(event, context):
  try:
    dynamoDB = boto3.resource("dynamodb")
    table = dynamoDB.Table("Users")
      user1_friends = [
        { "id": 2, "name": "user2", "address": "address2" },
        { "id": 3, "name": "user3", "address": "address3" }
      ]
      user2_friends = [
        { "id": 1, "name": "user1", "address": "address1" },
        { "id": 3, "name": "user3", "address": "address3" }
      ]
      user3_friends = [
        { "id": 1, "name": "user1", "address": "address1" },
        { "id": 2, "name": "user2", "address": "address2" }
      ]

      users_friends = [user1_friends, user2_friends, user3_friends]

    update_users(table, users_friends)

    return event["message"] # 返すものは適当
  except Exception as e:
        print(e)

軽く解説

コードの書き方云々や書き込むデータ自体については本質ではないので、スルーしてもらって大丈夫です
大事なのはupdate_users関数の中身で、DynamoDBのモデルのインスタンスに生えているbatch_writeメソッドを使っているということ
すなわち、複数書き込みをしたいときは以下のブロック内でPUTをする

with table.batch_write() as batch:
    # ...略

また、PUTする際には例でいうとbatch.put_itemを呼び出してやる
こいつのItemという名前の引数に書き込みの内容を記述していく
そしてもう一点注目したいのが、List[Map]という型であっても問題なく書き込むことができるという点
基本的にどんな型でも指定したキーの値としてちゃんとDBに書き込めるので、要するにItemには更新したい辞書をそのまま渡してあげれば問題ない
最後にちょっとした補足として、batch_writeのブロック内でfor文を回しているが、当然batch.put_itemをベタで複数回書いていってもよいので、もしfor文では書けないときはベタでやればいい
また、一度にできる書き込める上限は25件までと言われているが、どうやら25件以上の場合は再送してくれたりよしなにやってくれているらしいので、件数を気にせずコードを書いていけるっぽい

Node.jsでの実装

const AWS = require("aws-sdk");
const dynamoDB = new AWS.DynamoDB.DocumentClient({
  region: "ap-northeast-1"
});

const tableName = "Users";

exports.handler = (event, context) => {
      const user1Friends = [
        { id: 2, name: "user2", address: "address2" },
        { id: 3, name: "user3", address: "address3" }
      ];
      const user2Friends = [
        { id: 1, name: "user1", address: "address1" },
        { id: 3, name: "user3", address: "address3" }
      ];
      const user3Friends = [
        { id: 1, name: "user1", address: "address1" },
        { id: 2, name: "user2", address: "address2" }
      ];
      const usersFriends = [user1Friends, user2Friends, user3Friends]

    const params = {
        RequestItems: {
          [tableName]: usersFriends.map((e, i) => ({
            PutRequest: {
              Item: {
                id: i + 1,
                name: `user${i + 1}`,
                address: `address${i + 1}`,
                friends: e
              }
            }
          }))
        }
      };

// callbackは適当
    dynamoDB.batchWrite(params, (e, data) => {
        if (e) {
          context.fail(e);
        } else {
          context.succeed(data);
        }
    });
};

軽い解説

Pythonでの解説で述べたようにコードやデータの値そのものに関してはスルー
ここで大事なのはAWS.DynamoDB.DocumentClientにあるbatchWriteというプロパティ(関数)
こいつの引数は結構癖があるのでClass: AWS.DynamoDB.DocumentClient — AWS SDK for JavaScriptを参照してください
Node.jsでの複数書き込みについて調べたときによく出てくるのはbatchWriteItemだったのですが、現在(2020年9月時点)のLambdaで用意されている最新のNode.jsの実行環境だとどうやらないっぽいので、動かず意外と手こずる
batchWriteItemとの大きな違いはItemのオブジェクトがDynamoDB特有の型付きのオブジェクト(name: { "S": "user" }みたいなやつ)である必要がなくなっている点
要するに書き込みたい値を気にせずそのままJSONにしてItemの値として渡せば問題なく動く
そのため、当然Map[List]という複雑な型であってもそのまま渡せば書き込み可能です

全体の補足

バッチ処理で更新(update)をしたいときはPUTを使うしかない
理由は単純でUPDATEがないからで、仕方ないので地道にPUTで記述していく必要がある
またバッチ処理内ではDELETEをすることができるので、今回はフォーカスしませんでしたが、一応あるということだけ言っておきます・・・

おわりに

いかがでしたでしょうか
複数操作についてはコードと1, 2行程度の解説がある記事はちょこちょこ見かけますが、書き込みに特化して解説されているのはあまり見かけず(あっても複雑な型でもできるのか不明であったり、そもそも実行時エラーになってしまう)、多少やってて手こずったので、備忘の意味も兼ねて書き残してみました
若干ニッチな分野かもしれないですが、少しでも役に立てて貰えれば幸いです

参考

Amazon DynamoDB — Boto3 Docs 1.14.56 documentation
Class: AWS.DynamoDB.DocumentClient — AWS SDK for JavaScript

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編nth関数の実相のアウトプット

nth関数の課題内容

   ↓
https://js-drills.com/blog/nth/

nth関数の取り組む前の状態

参考演算子が使えるんじゃないかと考えていた

nth関数に取り組んだ後の状態

どちらのやり方でもできるように理解できた

nth関数の実装コード(答えを見る前)

function nth(array, n = 0) {
  return n >= 0 ? array[n] : array[array.length + n];
}

const array = ['a', 'b', 'c', 'd'];

nth関数の実装コード(答えを見た後)

function nth(array, n = 0) {
  return n >= 0 ? array[n] : array[array.length + n];
}

const array = ['a', 'b', 'c', 'd'];

console.log( nth(array, 1) );
// => 'b'

console.log( nth(array, -2) );
// => 'c';
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編lastOf関数の実相のアウトプット

lastOf関数の課題内容

   ↓
https://js-drills.com/blog/lastindexof/

lastOf関数の取り組む前の状態

for分をどうすればいいのか悩んだ

lastOf関数に取り組んだ後の状態

let i = fromIndex; 0 <= i; i--
このように±を逆にできることを知れた。

lastOf関数の実装コード(答えを見る前)

実行できませんでした

last関数の実装コード(答えを見た後)

function lastIndexOf(array, value, fromIndex = array.length - 1) {
  for(let i = fromIndex; 0 <= i; i--) {
    if(array[i] === value) {
      return i;
    }
  }

  return -1;
}

console.log( lastIndexOf([1, 2, 1, 2], 2) );
// => 3

// Search from the `fromIndex`.
console.log( lastIndexOf([1, 2, 1, 2], 2, 2) );
// => 1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでHeadタグ内のタイトルとメタタグを設定する

課題

SPAでもページごとにタイトルやメタタグを設定したい。
SPAだし、メタタグはいらないけど、ページタイトルは変更したいかなぁって感じ。

Nuxt.jsは、title設定があるけど、同じような感じでVueでも設定したい。

TL;DR

vue-head

Headプロパティでタイトルを含めて、Headに設定できることを操作できる感じ。
934スターあるし、プロジェクトにスポンサーもついているし鉄板ぽい

https://github.com/ktquez/vue-head

vue-page-title

タイトル変更に特化した感じ。
Nuxt.jsのように、titleプロパティを設定して使えたり、Vue-routerのメタタグにTitleを設定することで反映される。
107スターなので、少しマニアックっぽい。

https://github.com/vinicius73/vue-page-title

追記

githubで検索していたら、vue-headfulというのもあった。

https://github.com/troxler/vue-headful

vue-headと機能は同じだけど、記述の仕方がコンポーネントとして記述するので、少し違和感が。。。。

ただし、219スターなので、それなりに人気があるのかな?

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編last関数の実相のアウトプット

last関数の課題内容

   ↓
https://js-drills.com/blog/last/

last関数の取り組む前の状態

なんとなく行けそうだった

last関数に取り組んだ後の状態

index番号を忘れていて-1をつけ忘れていた

last関数の実装コード(答えを見る前)

function last(array) {
  return array[array.length - 1];
}

console.log( last([1, 2, 3]) );

last関数の実装コード(答えを見た後)

function last(array) {
  return array[array.length - 1];
}

console.log( last([1, 2, 3]) );
// => 3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavascriptでEnumをシンプルに作成

仕様

  • 以下のようなオブジェクトリテラルを定義してEnumを作成する。
  • このオブジェクトリテラルにIDをラベルに変換するメソッドを設定するcreate関数を作成。
def.js
const def = {
  OPTION_A: [1, "オプションA"],
  OPTION_B: [2, "オプションB"]
};

利用方法

  • create関数(後段で定義)により仕様を満たすオブジェクトリテラルを作成。
  • 利用は以下のようになる。
use.js
//create関数により仕様を満たすオブジェクトリテラルを作成
const enum = create(def);

console.dir(enum.label(1));
//「オプションA」と表示。

console.dir(enum.OPTION_A)
//「1」と表示

オブジェクトリテラルにIDをラベルに変換するメソッドを設定するcreate関数

  • 定義は以下の通り。
creator.js
const create =  (resource) => {
  //オプションキー、ID、ラベルを格納したオブジェクトリテラルを作成
  const optionSet = {};
  const labels = {};
  for (const [option, idLabel] of Object.entries(resource)) {
    const id = idLabel[0];
    const label = idLabel[1];
    optionSet[option] = id;
    labels[id] = label;
  }
  //IDからラベルを取得する関数を作成
  const getLabel = (labels, id) => {
    return labels[id];
  };
  //オブジェクトリテラルに、IDをラベルに変換する関数を設定
  //bindで冒頭で作ったラベルオブジェクトを設定
  optionSet.label = getLabel.bind(null, labels);
  return optionSet;
}

bindを使って、オブジェクトリテラルに値を保持したメソッドを設定

  • bindを使うことで、「this」を使うようなオブジェクトリテラルを作ることができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

様々な日本語文章を、目線移動を無くし早く読めるオープンソースツール(Riot Shield)を公開しました

TL;DR

https://ampcpmgp.github.io/riot-shield/

11213.gif

概要

tdual さんのツイートを見て、目線移動無く文を読むと早く読めるということを知り、開発に至りました。
日々読んでいる記事や論文など、今より早く読めると QoL が上がるため、ワクワク感を持って取り組みました。

利用モジュール

形態素解析

形態素解析には kuromoji.js を使いました。多くの文章でより良い結果が得られて使いやすかったです。(結構前に作られたツールなのに凄い・・)
区切りの良いところで表示するために if 文を多用して力業で解決しています。感じ良く表示されるという曖昧な答えの中で、コーディングするのはだいぶ苦労しました。

ビューライブラリ

ビューライブラリには Svelte を採用しました。非常にサクサク開発出来て、書いていてとても楽しかったです。コーディングにおいて楽しさは正義。本当に素晴らしいライブラリだと思います。

UIデザイン

UIデザインにはニューモーフィズムを採用しました。 Nuemorphism UI をベースに利用し、不足分は Neumorphism.io で補いました。最初に出会ったときにコレだ!っと思っていて、何かで形にしたいと考えていました。出来上がってからも見栄えには非常に満足です。
ただ、 Nuemorphism UI 自体の css が 500kb 近くあってそれなりに重いこと、モーダルを実装するためだけに bootstrap, jquery, popper.js の依存が必要になったことで、もう少し軽量なライブラリがあったら良かったなと思いました。余裕が出来たら実装してみたいです。

その他ツール

他には自作モック作成ツールの am-mocktimes を利用したり、ユニットテストには ava を利用したりしています。利用モジュールの一覧は package.json よりご覧ください。自分はどれも便利だと感じています。

使ってみて

文章によっては一部表示が長くなったり、逆に短すぎたりすることもありますが、10記事ほど試し読みした感じでは、恩恵を十分に受けれているように感じました。これからも色々記事を読んでみて、気になったところは微調整していこうと思います。

何か要望などありましたら issue から連絡貰えると出来る限り対応しようと思います。

ブラウザ上で同じことがツールがあったらコメントで教えていただけると幸いです。(自分のググり力では見つけることが出来ませんでした)

ソースコード

https://github.com/ampcpmgp/riot-shield

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

様々な日本語文章を、目線移動無く早く読めるオープンソースツール(Riot Shield)を公開しました

TL;DR

https://ampcpmgp.github.io/riot-shield/

11213.gif

概要

tdual さんのツイートを見て、目線移動無く文を読むと早く読めるということを知り、開発に至りました。
日々読んでいる記事や論文など、今より早く読めると QoL が上がるため、ワクワク感を持って取り組みました。

利用モジュール

形態素解析

形態素解析には kuromoji.js を使いました。多くの文章でより良い結果が得られて使いやすかったです。(結構前に作られたツールなのに凄い・・)
区切りの良いところで表示するために if 文を多用して力業で解決しています。感じ良く表示されるという曖昧な答えの中で、コーディングするのはだいぶ苦労しました。

ビューライブラリ

ビューライブラリには Svelte を採用しました。非常にサクサク開発出来て、書いていてとても楽しかったです。コーディングにおいて楽しさは正義。本当に素晴らしいライブラリだと思います。

UIデザイン

UIデザインにはニューモーフィズムを採用しました。 Nuemorphism UI をベースに利用し、不足分は Neumorphism.io で補いました。最初に出会ったときにコレだ!っと思っていて、何かで形にしたいと考えていました。出来上がってからも見栄えには非常に満足です。
ただ、 Nuemorphism UI 自体の css が 500kb 近くあってそれなりに重いこと、モーダルを実装するためだけに bootstrap, jquery, popper.js の依存が必要になったことで、もう少し軽量なライブラリがあったら良かったなと思いました。余裕が出来たら実装してみたいです。

その他ツール

他には自作モック作成ツールの am-mocktimes を利用したり、ユニットテストには ava を利用したりしています。利用モジュールの一覧は package.json よりご覧ください。自分はどれも便利だと感じています。

使ってみて

文章によっては一部表示が長くなったり、逆に短すぎたりすることもありますが、10記事ほど試し読みした感じでは、恩恵を十分に受けれているように感じました。これからも色々記事を読んでみて、気になったところは微調整していこうと思います。

何か要望などありましたら issue から連絡貰えると出来る限り対応しようと思います。

ブラウザ上で同じことがツールがあったらコメントで教えていただけると幸いです。(自分のググり力では見つけることが出来ませんでした)

ソースコード

https://github.com/ampcpmgp/riot-shield

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OuDia形式ファイルのパース

OuDia形式とは

鉄道やバスの時刻表を記述するファイルフォーマット。主にOuDia系のソフトウェアで使用されている。

構文

OuDia形式のファイルは文字列で記述されているので、テキストエディタ等で開くことができる。
OuDia形式のファイルは以下の特徴を持つ。

  • ドット.から次のドット.までを1階層とした、階層構造になっている。
  • ドット.による階層構造のほかに、key=value形式でプロパティが定義できる。
  • プロパティ(=の左側)は複数回登場することがある。

構文の一例
oudiaの構文例
FileType=OuDia.1.02
Rosen.
Rosenmei=〇〇線
Eki.
Ekimei=A
Ekijikokukeisiki=Jikokukeisiki_NoboriChaku
Ekikibo=Ekikibo_Ippan
.
Eki.
Ekimei=B
Ekijikokukeisiki=Jikokukeisiki_Hatsu
Ekikibo=Ekikibo_Ippan
.
Ressyasyubetsu.
Syubetsumei=普通
JikokuhyouMojiColor=00000000
.
Dia.
DiaName=diaName
Kudari.
Ressya.
Houkou=Kudari
Syubetsu=0
EkiJikoku=1,1;001,1;010/
.
.
Nobori.
Ressya.
Houkou=Nobori
Syubetsu=0
EkiJikoku=1;001/,2,1;300/300
.
.
.
.
DispProp.
JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック
JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Bold=1
JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Itaric=1
JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Bold=1;Itaric=1
.
FileTypeAppComment=OuDia Ver. 1.02.05

これだけだと分かりにくいので、インデントを付けて見やすくしてみる。

oudiaの構文例
FileType=OuDia.1.02
Rosen.
 ├ Rosenmei=〇〇線
 ├ Eki.
 │  ├ Ekimei=A
 │  ├ Ekijikokukeisiki=Jikokukeisiki_NoboriChaku
 │  ├ Ekikibo=Ekikibo_Ippan
 ├ .
 ├ Eki.
 │  ├ Ekimei=B
 │  ├ Ekijikokukeisiki=Jikokukeisiki_Hatsu
 │  ├ Ekikibo=Ekikibo_Ippan
 ├ .
 ├ Ressyasyubetsu.
 │  ├ Syubetsumei=普通
 │  ├ JikokuhyouMojiColor=00000000
 ├ .
 ├ Dia.
 │  ├ DiaName=diaName
 │  ├ Kudari.
 │  │  ├ Ressya.
 │  │  │  ├ Houkou=Kudari
 │  │  │  ├ Syubetsu=0
 │  │  │  ├ EkiJikoku=1,1;001,1;010/
 │  │  ├ .
 │  ├ .
 │  ├ Nobori.
 │  │  ├ Ressya.
 │  │  │  ├ Houkou=Nobori
 │  │  │  ├ Syubetsu=0
 │  │  │  ├ EkiJikoku=1;001/,2,1;300/300
 │  │  ├ .
 │  ├ .
 ├ .
.
DispProp.
 ├ JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック
 ├ JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Bold=1
 ├ JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Itaric=1
 ├ JikokuhyouFont=PointTextHeight=9;Facename=MS ゴシック;Bold=1;Itaric=1
.
FileTypeAppComment=OuDia Ver. 1.02.05

OuDia形式のパース/文字列化

OuDia形式のデータはただのテキストなので、そのままだとプログラムで扱いにくい。
JSON.parse/stringifyよろしく、OuDia.parse/stringifyを自作して、ObjectとArrayの組み合わせに変換してみる。

方針

  • 基本はObject入れ子
  • 同じプロパティ名が複数回登場する可能性があるので、値は配列で保持する。
  • key=value形式のプロパティはkeyの先頭に$$を追加して、.の階層名と区別する

実装

const OuDia = {
    parse (text) {
        let current = {}
        for (const lineRaw of text.split(/\r\n|\n/)) {
            const line = lineRaw.trim()
            if (line==='') {
                continue
            } else if (line==='.') {
                const parent = current.parent
                delete current.parent
                current = parent
            } else if (line.endsWith('.')) {
                const type = line.slice(0, -1)
                const newCurrent = {parent: current}
                if (!current[type]) {current[type] = []}
                current[type].push(newCurrent)
                current = newCurrent
            } else if (line.includes('=')) {
                let [key, ...value] = line.split('=')
                key = `$$${key}`
                value = value.join('=')
                if (value.includes('=')) {
                    value = Object.fromEntries(value.split(';').map(v=>v.split('=').map(v=>v.trim())))
                }
                if (!current[key]) {current[key] = []}
                current[key].push(value)
            }
        }
        return current
    },
    stringify (obj, isReturnText=true) {
        const isObject = obj => Object.getPrototypeOf(JSON.parse(JSON.stringify(obj)))===Object.prototype
        let result = []
        for (const [key, values] of Object.entries(obj)) {
            for (let value of values) {
                if (value==null) {continue}
                if (key.startsWith('$$')) {
                    if (isObject(value)) {
                        value = Object.entries(value).map(v=>v.join('=')).join(';')
                    }
                    result.push(`${key.slice(2)}=${value}`)
                } else {
                    result.push(`${key}.`, ...this.stringify(value, false), '.')
                }
            }
        }
        if (isReturnText) {
            return result.join('\n')
        } else {
            return result
        }
    }
}

コメント書いてません、すいません。

使用例

const oudData = OuDia.parse(oudText) //=> {$$FileType: ["OuDiaSecond.1.09"], $$FileTypeAppComment: ["OuDiaSecondV2 Ver. 2.04.04"], DispProp: [{…}], Rosen: [{…}]}
const oudText = OuDia.stringify(oudData) //=> FileType=OuDiaSecond.1.09\nRosen.\nRosenmei=...
console.log(oudData.Rosen[0].$$Rosenmei[0]) //=> 〇〇線

上で書いたように値が配列で保持されているので、\$\$Rosenmei[0]みたいに毎回[0]で取りに行かなきゃいけない。\$\$\$始まりだったら\$\$の0番目を取得、みたいなルールをProxyで作ってもいいけど、そこまでするほどでもないかな…

stringifyのところで、ObjectなのかStringなのか判定する必要があったのだけど、ググっても微妙なのしか見つからなかったので、isObject関数を自作した。

const isObject = obj => Object.getPrototypeOf(JSON.parse(JSON.stringify(obj)))===Object.prototype

自作クラスとかが渡された時の挙動が不安だったので、一旦JSONを経由することで簡易的に型チェックしている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript関数ドリル】 初級編join関数の実装

join関数の課題内容

「課題内容」「解説動画」「解答例」を確認したい場合は、以下リンク先のページを参照。     ↓
https://js-drills.com/blog/initial/

join関数 に取り組む前の状態

joinメソット自体はどのようなもの知っていた

join関数に取り組んだ後の状態

swiftメソットが配列のはじめを消すことが分かった

join関数の実装コード(答えを見る前)

自力で実装できませんでした。

join関数の実装コード(答えを見た後)

function join(array, separator = ',') {
  const copiedArray = [...array];

  let joinedString = copiedArray.shift();
  for(let i = 0; i < copiedArray.length; i++) {
    joinedString += separator + copiedArray[i];
  }

  return joinedString;
}

console.log( join(['a', 'b', 'c'], '---') );
// => 'a~b~c'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【技術選定】React.js vs Vue.jsを真剣に評価してみた

目次

1. 対象読者
2. 目的
3. 技術選定の考え方
4. 評価基準
5. 結論
6. 比較評価
7. テックリード/エンジニア募集中!

1. 対象読者

React.jsとVue.jsどちらを採用するか?
を悩んでいるフロントエンドのアーキテクトや開発者向けです。

2. 目的

適切な技術選定の手助けとなれればと思います。

3. 技術選定の考え方

技術は要件を満たすための手段です。
そのため、技術選定では、求められる要件をまず明確にするべきです。
そして、要件を最も満たす技術を採用するべきです。

4. 評価基準

上記の考え方に基づき、よくある要件ごとにReact.js vs Vue.jsを比較評価しました。

  • 多角的&客観的に評価するよう心掛けました。
  • 主観的な評価の場合、個人見解である旨を明記しました。

【よくある要件をどう網羅したか】

要件は、機能要件/非機能要件に分けられます。

  • 機能要件は、多種多様なので網羅困難です。よくありそうな要件(個人見解)のみにしました。
  • 非機能要件はIPAの非機能要求グレードを参考に網羅しました。

5. 結論

  • 可用性を細かく気にする場合、Vue.jsの方が未解決バグ数は少ないです。(可用性の詳細)
  • テーブルの行入れ替えが多いシステムの場合、Vue.jsを選択した方が良いです。(性能の詳細)
  • 海外の開発者が必要な場合、React.jsを選択した方が「やや良い」です。(開発者確保の詳細)
  • 学習コストを重視する場合、React.jsを選択した方が良いです。(学習コストの詳細)
  • それ以外の場合、どちらを選択しても良いです。

【比較表】

評価項目 React Vue
可用性 ○※1
保守性
セキュリティ
性能 ○※2 ○※2
開発者の確保しやすさ
学習コスト
優れたUIの提供しやすさ
開発者が楽しい ○※3 ○※3

※1 ただし、未解決バグ数がReact.jsの方が多いです。(可用性の詳細)
※2 大きな問題はありませんが、それぞれに得意/不得意があります。(性能の詳細)
※3 個人見解です。

上記結論に至った詳細は、以下の通りです。

6. 比較評価

6.1 可用性

安定して継続的に本番稼動できる品質か?という観点で評価しました。

【結論】

引き分けです。未解決バグ数を気にされる場合は、Vue.jsの勝利です。

【理由】

どちらも本番運用実績が十分にある&十分な頻度で改修されているからです。
未解決バグ数はReact.jsの方が数字的には多いです。
個人見解としては気にするほどの差異ではありませんが、気にされる場合はVue.jsの勝利です。
詳細は以下の通りです。

【本番運用実績】

どちらも十分な実績があります。

  • React.js:Facebook,Uber,Tesla等
  • Vue.js:Google, Apple, Nintendo, Gitlab等

【未解決バグ数】

React.jsの方がやや多いです。
GitのIssuesの「bug」タグの数を見ると以下です。(2020/09時点)

※本来ならバグ率で比較したかったですが、規模が分からないので断念しました。
※バグ毎の重要度も異なりますので、バグ数はあくまでも参考値という扱いですね。

【改修頻度】

どちらもすごい頻度で改修されています。
GitのReleasesから2019年のリリース回数を数えると以下です。

6.2 保守性

【結論】

引き分けです。

【理由】

どちらもコンポーネント単位で開発できるため、保守性を確保しやすいからです。
もちろん技術選定以前に、保守性を確保した設計ができていることが大事です。

6.3 セキュリティ

【結論】

引き分けです。

【理由】

以下の通り、セキュリティ対策内容に差異が無いためです。

主にフロントエンド側のセキュリティ対策を対象に調査しました。
どちらもXSS対策が組み込まれています。
具体的には以下の通りです。

6.3.1 XSS(クロスサイトスクリプティング)

【XSSとは】

Webアプリに罠リンク等を表示できてしまう脆弱性のことです。

悪意のあるユーザが悪意のあるスクリプトやHTMLを入力したとき、
対策されていないWebアプリだと、罠リンク等が表示されてしまいます。

罠リンクをクリックしたユーザは、例えばログインセッション情報を盗まれ、不正ログイン(なりすまし)されてしまったり、様々な被害を受けます。

詳細は、「3分でわかるXSSとCSRFの違い」が分かりやすいと思います。

【React.jsのXSS対策】

対策されています。自動でサニタイズ(エスケープ)してくれます。

公式 | JSX はインジェクション攻撃を防ぐ
デフォルトでは、React DOM は JSX に埋め込まれた値をレンダリングされる前にエスケープします。このため、自分のアプリケーションで明示的に書かれたものではないあらゆるコードは、注入できないことが保証されます。レンダーの前に全てが文字列に変換されます。これは XSS (cross-site-scripting) 攻撃の防止に役立ちます。

:warning: dangerouslySetInnerHTMLを使用する場合はサニタイズされませんので、脆弱性につながるリスクがあります。スクリプトに該当する文字列をサニタイズする等の対策が必要です。

公式 | dangerouslySetInnerHTML
dangerouslySetInnerHTML は、ブラウザ DOM における innerHTML の React での代替です。一般に、コードから HTML を設定することは、誤ってあなたのユーザをクロスサイトスクリプティング (XSS) 攻撃に晒してしまいやすいため、危険です。

※GoogleデベロッパーエキスパートでAuth0アンバサダー/エキスパートであるPhilippe De Ryck氏がReact.jsのXSS対策の記事を書いており、参考になります。
Preventing XSS in React

※サニタイズとは、ユーザ入力値に含まれるHTML/JavaScript特殊文字をエスケープすることで、HTML/JavaScriptとして解釈されないようにすることです。

【Vue.jsのXSS対策】

対策されています。自動でサニタイズ(エスケープ)してくれます。

公式 | セキュリティ
テンプレートを使用する場合も、描画関数を使用する場合も、コンテンツは自動的にエスケープ処理されます。

:warning: ただし、v-htmldomPropsInnerHTMLを使用する場合はサニタイズされませんので、脆弱性につながるリスクがあります。スクリプトに該当する文字列をサニタイズする等の対策が必要です。

:warning: SSR(サーバサイドレンダリング)に乗せると、脆弱性につながる場合があります。こちらのサイトが参考になります。

6.3.2 XSS以外

【評価】

React.jsとVue.jsどちらも対策の仕組みはありません。
基本的にフロントエンドではなくバックエンドやミドルウェアで対策するものだからでしょう。

【補足】

:warning: とはいえ例えば、CSRF対策のトークンをリクエストヘッダに埋め込む等の実装は必要ですね。
サーバで発行したCSRFトークンを、リクエスト毎にヘッダに設定するイメージです。
React.js or Vue.js問わず、Axios等で実装すると思いますが、
Vue.jsならこの記事、React.jsならこの記事が、参考になるかもしれません。

6.4 性能

【結論】

引き分けです。

【理由】

一部で得意/不得意の差があるものの、処理時間/メモリ使用量ともに互角です。
詳細は以下の通りです。

【処理時間】

ベンチマークサイトによると、それぞれに得意/不得意があります。
※react-v16.4.1、vue-v2.5.16の「keyed」を比較しました。

  • React.jsは、テーブルの行入れ替えがVue.jsよりも遅いです。
  • Vue.jsは、テーブル行の部分更新がReact.jsよりも遅いです。
  • それ以外の処理時間は、どちらも互角です。

以下は、ベンチマークサイトの結果から作成した表です。
paformance.png

【メモリ使用量】

ベンチマークサイトによると、どちらも互角です。
※react-v16.4.1、vue-v2.5.16の「keyed」を比較しました。

6.5 開発者の確保しやすさ

【結論】

国内は引き分け、海外は僅差でReact.jsの勝利です。

【理由】

どちらも人気(≒開発者が多い)だからです。
Googleトレンドでは海外だとReact.jsの方が多いです。
具体的には、以下の通りです。

【人気度】

React.jsとVue.jsともに、人気度/要望度がトップです。

StackOverflow_React_Vue.PNG
https://insights.stackoverflow.com/survey/2019#technology

【GitのStar数】

どちらも同等の人気があります。

【Googleトレンド】

React.jsとVue.jsのGoogleトレンドは、国内は引き分け、海外はReact.jsの勝利です。

※国内はこちら↓

GoogleTrend_React_Vue_JP.PNG

※海外はこちら↓

GoogleTrend_React_Vue.PNG

6.6 学習コスト

【結論】

定量評価だとReact.jsの勝利、定性評価だとVue.jsの勝利(個人見解)です。
客観的に見れば、定量評価を優先してReact.jsの勝利です。

【理由】

定量評価だと、Vue.jsの方が覚える量が物理的に多いからです。
具体的には以下の通りです。

6.6.1 定量評価:React.jsの勝利

【評価】

JSX記法より、Vue.js構文の方が覚える量が物理的に多いです。
それ以外のstateやRouter等の学習量は、React.jsとVue.jsでほぼ差異はありません。
詳細は以下の通りです。

【React.jsで学習が必要なこと】

主に以下の学習が必要です。

  • JSX記法
  • state/Redux
  • Router/hook等

【Vue.jsで学習が必要なこと】

主に以下の学習が必要です。

  • Vue.js構文(各種ディレクティブ/computed/watch/method等)
  • state/Vuex
  • Router等

6.6.2 定性評価:Vue.jsの勝利(個人見解)

【評価】

Vue.jsの方が学習しやすいです。個人見解です。

【理由】

個人見解ですが、見慣れないJSX記法より、HTMLっぽく読めるVue.jsの方が、抵抗感無く学習できたからです。
※慣れてしまった今はJSX記法への抵抗感は無いですが、当初は抵抗感があったという意味です。

6.7 優れたUIの提供しやすさ

【結論】

引き分けです。

【理由】

以下の通り、「優れたUI」を実装するコストに差異が無いためです。

【優れたUIの定義】

この記事では、ユーザーが考えずに操作できるUIを「優れたUI」と定義します。
※参考:Steve Krug著「Don't Make Me Think」

優れたUIの具体例は以下です。
・どれがクリックできる要素か?が明確であること。
・説明がシンプル&明確であること。(自己満足な宣伝文句が無い)

【評価観点】

まず、設計工程で「優れたUI」が設計されている必要があります。
その前提で、React.jsとVue.jsのどちらが実装コストが少ないか?で評価します。

【評価】

実装コストに差異は無いでしょう。

  • UIフレームワークで実現できるUIデザインの場合
    • React.jsなら「Material-UI」等、Vue.jsなら「Vuetify」等で実装できます。
  • 上記で実現できない凝ったUIデザインの場合
    • React.js or Vue.jsに関わらず、UIフレームワークのカスタマイズや独自CSSの実装が必要です。

6.8 開発者が楽しい

開発者のモチベーションは大事ですね。

【結論】

引き分けです。

【理由】

開発者の主観で決まりますので引き分けです。
個人見解としては、どちらも楽しいです。

7. テックリード/エンジニア募集中!

株式会社ビジョン・コンサルティングでは、アーキテクトや開発者を募集しています!
世界をより便利にすることに情熱を燃やし、新規事業部で新サービスを一緒に開発しませんか?
詳細はこちら

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React.js vs Vue.jsを真剣に評価してみたら、巷の評価とは違う結果になった

目次

1. 対象読者
2. 目的
3. 技術選定の考え方
4. 評価基準
5. 結論 | 比較表
6. 比較評価
7. テックリード/エンジニア募集中!

1. 対象読者

React.jsとVue.jsどちらを採用するか?
を悩んでいるフロントエンドのアーキテクトや開発者向けです。

2. 目的

適切な技術選定の手助けとなれればと思います。

3. 技術選定の考え方

技術は要件を満たすための手段です。
そのため、技術選定では、求められる要件をまず明確にするべきです。
そして、要件を最も満たす技術を採用するべきです。

4. 評価基準

上記の考え方に基づき、よくある要件ごとにReact.js vs Vue.jsを比較評価しました。

  • 多角的&客観的に評価するよう心掛けました。
  • 主観的な評価の場合、個人見解である旨を明記しました。

【よくある要件をどう網羅したか】

要件は、機能要件/非機能要件に分けられます。

  • 機能要件は、多種多様なので網羅困難です。よくありそうな要件(個人見解)のみにしました。
  • 非機能要件はIPAの非機能要求グレードを参考に網羅しました。

5. 結論 | 比較表

  • 可用性を細かく気にする場合、Vue.jsの方が未解決バグ数は少ないです。(可用性の詳細)
  • テーブルの行入れ替えが多いシステムの場合、Vue.jsを選択した方が良いです。(性能の詳細)
  • 海外の開発者が必要な場合、React.jsを選択した方が「やや良い」です。(開発者確保の詳細)
  • 学習コストを重視する場合、React.jsを選択した方が良いです。(学習コストの詳細)
  • それ以外の場合、どちらを選択しても良いです。

【比較表】

評価項目 React Vue
可用性 ○※1
保守性
セキュリティ
性能 ○※2 ○※2
開発者の確保しやすさ
学習コスト
優れたUIの提供しやすさ
開発者が楽しい ○※3 ○※3

※1 ただし、未解決バグ数がReact.jsの方が多いです。(可用性の詳細)
※2 大きな問題はありませんが、それぞれに得意/不得意があります。(性能の詳細)
※3 個人見解です。

上記結論に至った詳細は、以下の通りです。

6. 比較評価

6.1 可用性

安定して継続的に本番稼動できる品質か?という観点で評価しました。

【結論】

引き分けです。未解決バグ数を気にされる場合は、Vue.jsの勝利です。

【理由】

どちらも本番運用実績が十分にある&十分な頻度で改修されているからです。
未解決バグ数はReact.jsの方が数字的には多いです。
個人見解としては気にするほどの差異ではありませんが、気にされる場合はVue.jsの勝利です。
詳細は以下の通りです。

【本番運用実績】

どちらも十分な実績があります。

  • React.js:Facebook,Uber,Tesla等
  • Vue.js:Google, Apple, Nintendo, Gitlab等

【未解決バグ数】

React.jsの方がやや多いです。
GitのIssuesの「bug」タグの数を見ると以下です。(2020/09時点)

※本来ならバグ率で比較したかったですが、規模が分からないので断念しました。
※バグ毎の重要度も異なりますので、バグ数はあくまでも参考値という扱いですね。

【改修頻度】

どちらもすごい頻度で改修されています。
GitのReleasesから2019年のリリース回数を数えると以下です。

6.2 保守性

【結論】

引き分けです。

【理由】

どちらもコンポーネント単位で開発できるため、保守性を確保しやすいからです。
もちろん技術選定以前に、保守性を確保した設計ができていることが大事です。

6.3 セキュリティ

【結論】

引き分けです。

【理由】

以下の通り、セキュリティ対策内容に差異が無いためです。

主にフロントエンド側のセキュリティ対策を対象に調査しました。
どちらもXSS対策が組み込まれています。
具体的には以下の通りです。

6.3.1 XSS(クロスサイトスクリプティング)

【XSSとは】

Webアプリに罠リンク等を表示できてしまう脆弱性のことです。

悪意のあるユーザが悪意のあるスクリプトやHTMLを入力したとき、
対策されていないWebアプリだと、罠リンク等が表示されてしまいます。

罠リンクをクリックしたユーザは、例えばログインセッション情報を盗まれ、不正ログイン(なりすまし)されてしまったり、様々な被害を受けます。

詳細は、「3分でわかるXSSとCSRFの違い」が分かりやすいと思います。

【React.jsのXSS対策】

対策されています。自動でサニタイズ(エスケープ)してくれます。

公式 | JSX はインジェクション攻撃を防ぐ
デフォルトでは、React DOM は JSX に埋め込まれた値をレンダリングされる前にエスケープします。このため、自分のアプリケーションで明示的に書かれたものではないあらゆるコードは、注入できないことが保証されます。レンダーの前に全てが文字列に変換されます。これは XSS (cross-site-scripting) 攻撃の防止に役立ちます。

:warning: dangerouslySetInnerHTMLを使用する場合はサニタイズされませんので、脆弱性につながるリスクがあります。スクリプトに該当する文字列をサニタイズする等の対策が必要です。

公式 | dangerouslySetInnerHTML
dangerouslySetInnerHTML は、ブラウザ DOM における innerHTML の React での代替です。一般に、コードから HTML を設定することは、誤ってあなたのユーザをクロスサイトスクリプティング (XSS) 攻撃に晒してしまいやすいため、危険です。

※GoogleデベロッパーエキスパートでAuth0アンバサダー/エキスパートであるPhilippe De Ryck氏がReact.jsのXSS対策の記事を書いており、参考になります。
Preventing XSS in React

※サニタイズとは、ユーザ入力値に含まれるHTML/JavaScript特殊文字をエスケープすることで、HTML/JavaScriptとして解釈されないようにすることです。

【Vue.jsのXSS対策】

対策されています。自動でサニタイズ(エスケープ)してくれます。

公式 | セキュリティ
テンプレートを使用する場合も、描画関数を使用する場合も、コンテンツは自動的にエスケープ処理されます。

:warning: ただし、v-htmldomPropsInnerHTMLを使用する場合はサニタイズされませんので、脆弱性につながるリスクがあります。スクリプトに該当する文字列をサニタイズする等の対策が必要です。

:warning: SSR(サーバサイドレンダリング)に乗せると、脆弱性につながる場合があります。こちらのサイトが参考になります。

6.3.2 XSS以外

【評価】

React.jsとVue.jsどちらも対策の仕組みはありません。
基本的にフロントエンドではなくバックエンドやミドルウェアで対策するものだからでしょう。

【補足】

:warning: とはいえ例えば、CSRF対策のトークンをリクエストヘッダに埋め込む等の実装は必要ですね。
サーバで発行したCSRFトークンを、リクエスト毎にヘッダに設定するイメージです。
React.js or Vue.js問わず、Axios等で実装すると思いますが、
Vue.jsならこの記事、React.jsならこの記事が、参考になるかもしれません。

6.4 性能

【結論】

引き分けです。

【理由】

一部で得意/不得意の差があるものの、処理時間/メモリ使用量ともに互角です。
詳細は以下の通りです。

【処理時間】

ベンチマークサイトによると、それぞれに得意/不得意があります。
※react-v16.4.1、vue-v2.5.16の「keyed」を比較しました。

  • React.jsは、テーブルの行入れ替えがVue.jsよりも遅いです。
  • Vue.jsは、テーブル行の部分更新がReact.jsよりも遅いです。
  • それ以外の処理時間は、どちらも互角です。

以下は、ベンチマークサイトの結果から作成した表です。
paformance.png

【メモリ使用量】

ベンチマークサイトによると、どちらも互角です。
※react-v16.4.1、vue-v2.5.16の「keyed」を比較しました。

6.5 開発者の確保しやすさ

【結論】

国内は引き分け、海外は僅差でReact.jsの勝利です。

【理由】

どちらも人気(≒開発者が多い)だからです。
Googleトレンドでは海外だとReact.jsの方が多いです。
具体的には、以下の通りです。

【人気度】

React.jsとVue.jsともに、人気度/要望度がトップです。

StackOverflow_React_Vue.PNG
https://insights.stackoverflow.com/survey/2019#technology

【GitのStar数】

どちらも同等の人気があります。

【Googleトレンド】

React.jsとVue.jsのGoogleトレンドは、国内は引き分け、海外はReact.jsの勝利です。

※国内はこちら↓

GoogleTrend_React_Vue_JP.PNG

※海外はこちら↓

GoogleTrend_React_Vue.PNG

6.6 学習コスト

【結論】

定量評価だとReact.jsの勝利、定性評価だとVue.jsの勝利(個人見解)です。
客観的に見れば、定量評価を優先してReact.jsの勝利です。

【理由】

定量評価だと、Vue.jsの方が覚える量が物理的に多いからです。
具体的には以下の通りです。

6.6.1 定量評価:React.jsの勝利

【評価】

JSX記法より、Vue.js構文の方が覚える量が物理的に多いです。
それ以外のstateやRouter等の学習量は、React.jsとVue.jsでほぼ差異はありません。
詳細は以下の通りです。

【React.jsで学習が必要なこと】

主に以下の学習が必要です。

  • JSX記法
  • state/Redux
  • Router/hook等

【Vue.jsで学習が必要なこと】

主に以下の学習が必要です。

  • Vue.js構文(各種ディレクティブ/computed/watch/method等)
  • state/Vuex
  • Router等

6.6.2 定性評価:Vue.jsの勝利(個人見解)

【評価】

Vue.jsの方が学習しやすいです。個人見解です。

【理由】

個人見解ですが、見慣れないJSX記法より、HTMLっぽく読めるVue.jsの方が、抵抗感無く学習できたからです。
※慣れてしまった今はJSX記法への抵抗感は無いですが、当初は抵抗感があったという意味です。

6.7 優れたUIの提供しやすさ

【結論】

引き分けです。

【理由】

以下の通り、「優れたUI」を実装するコストに差異が無いためです。

【優れたUIの定義】

この記事では、ユーザーが考えずに操作できるUIを「優れたUI」と定義します。
※参考:Steve Krug著「Don't Make Me Think」

優れたUIの具体例は以下です。
・どれがクリックできる要素か?が明確であること。
・説明がシンプル&明確であること。(自己満足な宣伝文句が無い)

【評価観点】

まず、設計工程で「優れたUI」が設計されている必要があります。
その前提で、React.jsとVue.jsのどちらが実装コストが少ないか?で評価します。

【評価】

実装コストに差異は無いでしょう。

  • UIフレームワークで実現できるUIデザインの場合
    • React.jsなら「Material-UI」等、Vue.jsなら「Vuetify」等で実装できます。
  • 上記で実現できない凝ったUIデザインの場合
    • React.js or Vue.jsに関わらず、UIフレームワークのカスタマイズや独自CSSの実装が必要です。

6.8 開発者が楽しい

開発者のモチベーションは大事ですね。

【結論】

引き分けです。

【理由】

開発者の主観で決まりますので引き分けです。
個人見解としては、どちらも楽しいです。

7. テックリード/エンジニア募集中!

株式会社ビジョン・コンサルティングでは、アーキテクトや開発者を募集しています!
世界をより便利にすることに情熱を燃やし、新規事業部で新サービスを一緒に開発しませんか?
詳細はこちら

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

簡易LISP処理系の実装例(JavaScript版)

Python版Scheme版C言語版Ruby版に続く第五の実装例です.しばらくはここまでかな.

この記事は,下記拙作記事のJavaScript版を抜粋・修正したものを利用した,簡易LISP処理系の実装例をまとめたものです.

主旨およびプログラムコードはPython版とほぼ同じで,最低限の機能をもったLISP処理系の実装の場合,本体である超循環評価器(meta-circular evaluator)の作成はとても簡単であり,むしろ,字句・構文解析を行うS式入出力やリスト処理実装の方が開発言語ごとの手間が多く,それが敷居になっている人向けにまとめています.

処理系の概要

実行例は次の通り.Node.js 10.21にて確認.

> s_rep("(car (cdr '(10 20 30)))")
'20'
> s_rep("((lambda (x) (car (cdr x))) '(abc def ghi))")
'def'
> s_rep("((lambda (f x y) (f x (f y '()))) 'cons '10 '20)")
'(10 20)'
> s_rep("((lambda (f x y) (f x (f y '()))) \
...       '(lambda (x y) (cons x (cons y '()))) \
...       '10 '20)")
'(10 (20 ()))'
> s_rep("((lambda (assoc k v) (assoc k v)) \
...      '(lambda (k v) \
.....       (cond ((eq v '()) nil) \
.......           ((eq (car (car v)) k) \
.........          (car v)) \
.......           ('t (assoc k (cdr v))))) \
...      'Orange \
...      '((Apple . 120) (Orange . 210) (Lemmon . 180)))")
'(Orange . 210)'

実装内容は次の通り.

  • "McCarthy's Original Lisp"をベースにした超循環評価器
  • 数字を含むアトムは全てシンボルとし,変数の値とする場合はquote')を使用
  • 構文としてquoteの他,condlambdaが使用可能
  • 組込関数:atom eq cons car cdr(内部でコンスセルを作成)
  • 真偽値はt(真)およびnil(偽)=空リスト=null
  • エラーチェックなし,モジュール化なし,ガーベジコレクションなし

"McCarthy's Original Lisp"の詳細についてはScheme版記事を参照.ダイナミックスコープということもあり,実行例ではlambda式をletrec(Scheme)やlabels(Common Lisp)などの代わりに使用しています.

実装例

ソースコード一式

jmclisp.js
//
// JMC Lisp: defined in McCarthy's 1960 paper,
// with S-expression input/output and basic list processing
//


// basic list processing: cons, car, cdr, eq, atom
function cons(x, y) { return Object.freeze([x, y]); }
function car(s) { return s[0]; }
function cdr(s) { return s[1]; }
function eq(s1, s2) { return s1 === s2; }
function atom(s) { return typeof s == 'string' || eq(s, null) || eq(s, true) || eq(s, false); }


// S-expression input: s_read

function s_lex(s) {
  s = s.replace(/(\(|\)|\'|\,)/g, ' $1 ');
  return s.split(/\s+/).filter(x => x != '');
}

function s_syn(s) {
  function quote(x, s) {
    if (s.length != 0 && s.slice(-1)[0] == '\'') {
      s.pop();
      return cons("quote", cons(x, null));
    } else {
      return x
    }
  }
  var t = s.pop();
  if (t == ')') {
    var r = null;
    while (s.slice(-1)[0] != '(') {
      if (s.slice(-1)[0] == '.') {
        s.pop();
        r = cons(s_syn(s), car(r));
      } else {
        r = cons(s_syn(s), r);
      }
    }
    s.pop();
    return quote(r, s);
  } else {
    return quote(t, s);
  }
}

function s_read(s) { return s_syn(s_lex(s)); }


// S-expression output: s_string

function s_strcons(s) {
  var sa_r = s_string(car(s));
  var sd = cdr(s);
  if (eq(sd, null)) {
    return sa_r;
  } else if (atom(sd)) {
    return sa_r + ' . ' + sd;
  } else {
    return sa_r + ' ' + s_strcons(sd);
  }
}

function s_string(s) {
  if      (eq(s, null))  return '()';
  else if (eq(s, true))  return 't';
  else if (eq(s, false)) return 'nil';
  else if (atom(s))
    return s;
  else
    return '(' + s_strcons(s) + ')';
}


// JMC Lisp meta-circular evaluator: s_eval

function caar(x) { return car(car(x)); }
function cadr(x) { return car(cdr(x)); }
function cadar(x) { return car(cdr(car(x))); }
function caddr(x) { return car(cdr(cdr(x))); }
function caddar(x) { return car(cdr(cdr(car(x)))); }

function s_null(x) { return eq(x, null); }

function s_append(x, y) {
  if (s_null(x)) return y;
  else return cons(car(x), s_append(cdr(x), y));
}

function s_list(x, y) { return cons(x, cons(y, null)); }

function s_pair(x, y) {
  if (s_null(x) && s_null(y)) return null;
  else if (!atom(x) && !atom(y))
    return cons(s_list(car(x), car(y)), s_pair(cdr(x), cdr(y)));
}

function s_assoc(x, y) {
  if (eq(caar(y), x)) return cadar(y);
  else return s_assoc(x, cdr(y));
}

function s_eval(e, a) {
  if      (eq(e, "t"))   return true;
  else if (eq(e, "nil")) return false
  else if (atom(e)) return s_assoc(e, a);
  else if (atom(car(e))) {
    if      (eq(car(e), "quote")) return cadr(e);
    else if (eq(car(e), "atom"))  return atom(s_eval(cadr(e), a));
    else if (eq(car(e), "eq"))    return eq(  s_eval(cadr(e), a),
                                              s_eval(caddr(e), a));
    else if (eq(car(e), "car"))   return car( s_eval(cadr(e), a));
    else if (eq(car(e), "cdr"))   return cdr( s_eval(cadr(e), a));
    else if (eq(car(e), "cons"))  return cons(s_eval(cadr(e), a),
                                             s_eval(caddr(e), a));
    else if (eq(car(e), "cond"))  return evcon(cdr(e), a);
    else return s_eval(cons(s_assoc(car(e), a), cdr(e)), a);
  } else if (eq(caar(e), "lambda"))
      return s_eval(caddar(e),
                    s_append(s_pair(cadar(e), evlis(cdr(e), a)), a));
  else console.log("Error");
}

function evcon(c, a) {
  if (s_eval(caar(c), a)) return s_eval(cadar(c), a);
  else return evcon(cdr(c), a);
}

function evlis(m, a) {
  if (s_null(m)) return null;
  else return cons(s_eval(car(m), a), evlis(cdr(m), a));
}


// REP (no Loop): s_rep
function s_rep(e) { return s_string(s_eval(s_read(e), s_read("()"))); }

解説

  • リスト処理:cons car cdr eq atom
    先の記事より,ほぼそのまま抜粋.

  • S式入力:s_read
    Python版とほぼ同じ.先の記事から,字句解析部を()および'の識別に変更(s_lex),抽象構文木生成部をドット対とクォート記号対応としつつ,リスト処理関数でコンスセルによる構文木を生成するよう変更(s_syn),それらをまとめたS式入力関数s_readを定義.

  • S式出力:s_string
    ほぼPython版の書き換え.内部ではnullである空リストは()を,真偽値はt nilを出力するよう設定.

  • 超循環評価器:s_eval+ユーティリティ関数
    こちらも,ほぼPython版の書き換え."McCarthy's Original Lisp"をベースにs_eval関数およびユーティリティ関数を作成.

  • REP (no Loop):s_rep
    s_reads_evals_stringをまとめたs_repを定義.

備考

記事に関する補足

  • 超循環評価器のみの場合,約50行/1900バイトほど.Python版とほぼ同じかな.

更新履歴

2020-09-11:eqの定義について,==ではなく===を使用(コメントより)
2020-09-11:初版公開

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】dotenvを使用して環境変数を設定する方法&設定した環境変数をHerokuにも適用する方法

node.jsで環境変数を設定する場合、
「dotenv」を使用すれば楽に設定することができる
色々と遠回りというか勘違いをして時間がかかってしまったのでメモ

手順

  1. ルートディレクトリに「.env」という名前のファイルを作成
  2. ターミナルにて$npm install dotenv --saveを実行
  3. 1で作成した.envに環境変数として使用したいkey&valueを以下のように記載
.env
NODE_USER_ID=12345678
NODE_PASSWORD=abcdefgh

4.環境変数を使用したい箇所に以下のように記載

app.js
require('dotenv').config();
let userId = process.env.NODE_USER_ID;
// 12345678
console.log(userId);

以上の手順で環境変数を設定できます。
※.gitignoreに .envを追記するのを忘れないようにしてください。
記載済みであれば大丈夫です。

あくまでもローカル上での設定となりますので、
Herokuにデプロイしているアプリに対して環境変数を適用する方法を以下に記載しました。

Herokuにデプロイする場合

便利ですよね、heroku。
Githubとherokuを連携している場合、.envファイルをプッシュしていないのでどうしたものかと・・・。

Config Varsを設定する

1.heroku上部メニューより、Settingsへ遷移
heroku_menu.png

2.「Reveal Config Vars」ボタンをクリック
reveal_config.png

3.お誂え向きなKEYとVALUEを入力できるフォームが出てくるので、
.envに記載している内容と同等のものを記載し、ADDをクリック
key_value.png

以上で、.envをgitに上げなくてもHerokuにデプロイしたアプリから、
環境変数を呼び出すことが可能となります。

備考

アプリ公開しました!よろしければインストールお願いします。
とらんぽ

Twitter始めました!よろしければフォローお願いします。
@yajima_tohshu

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】dotenvを使用して環境変数を設定&設定した環境変数をHerokuにも適用する方法

node.jsで環境変数を設定する場合、
「dotenv」を使用すれば楽に設定することができる
色々と遠回りというか勘違いをして時間がかかってしまったのでメモ

手順

  1. ルートディレクトリに「.env」という名前のファイルを作成
  2. ターミナルにて$npm install dotenv --saveを実行
  3. 1で作成した.envに環境変数として使用したいkey&valueを以下のように記載
.env
NODE_USER_ID=12345678
NODE_PASSWORD=abcdefgh

4.環境変数を使用したい箇所に以下のように記載

app.js
require('dotenv').config();
let userId = process.env.NODE_USER_ID;
// 12345678
console.log(userId);

以上の手順で環境変数を設定できます。
※.gitignoreに .envを追記するのを忘れないようにしてください。
記載済みであれば大丈夫です。

あくまでもローカル上での設定となりますので、
Herokuにデプロイしているアプリに対して環境変数を適用する方法を以下に記載しました。

Herokuにデプロイする場合

便利ですよね、heroku。
Githubとherokuを連携している場合、.envファイルをプッシュしていないのでどうしたものかと・・・。

Config Varsを設定する

1.heroku上部メニューより、Settingsへ遷移
heroku_menu.png

2.「Reveal Config Vars」ボタンをクリック
reveal_config.png

3.お誂え向きなKEYとVALUEを入力できるフォームが出てくるので、
.envに記載している内容と同等のものを記載し、ADDをクリック
key_value.png

以上で、.envをgitに上げなくてもHerokuにデプロイしたアプリから、
環境変数を呼び出すことが可能となります。

備考

アプリ公開しました!よろしければインストールお願いします。
とらんぽ

Twitter始めました!よろしければフォローお願いします。
@yajima_tohshu

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】何気なく使っているthisとかcontextとかって結局なんなの??

オブジェクト 指すもの 使う場面
this Vueインスタンス 現Vueインスタンスを指定する時
context ・Vueインスタンス生成前のインスタンス・Storeインスタンス ・まだVueインスタンスが作られる前にVueインスタンスを使いたい時。・Storeメソッドを使いたい時。

thisオブジェクト

data

Vueインスタンスを指す

data () {
  return {
    email: ''
  }
},
methods: {
  into () {
    this.email = 'foo@bar.com'
  }
}

Vueメソッド

Vueインスタンスを指す

methods: {
  async login () {
    const res = await this.$axios.$get('/login')
  }
}

contextオブジェクト

asyncData

仮のVueインスタンスを指す

async asyncData(context) {
  const posts = await context.$axios.$get('/posts')
  return { posts }
},

{ posts }とは{ posts: posts }の略

async asyncData (context) {
  const res = await context.$axios.$get('/posts/' + context.params.id)
  return { post: res.data }
}

Vuexのstoreメソッド

Storeインスタンスを指す

export const actions = {
  async login (context) {
    await context.commit('switchLogin')
  })
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編head関数の実相のアウトプット

initial関数の課題内容

詳細はこちら
   ↓
https://js-drills.com/blog/initial/

initial関数の取り組む前の状態

配列の一番初めに何かをするのかと思った

initial関数に取り組んだ後の状態

分かった

initial関数の実装コード(答えを見る前)

実行できませんでした

initial関数の実装コード(答えを見た後)

function initial(array) {
  const copiedArray = [...array];
  copiedArray.pop();

  return copiedArray;
}

const numbers = [1, 2, 3];
console.log( initial(numbers) );
// => [1, 2]

console.log('number : ', numbers);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編initial関数の実相のアウトプット

initial関数の課題内容

詳細はこちら
   ↓
https://js-drills.com/blog/initial/

initial関数の取り組む前の状態

配列の一番初めに何かをするのかと思った

initial関数に取り組んだ後の状態

popメソットが配列の最後を取り除くことが分かった

initial関数の実装コード(答えを見る前)

実行できませんでした

initial関数の実装コード(答えを見た後)

function initial(array) {
  const copiedArray = [...array];
  copiedArray.pop();

  return copiedArray;
}

const numbers = [1, 2, 3];
console.log( initial(numbers) );
// => [1, 2]

console.log('number : ', numbers);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編head関数の実相のアウトプット

indexOf関数の課題内容

詳細はこちら
   ↓
https://js-drills.com/blog/indexof/

indexOf関数の取り組む前の状態

見てなんとなくわかった

indexOf関数に取り組んだ後の状態

return -1以外は分かった

indexOf関数の実装コード(答えを見る前)

function indexOf(array, value, secoundIndex = 0) {
  for(let i = secoundIndex; i < array.length; i++) {
    if(array[i] === value) {
      return i;
    }}}

indexOf関数の実装コード(答えを見た後)

function indexOf(array, value, fromIndex = 0) {
  for(let i = fromIndex; i < array.length; i++) {
    if(array[i] === value) {
      return i;
    }
  }

  return -1;
}

// console.log( indexOf([1, 2, 1, 2], 2) );
// => 1

// Search from the `fromIndex`.
console.log( indexOf([1, 2, 1, 2], 3, 2) );
// => 3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript関数ドリル 初級編indexOf関数の実相のアウトプット

indexOf関数の課題内容

詳細はこちら
   ↓
https://js-drills.com/blog/indexof/

indexOf関数の取り組む前の状態

見てなんとなくわかった

indexOf関数に取り組んだ後の状態

return -1以外は分かった

indexOf関数の実装コード(答えを見る前)

function indexOf(array, value, secoundIndex = 0) {
  for(let i = secoundIndex; i < array.length; i++) {
    if(array[i] === value) {
      return i;
    }}}

indexOf関数の実装コード(答えを見た後)

function indexOf(array, value, fromIndex = 0) {
  for(let i = fromIndex; i < array.length; i++) {
    if(array[i] === value) {
      return i;
    }
  }

  return -1;
}

// console.log( indexOf([1, 2, 1, 2], 2) );
// => 1

// Search from the `fromIndex`.
console.log( indexOf([1, 2, 1, 2], 3, 2) );
// => 3
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】クラスコンポーネント /関数コンポーネントの違いと使い分け。なぜ 関数コンポーネントが好まれるのか。

はじめに

どうもシュータといいます!
2019年、 React 16.8のリリースに伴いHooksが導入され、
stateやlife cycle methodを使用する際はClass Componentを使用した方が良いという判断が実情に合わなくなってしまったように思います。
この記事では今一度、クラスコンポーネントと関数コンポーネントの違いを明らかにしつつ、Hooksの導入に伴いどう変化したか、
そして関数コンポーネントが好まれやすいのは何故かという理由を記載します。

関数コンポーネントとクラスコンポーネント

まず関数コンポーネントとクラスコンポーネントで
シンプルなコンポーネントを作成した例を示します。

関数コンポーネント
function Welcome(props) {
  return <h1>Hello, {props.name} </h1>;
}

こちらは基本的なjavascriptの関数と同様です。
propsを受け取り、使用することが可能です。

クラスコンポーネント
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

こちらはReactのCompoentクラスを継承しています。
クラスコンポーネントでもpropsを受け取り
使用することは可能です。

Hooks導入以前の関数コンポーネントとクラスコンポーネントの主な違い

クラスコンポーネントでは関数コンポーネントと異なり、コンポーネント内で下記の二つを使用することができます。

  • state(状態管理)
  • Lifecycle method

stateの例

class Welcome extends React.Component {
  constructor(props){
    super(props)
    this.state = {
    name: "hoge"
    }
  }
  render() {
    return <h1>Hello, {this.state.name}</h1>;
  }
}

Lifecycle methodの例

詳しくはこちらをご覧ください。

class Welcome extends React.Component {
  constructor(props){
    super(props)
    this.state = {
    name: "hoge"
    }
  }

 componentDidMount() {
   this.setState({name:"fuga"})
  }
 
  render() {
    return <h1>Hello, {this.state.name}</h1>;
  }
}

Hooks導入後

HooKs導入前は関数コンポーネントとクラスコンポーネントは
前述のような違いがありましたが
Hooksの登場により、関数コンポーネントでも
状態管理やライフサイクルの機能を使用できるようになりました。
そしてそれらはクラスコンポーネントで記述するよりも
簡潔に書くことが可能です。

状態管理

Hooksを利用した関数コンポーネントで状態管理する例

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

同様のコンポーネントをクラスコンポーネントで書いた場合
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

ライフサイクル

Hooksを利用した関数コンポーネントでライフサイクルメソッドを使う例
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );

同様のコンポーネントをクラスコンポーネントで書いた場合
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

なぜ関数コンポーネントが好まれるか

  • コードが簡潔にかける

これまでみてきたように
関数コンポーネントで記述することによってより見通しの良いコードがかけることに気付いていただけたと思います。

  • Viewからの複雑さを排除することも可能となる

これまで示した例だけでなく、データフロー全体の設計からの観点となりますがコンポーネントになるべく状態を持たせない設計や、view以外ファイルにAPI通信などの副作用を局所化させる設計を行うことより
viewの責務を軽くすることも可能です。
将来的にReact以外のライブラリ、フレームワークを使用することになった際も乗り換えやすくなることが期待できます。

 最後に

読んでくださった方ありがとうございました!

誤字脱字・間違い等ございましたら、指摘して頂けますと幸いです!
また、説明にわかりづらい点がございましたら、改善致しますのでぜひコメントください。

reference

https://reactjs.org/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む