- 投稿日:2020-09-08T23:17:04+09:00
高階関数 Higher-Order Functions とは
この記事を読むとわかること
JavaScript初心者が高階関数(Higher-Order Functions)を理解できます。
高階関数 Higher-Order Functions とは
高階関数 Higher-Order Functions とは
高階関数とは、次のいずれかを実行する関数です。
- 関数を引数として扱う
- 関数を戻り値として扱う
高階関数を使うことのメリット
- 抽象化により、人間の読者が簡単に再利用、デバッグ、理解できる方法で複雑なコードを書くことができます
- 新しい変数に再割り当てするなど、他のタイプのデータと同じように関数を操作できます
- 機能のまとまりを細切れに保つことにより、テストが容易になります
第一級関数 first-class function とは
高階関数を理解する上でJavaScriptの関数の持つ基本の性質を知る必要があり、第一級関数 first-class function について触れておきます。
第一級関数とは、第一級オブジェクトとして扱うことのできる関数のことであり、JavaScriptがサポートしています。
第一級関数は他のすべての変数と同じように扱われます。 よってこれらを引数として関数に渡したり、他の関数から値として返したり、変数やデータ構造に割り当てたりすることができます。第一級関数の複雑さ(関数の引数が関数..)に特に制限が設けられていない時,プログラミング言語が高階関数(higher-order function) をサポートしている、と表現できます。第一級関数は、高階関数のような形で日常的に用いられます。
以下は、関数を変数に割り当てる例です。
関数を変数に割り当てる例function greet(name) { console.log('Hello ' + name) } greet(“John”) // “Hello John” var sayHello = greet sayHello(“Alex”) // “Hello Alex”高階関数の例
以下にて高階関数の例を2つ見てみましょう。
1. 配列の内容を順に処理するforEachメソッド
以下は、配列の要素を順番に取り出すための高階関数です。
配列の内容を順に処理するforEachメソッドarray.forEach(callback [,that]) // array : 配列オブジェクト // callback : 個々の要素を処理するための関数 // 関数callbackの中でthisが示すオブジェクト以下は処理用の関数として、配列の要素を二乗するコールバック関数です。
コールバック関数とは、呼び出し先の関数の中で呼び出される関数のことです。
この関数では配列の中身を受け取れるように引数value(要素の値)
,index(インデックス番号)
,array(元の配列)
を指定する必要があります。配列の要素を二乗するコールバック関数var data = [1, 2, 3, 4]; data.forEach(function(value,index,array){ console.log(value * value);// 結果 1, 4, 9, 16 });2. 配列を指定されたルールで加工するmapメソッド
以下は、配列の要素を順番に取り出すための高階関数です。
配列を指定されたルールで加工するmapメソッドarray.map(callback [,that]) // array : 配列オブジェクト // callback : 個々の要素を加工するための関数 // 関数callbackの中でthisが示すオブジェクト以下は処理用の関数として、配列の要素を加工した結果をまとめて新たな配列として返すコールバック関数です。
この関数では配列の中身を受け取れるように引数value(要素の値)
,index(インデックス番号)
,array(元の配列)
を指定する必要があります。配列の要素を二乗した結果を新たな配列として返すコールバック関数var data = [1, 2, 3, 4]; var result = data.map(function(value,index,array) { return value * value ; }); console.log(result); // 結果 : [1, 4, 9, 16]
上記の2例に共通して言える事ですが、高階関数から呼ばれるコールバック関数は自由に差し替える事ができます。
これこそが高階関数を使う理由です。高階関数を使わない場合と使う場合の違い
元の配列の各値を2倍した新しい配列を作成するコードで違いを見てみます。
高階関数を使わない場合const arr1 = [1, 2, 3]; const arr2 = []; for(let i = 0; i < arr1.length; i++) { arr2.push(arr1[i] * 2); } // prints [ 2, 4, 6 ] console.log(arr2);高階関数を使う場合const arr1 = [1, 2, 3]; const arr2 = arr1.map(function(item) { return item * 2; }); console.log(arr2);参考情報
Higher Order Functions in JavaScript
Higher-order functions in Javascript
- 投稿日:2020-09-08T23:17:04+09:00
JavaScript 高階関数 Higher-Order Functions とは
この記事を読むとわかること
JavaScript初心者が高階関数(Higher-Order Functions)を理解できます。
高階関数 Higher-Order Functions とは
高階関数 Higher-Order Functions とは
高階関数とは、次のいずれかを実行する関数です。
- 関数を引数として扱う
- 関数を戻り値として扱う
高階関数を使うことのメリット
- 抽象化により、人間の読者が簡単に再利用、デバッグ、理解できる方法で複雑なコードを書くことができます
- 新しい変数に再割り当てするなど、他のタイプのデータと同じように関数を操作できます
- 機能のまとまりを細切れに保つことにより、テストが容易になります
第一級関数 first-class function とは
高階関数を理解する上でJavaScriptの関数の持つ基本の性質を知る必要があり、第一級関数 first-class function について触れておきます。
第一級関数とは、第一級オブジェクトとして扱うことのできる関数のことであり、JavaScriptがサポートしています。
第一級関数は他のすべての変数と同じように扱われます。 よってこれらを引数として関数に渡したり、他の関数から値として返したり、変数やデータ構造に割り当てたりすることができます。第一級関数の複雑さ(関数の引数が関数..)に特に制限が設けられていない時,プログラミング言語が高階関数(higher-order function) をサポートしている、と表現できます。第一級関数は、高階関数のような形で日常的に用いられます。
以下は、関数を変数に割り当てる例です。
関数を変数に割り当てる例function greet(name) { console.log('Hello ' + name) } greet(“John”) // “Hello John” var sayHello = greet sayHello(“Alex”) // “Hello Alex”高階関数の例
以下にて高階関数の例を2つ見てみましょう。
1. 配列の内容を順に処理するforEachメソッド
以下は、配列の要素を順番に取り出すための高階関数です。
配列の内容を順に処理するforEachメソッドarray.forEach(callback [,that]) // array : 配列オブジェクト // callback : 個々の要素を処理するための関数 // 関数callbackの中でthisが示すオブジェクト以下は処理用の関数として、配列の要素を二乗するコールバック関数です。
コールバック関数とは、呼び出し先の関数の中で呼び出される関数のことです。
この関数では配列の中身を受け取れるように引数value(要素の値)
,index(インデックス番号)
,array(元の配列)
を指定する必要があります。配列の要素を二乗するコールバック関数var data = [1, 2, 3, 4]; data.forEach(function(value,index,array){ console.log(value * value);// 結果 1, 4, 9, 16 });2. 配列を指定されたルールで加工するmapメソッド
以下は、配列の要素を順番に取り出すための高階関数です。
配列を指定されたルールで加工するmapメソッドarray.map(callback [,that]) // array : 配列オブジェクト // callback : 個々の要素を加工するための関数 // 関数callbackの中でthisが示すオブジェクト以下は処理用の関数として、配列の要素を加工した結果をまとめて新たな配列として返すコールバック関数です。
この関数では配列の中身を受け取れるように引数value(要素の値)
,index(インデックス番号)
,array(元の配列)
を指定する必要があります。配列の要素を二乗した結果を新たな配列として返すコールバック関数var data = [1, 2, 3, 4]; var result = data.map(function(value,index,array) { return value * value ; }); console.log(result); // 結果 : [1, 4, 9, 16]
上記の2例に共通して言える事ですが、高階関数から呼ばれるコールバック関数は自由に差し替える事ができます。
これこそが高階関数を使う理由です。高階関数を使わない場合と使う場合の違い
元の配列の各値を2倍した新しい配列を作成するコードで違いを見てみます。
高階関数を使わない場合const arr1 = [1, 2, 3]; const arr2 = []; for(let i = 0; i < arr1.length; i++) { arr2.push(arr1[i] * 2); } // prints [ 2, 4, 6 ] console.log(arr2);高階関数を使う場合const arr1 = [1, 2, 3]; const arr2 = arr1.map(function(item) { return item * 2; }); console.log(arr2);参考情報
Higher Order Functions in JavaScript
Higher-order functions in Javascript
- 投稿日:2020-09-08T23:16:59+09:00
form_tag内のsubmitが反応しないときの対処
- 投稿日:2020-09-08T22:54:45+09:00
Railsのformを素のJavaScriptで送信する
やりたかったこと
form送信後にJS側で何かしらの処理をしたい。
方法
fetchAPIのコールバックチェーンで行うことができる。
turbolinks
やrails-ujs
は使用しないので、remote: true
を使ったAjax通信とも異なるやり方。「form送信後のJS処理」を、
当初form.submit() ~ setTimeout()
のような感じで書いたが、実行順序が担保されなかったので書き直した。ポイント
fetch
では
1.credentials
に'same-origin'
2.body
にnew FormData(form)
を指定。
= f.submit
や%button{ type: 'submit' }
を使うと Railsによってリクエストが飛ばされてしまうので、%button{ type: 'button' }
を使って送信ボタンをクリックしたときにJavaScriptを呼び出すようにしている。サンプル
あまりサンプルがなかった気がするのでさらっと残す。
送信ボタンを押した後に二度押せないようにする処理も入れてるが、もっとちゃんとした書き方はあるのかも。const userForm = document.getElementById('user-form'); const submitBtn = document.getElementById('submit-btn'); submitBtn.addEventListener('click', () => { submitBtn.textContent = '送信中...'; submitBtn.disabled = true; fetch('/users', { method: 'POST', credentials: 'same-origin', body: new FormData(userForm), }).then(response => { // 送信後の処理があればここに書く }).catch(error => { // 例外処理 }); });= form_with(model: @user, id: 'user-form') do |f| = f.text_field :name = f.text_field :mail %button{ type: 'button', id: 'submit-btn' } 送信追記
コントローラ側でリクエストを受け取るアクションに、
protect_from_forgery
を適切に使わないとうまくいかないかも。参考リンク
他の参考リンクは忘れてしまったので思い出したら書く。
- 投稿日:2020-09-08T22:16:59+09:00
ブラウザに表示させるための事前処理(コンパイラ)
複数の静的ファイルがブラウザに適用されるまでの仕組みをまとめました。
大まかに言うと、
ブラウザでは認識できる言語が決まっており、どんな言語も認識できる言語に翻訳する必要があります。
この翻訳作業をコンパイルといい、コンパイルできないものは事前に処理をする必要があります。この事前処理はプリコンパイルと呼ばれ、細かい機能1つ1つをモジュールという処理のまとまりにし、コンパイルしてブラウザに返すといった流れです。前提 プログラミング初学者(2ヶ月)が学んだ内容をまとめたものです。 実際の現場では通用しないことや間違った情報がある可能性があります。 お気づきの方はコメントにてご指摘いただければ幸いです。ブラウザは認識できる言語が決まっている
ブラウザはHTML,CSS,JavaScript,WebAssemblyという言語のみを認識することができます。
サーバーサイドでどんな記述をしていたとしても、最終的にはこの4つでブラウザに返されています。
これ以外の言語ではブラウザはページを描画することができません。開発を便利にする言語
ブラウザでは、上記4つだけを認識してくれますが、開発ではより書きやすく読みやすいように設計されたプログラミング言語があります。
このプログラミング言語を高級言語といいます。高級言語
機械よりも人間が理解しやすいように設計されたプログラミング言語
機械が認識しやすい言語は低級言語と呼ばれます。
高級言語の例
CSS:SCSS、SASS
JavaScript:TypeScript、CoffeeScriptブラウザが認識できる言語に変換する仕組み
開発に便利な言語をブラウザが認識できるように翻訳する作業をコンパイルといいます。
コンパイル
プログラミング言語を動作する機械が理解できるように翻訳する作業
コンパイルはコンパイラというプログラムによって行われる。
コンパイラで認識できない言語がある場合は予めプリコンパイルしておく必要があります。プリコンパイル
コンパイラが翻訳できない言語を翻訳できるようにする事前コンパイルのこと
メインとなる処理に対して行う前処理のこと
このプリコンパイルを行うための手法として、アセットパイプラインという仕組みがある。アセットパイプライン
JavaScriptやCSSなどのアセットと呼ばれる静的ファイルを小さくまとめてくれる機能
アセットパイプラインの処理は、プリコンパイル→連結→圧縮→配置の流れで行われます。
複数の静的ファイルをプリコンパイルして連結したのち、圧縮して軽量化したものをpublicディレクトリに配置して、ブラウザへ渡せるようにします。
プリコンパイルはモジュールバンドラを使って行われます。モジュールバンドラ
モジュールバンドラはJavaScriptのモジュールの依存関係を考慮しながら管理するツール
モジュールは機能を1つずつ分けて他のファイルから読み込めるようにした処理のまとまりのこと
機能のまとまりをモジュール、この1つ1つの機能が依存関係にある場合にモジュールバンドラはこれらを考慮しながら管理してくれます。
モジュールで管理せず、機能をファイルごとに分割してしまうと、最終的に1つのファイルにまとめるときに不具合が生じるためモジュールパンドラが使われています。webpack
モジュールバンドラの中で主流なツール
webpackが行うことは大きく4つ
・Entry
依存関係を解決するためにどのファイルを基準(エントリーポイント)とするかを決める。
・Output
エントリーポイントにされ、webpackによってまとめられたファイルをどこへどのような名前で出力(アウトプット)するのか指定する。
・Loaders
JavaScript以外のCSSやHTMLなどのファイルをモジュールに変換する方法を読み込み(ロード)、指定した処理を行う。
・Plugins
圧縮などのファイルをまとめる以外でローダーが実行できないタスクを導入し、拡張(プラグイン)する。
- 投稿日:2020-09-08T21:53:04+09:00
Raspberry PiとOpenCVでWEB監視カメラを作成する
Raspberry PiとOpenCVでWEB監視カメラを作成する
はじめに
Mac環境の記事ですが、Windows環境も同じ手順になります。環境依存の部分は読み替えてお試しください。
目的
ブラウザにストリーミング動画を表示します。
この記事を最後まで読むと、次のことができるようになります。
No. 概要 キーワード 1 REST API Flask 2 OpenCV cv2 完成イメージ
ストリーミング 実行環境
環境 Ver. macOS Catalina 10.15.6 Raspberry Pi 4 Model B 4GB RAM - Raspberry Pi OS (Raspbian) 10 Python 3.7.3 Flask 1.1.2 opencv-python 4.4.0.42 ソースコード
実際に実装内容やソースコードを追いながら読むとより理解が深まるかと思います。是非ご活用ください。
関連する記事
- Raspberry PiのセットアップからPython環境のインストールまで
- Raspberry PiとPythonでリモコンカーを作成する
- Raspberry PiとPythonでLCD(16x2)ゲームを作成する
Camera設定
Raspberry Pi Software Configuration Tool起動
command.sh~$ sudo raspi-configCamera有効化
5 Interfacing Options
を選択P1 Camera
を選択再起動
command.sh~$ sudo rebootOpenCV依存関係
HDF5
HDF5 is a file format and library for storing scientific data.
command.sh~$ sudo apt-get install -y libhdf5-dev libhdf5-serial-dev libhdf5-103ATLAS
ATLAS is an approach for the automatic generation and optimization of numerical software.
command.sh~$ sudo apt-get install -y libatlas-base-devJasPer
JasPer is a collection of software (i.e., a library and application programs) for the coding and manipulation of images.
command.sh~$ sudo apt-get install -y libjasper-devハンズオン
ダウンロード
command.sh~$ git clone https://github.com/nsuhara/raspi-streaming.git -b masterセットアップ
command.sh~$ cd raspi-streaming ~$ python -m venv .venv ~$ source .venv/bin/activate ~$ pip install -r requirements.txt ~$ source configサービス起動
command.sh~$ flask run --host=0.0.0.0 --port=5000アクセス
command.sh~$ open "http://{host}:5000/raspi-streaming/api?process=front_end&request=app_form&secret_key=M7XvWE9fSFg3"サービス終了
command.sh~$ Control Key + C
アプリ構成
target.sh/app ├── __init__.py ├── apis │ ├── templates │ │ └── app_form.html │ └── views │ ├── __init__.py │ ├── back_end_handler.py │ ├── camera.py │ ├── front_end_handler.py │ └── main_handler.py ├── common │ ├── __init__.py │ └── utility.py ├── config │ ├── __init__.py │ ├── localhost.py │ └── production.py └── run.pyfront-end
target.sh/app └── apis ├── templates │ └── app_form.html └── views └── front_end_handler.pyfront-end制御
front_end_handler.py"""app/apis/views/front_end_handler.py """ from flask import jsonify, render_template from app import secret_key def handler(req): """handler """ param1 = req.get('param1') param2 = req.get('param2') if param1 == 'app_form': return _app_form(req=param2) return jsonify({'message': 'no route matched with those values'}), 200 def _app_form(req): """_app_form """ if req.get('secret_key', '') != secret_key: return jsonify({'message': 'no route matched with those values'}), 200 return render_template('app_form.html', secret_key=req.get('secret_key', ''))front-endレイアウト
app_form.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>raspi-streaming</title> <style type="text/css"> html, body { -webkit-user-select: none; width: 100%; height: 100%; } table { width: 100%; height: 100%; } table, td { border: 1px gray solid; padding: 10px; } img.img-option { width: 100%; /* height: 100%; */ } </style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script type="text/javascript"> // nothing to do </script> </head> <body> <table> <tr height="5%"> <td> <h1>raspi-streaming</h1> </td> </tr> <tr> <td> <img class="img-option" src="{{ url_for('raspi-streaming.api', process='back_end', request='video_feed', secret_key=secret_key) }}"> </td> </tr> </table> </body> </html>back-end
target.sh/app └── apis └── views ├── back_end_handler.py └── camera.pyback-end制御
back_end_handler.py"""app/apis/views/back_end_handler.py """ from flask import Response, jsonify from app import secret_key from app.apis.views.camera import Camera def handler(req): """handler """ param1 = req.get('param1') param2 = req.get('param2') if param1 == 'video_feed': return _video_feed(req=param2) return jsonify({'message': 'no route matched with those values'}), 200 def _video_feed(req): """_video_feed """ if req.get('secret_key', '') != secret_key: return jsonify({'message': 'no route matched with those values'}), 200 return Response(_generator(Camera()), mimetype='multipart/x-mixed-replace; boundary=frame') def _generator(camera): """_generator """ while True: frame = camera.frame() yield b'--frame\r\n' yield b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n'カメラ制御
camera.py"""app/apis/views/camera.py """ import cv2 class Camera(): """Camera """ def __init__(self): self.video_capture = cv2.VideoCapture(-1) def __del__(self): self.video_capture.release() def frame(self): """frame """ _, frame = self.video_capture.read() _, image = cv2.imencode('.jpeg', frame) return image.tobytes()
- 投稿日:2020-09-08T21:30:16+09:00
簡単レシート印刷 receiptline で行間隔を調整してみた
前回は 80 ミリカメラと 80 ミリフィルムで動画を撮影していました。
「役に立たない機械」感を醸し出していたかもしれないですね。連続で印刷するため、変換ライブラリを少し変更して、自動用紙カットを解除しました。
今回もこの変換ライブラリlib/receiptline.js
に手を入れてみようと思います。用紙節約か、読みやすさか
receiptline に添付されているサンプルデータを印刷してみました。
左は TM-T88V で、右は mC-Print3 です。行間隔が狭いので、文字が詰まって見えます。
今どきのレシートプリンターには用紙節約機能があるので、そういう時代だと思いますが。行間隔を空ける
行間隔を空けて読みやすくしてみようと思います。
レシートプリンターのコマンドを調べて、一部のコマンドを置き換えてみます。ESC/POS コマンドリファレンス
https://reference.epson-biz.com/pos/reference_ja/
- 変更前
- 行間隔なしコマンド ESC 3 n
- 変更後
- 行間隔ありコマンド ESC 2
StarPRNTモード コマンド仕様書
http://sp-support.star-m.jp/SDKDocumentation.aspx
- 変更前
- 行間隔なしコマンド ESC 0
- 変更後
- 行間隔ありコマンド ESC z n
lib/receiptline.js// // ESC/POS // const _escpos = { // start printing: ESC @ GS a n ESC M n FS ( A pL pH fn m ESC SP n FS S n1 n2 ESC 3 n ESC { n // open: printer => '\x1b@\x1da\x00\x1bM0\x1c(A' + $(2, 0, 48, 0) + '\x1b \x00\x1cS\x00\x00\x1b3\x00\x1b{' + $(printer.upsideDown), open: printer => '\x1b@\x1da\x00\x1bM0\x1c(A' + $(2, 0, 48, 0) + '\x1b \x00\x1cS\x00\x00\x1b2\x1b{' + $(printer.upsideDown), ... }; // // StarPRNT MBCS // const _starmbcs = { // start printing: ESC @ ESC RS a n ESC RS F n ESC SP n ESC s n1 n2 ESC 0 (SI) (DC2) // open: printer => '\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1b0' + (printer.upsideDown ? '\x0f' : '\x12'), open: printer => '\x1b@\x1b\x1ea0\x1b\x1eF\x00\x1b 0\x1bs00\x1bz1' + (printer.upsideDown ? '\x0f' : '\x12'), ... };Node を再起動して再印刷。
行間隔は広がりましたが、縦罫線が途切れてしまっています。縦罫線を接続する
変換ライブラリをさらに変更。
縦罫線を引く領域では、行間隔を空けないようにします。ESC/POS
- 縦罫線の始まり
- 行間隔なしコマンドを追加 ESC 3 n
- 縦罫線の終わり
- 行間隔ありコマンドを追加 ESC 2
StarPRNT
- 縦罫線の始まり
- 行間隔なしコマンドを追加 ESC 0
- 縦罫線の終わり
- 行間隔ありコマンドを追加 ESC z n
lib/receiptline.js// // ESC/POS // const _escpos = { ... // start rules: FS C n ESC t n ... LF // vrstart: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'), vrstart: (widths, left, right) => '\x1b3\x00\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'), // stop rules: FS C n ESC t n ... LF // vrstop: (widths, left, right) => '\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'), vrstop: (widths, left, right) => '\x1b2\x1cC0\x1bt\x01' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'), ... }; // // StarPRNT MBCS // const _starmbcs = { ... // start rules: ESC $ n ... LF // vrstart: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'), vrstart: (widths, left, right) => '\x1b0\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x91', left ? '\x9c' : '\x98').slice(0, -1) + (right ? '\x9d' : '\x99'), // stop rules: ESC $ n ... LF // vrstop: (widths, left, right) => '\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'), vrstop: (widths, left, right) => '\x1bz1\x1b$0' + widths.reduce((a, w) => a + '\x95'.repeat(w) + '\x90', left ? '\x9e' : '\x9a').slice(0, -1) + (right ? '\x9f' : '\x9b'), ... };もう一度 Node を再起動して再印刷。
縦罫線がくっつきました!動作条件
実は、これが動作するのは
printers.json
の upsideDown がfalse
の場合です。
この値がtrue
で、印刷の向きを上下反転している場合はうまく動きません。printers.json{ "tm_t88v": { "host": "192.168.1.2", "port": 9100, "cpl": 42, "encoding": "cp932", "gamma": 1.8, "upsideDown": false, "command": "escpos" }, "mc_print3": { "host": "192.168.1.3", "port": 9100, "cpl": 48, "encoding": "cp932", "gamma": 1.8, "upsideDown": false, "command": "starmbcs" } }奥が深いです。
また何か作ったら投稿します。ではまた!
- 投稿日:2020-09-08T20:37:01+09:00
JSのProxyでアルゴリズムを可視化する
JSのProxyを使ってアルゴリズムの実行中の各ステップを可視化する仕組みを作る記事です。
(inspired by アルゴリズム図鑑)
初めに完成品のキャプチャとデモのリンクを貼っておきます。
DEMO: https://codesandbox.io/s/heuristic-morse-1kc2d?file=/src/index.js
※ スマホだとシンタックスハイライトでエラーが出てるのでコード記載無し版も置いておきます。
https://codesandbox.io/s/happy-sun-211bq?file=/src/index.jsJavaScriptのProxyとは
Proxy - JavaScript | MDN
最初にJSのProxyとは何か簡単に説明すると、
特定の操作に対するオブジェクトの振る舞いを拡張/変更できるオブジェクトです。
拡張されるオブジェクト(ターゲット)と、操作を受けた時の挙動を定義するオブジェクト(ハンドラ)をコンストラクタに渡すことで生成できます。const proxy = new Proxy(target, handler);振る舞いを変更できる操作の種類は
- プロパティ値の読み出し/書き込み/削除
- 関数オブジェクトに対する関数呼び出しやnew付きの呼び出し
- その他(プロパティやプロトタイプに関する様々な操作)
などがあります。
ハンドラに各操作に対応するトラップという関数を持たせることで挙動を定義できます。定義できるトラップの一覧はこちら。
Proxy handler - JavaScript | MDN以下はプロパティ値の書き込み操作に対応する
set
トラップを定義する例。
代入された値を10倍にしてターゲットに設定しています。const t = {}; const p = new Proxy(t, { set(target, property, value, receiver) { target[property] = value * 10; return true; // 値の設定に成功した場合はtrueを返す } }); p.n = 5; console.log(t.n); // 50
set
トラップの引数はターゲット、設定するプロパティの名前、設定するプロパティの新しい値、操作を受けたオブジェクト(プロキシ自身、またはプロキシを継承しているオブジェクト)です。
receiver
についてはReflectと併用するのが主な用途なのかなと思います。
参考: JavaScript(ES2015〜)のProxyで、プロパティにフックする正しいやり方プログラムの流れ
それではコードを見ていきます。
視覚化したいデータを初期化関数に渡す
今回は以下の内容の挿入ソートを視覚化します。
(ちなみにアルゴリズムはプログラミングコンテスト攻略のためのアルゴリズムとデータ構造を参考に実装しています)const a = [5, 2, 4, 6, 1, 3]; insertionSort(a); function insertionSort(a) { const n = a.length; for (let i = 1; i < n; i++) { const v = a[i]; let j = i-1; while (j >= 0 && a[j] > v) { a[j+1] = a[j]; j--; } a[j+1] = v; } }視覚化する値としては操作対象の配列
a
、ループ変数のi, j
、一時変数のv
にします。
それらを初期化関数に渡すことでプロキシに置き換えて値の変更を捕捉可能にします。
ただし、プロキシはオブジェクトに対する操作しか捕捉できないため、ローカル変数i, j, v
に関してはローカル変数保持用のオブジェクト$
のプロパティとして実装します。index.js// 初期化関数に{変数名: 値}の形で渡すことでプロキシが返される const { a, $ } = initVisualization({ a: [5, 2, 4, 6, 1, 3], $: { i: null, j: null, v: null } // {}でもいいけど分かりやすいようにnullで初期化 }); ... // アルゴリズム実装側もローカル変数の代わりに$を使うよう書き換え function insertionSort(a) { const n = a.length; for ($.i = 1; $.i < n; $.i++) { $.v = a[$.i]; $.j = $.i - 1; while ($.j >= 0 && a[$.j] > $.v) { a[$.j + 1] = a[$.j]; $.j--; } a[$.j + 1] = $.v; } }できればアルゴリズム実装側には手を入れずに実現したかったけど、止むを得ません…。
初期化関数で渡されたデータのプロキシを生成
受け取った観測対象のデータ(
a
と$
)それぞれに対して、プロパティ値が設定された時に全てのデータのスナップショットを取るようsetトラップを定義します。visualize.jsimport { takeSnapshot } from "./snapshot"; export function initVisualization(targets) { const proxies = {}; Object.entries(targets).forEach(([key, target]) => { proxies[key] = new Proxy(target, { set(t, p, v) { t[p] = v; takeSnapshot(targets); return true; } }); }); takeSnapshot(targets); return proxies; }スナップショットの実装
snapshot.jsconst snapshots = []; export function takeSnapshot(variables) { const snapshot = {}; for (const k in variables) { snapshot[k] = deepCopy(variables[k]); } snapshots.push(snapshot); }返されたプロキシを使ってアルゴリズムの処理を実行し、UI描画
あとは返されたプロキシを使ってアルゴリズムを実行するだけです。代入やインクリメントのタイミングでスナップショットが取られます。
完了してからViewを描画します。index.jsinsertionSort(a); render();この時点でのデモはこちらです。
代入操作にアニメーションを付ける
ここまでで値の可視化はできました。
ここからさらに、変数間での値の受け渡しが分かりやすいように代入元から代入先に要素が移動するアニメーションを付けてみます。
そのためには渡された値はどの変数に入っていたかという情報が要りますが、setトラップには値そのものしか渡ってきません。
そこで、ちょっとhackyですが初期化関数を以下のように書き換えてみました。visualize.jsexport function initVisualization(targets) { const proxies = {}; const name = Symbol("variable name"); // 変数名を保持するためのシンボル const from = Symbol("assign from"); // 代入元を保持するためのシンボル Object.entries(targets).forEach(([key, target]) => { target[name] = key; // 変数名を持たせる proxies[key] = new Proxy(target, { get(t, p) { if (t[name] === "a" || p === "v") return { valueOf: () => t[p], [from]: t[name] === "$" ? p : `${t[name]}.${p}` // 代入元情報 }; else return t[p]; }, set(t, p, v) { if (v[from]) { // fromを持っていれば代入されたと判定 t[p] = v.valueOf(); takeSnapshot(targets, { assign: { from: v[from], to: t[name] === "$" ? p : `${t[name]}.${p}` // 代入先情報 } }); } else { t[p] = v; takeSnapshot(targets); } return true; } }); }); ... }まずプロキシ生成時にターゲットに自身の名前を持たせておきます。
getトラップを使用してアニメーション対象である配列a
の要素か変数v
が参照された場合には値をオブジェクトでラップして、どの変数の値を読み出したかを示すfrom
プロパティを添えて返します。この時、元の値をvalueOfの戻り値とすることで比較演算など数値としての振る舞いが期待される文脈では元の値が自動的に返されます。
setトラップでは渡された値がfrom
プロパティを持っていれば別変数からの代入と判断して、自身の名前を代入先to
としてfrom
と合わせてtakeSnapshot
の第2引数に渡します。これで値と共に操作もスナップショットに記録されます。
snapshot.jsexport function takeSnapshot(variables, operation) { const snapshot = {}; for (const k in variables) { snapshot[k] = deepCopy(variables[k]); } snapshot[Symbol.for("operation")] = operation; // シンボルキーで操作内容を保存 snapshots.push(snapshot); }後はその情報を元にアニメーションを組み立てます。
animation.jsexport function getAnimation(operation) { if (!operation || !operation.assign) return null; const { from, to } = operation.assign; const fromSelector = `[data-variable-name="${from}"]`; const toSelector = `[data-variable-name="${to}"]`; const fromRect = document.querySelector(fromSelector).getBoundingClientRect(); const toRect = document.querySelector(toSelector).getBoundingClientRect(); const startX = fromRect.left - toRect.left; const startY = fromRect.bottom - toRect.bottom; return ` ${toSelector} { animation: assign .3s; } @keyframes assign { 0% { transform: translate(${startX}px, ${startY}px); } } `; }今回はやりませんが、比較演算など代入以外の操作も_.gtのように関数でラップしたスタイルで実装し、applyトラップと組み合わせることで操作の記録ができると思います。
View側(React)は省略しますが、ソース全体はCodeSandboxかGithubからご覧いただけます。
おわり
プロキシによるロギングと、それを利用したアルゴリズムの可視化の試みでした。
プロキシにはログを取る以外にもバリデーションや値の加工、変更の通知など様々な活用方法があるので以下の記事も参考になると思います。
- 投稿日:2020-09-08T20:12:57+09:00
Object等の呼び出しでkeyに動的な変数を使う [TypeScript]
やりたいこと
object等の呼び出しで
Object[key]
のようにするとき、key
に動的な変数を入れたい。
上記をより美しく。問題・エラー
今回の記事では、こちらのコードを修正します。
関数receivedStringValue()
は、ランダムでStringの値を返す関数とします。const object = { aaa: 'aaa', bbb: 'bbb', ccc: 'ccc', }; // keyには動的に生成された値 const key: string = receivedStringValue(); const value = object[key];こちらのコードでは、下記のエラーが出ます。
Element implicitly has an 'any' type because expression of type 'string' can't be used to解決策
object
に対して型を定義する結論
key
の型がわかるようなobject
の型を定義するinterface StringKeyObject { // 今回はstring [key: string]: any; } const object: StringKeyObject = { aaa: 'aaa', bbb: 'bbb', ccc: 'ccc', }; // keyには動的に生成された値 const key: keyof typeof object = receivedStringValue(); const value = object[key];keyの型がstringだとあいまいなので、
keyof typeof object
でしっかりと制限をかけることがポイントです。最後まで読んでくださって、ありがとうございます。
- 投稿日:2020-09-08T20:12:57+09:00
keyof typeofの使い方 [TypeScript]
やりたいこと
object等の呼び出しで
Object[key]
のようにするとき、key
に動的な変数を入れたい。
上記をより美しく。問題・エラー
今回の記事では、こちらのコードを修正します。
関数receivedStringValue()
は、ランダムでStringの値を返す関数とします。const object = { aaa: 'aaa', bbb: 'bbb', ccc: 'ccc', }; // keyには動的に生成された値 const key: string = receivedStringValue(); const value = object[key];こちらのコードでは、下記のエラーが出ます。
Element implicitly has an 'any' type because expression of type 'string' can't be used to解決策
object
に対して型を定義する結論
key
の型がわかるようなobject
の型を定義するinterface StringKeyObject { // 今回はstring [key: string]: any; } const object: StringKeyObject = { aaa: 'aaa', bbb: 'bbb', ccc: 'ccc', }; // keyには動的に生成された値 const key: keyof typeof object = receivedStringValue(); const value = object[key];keyの型がstringだとあいまいなので、
keyof typeof object
でしっかりと制限をかけることがポイントです。最後まで読んでくださって、ありがとうございます。
参考記事
- 投稿日:2020-09-08T18:30:22+09:00
Java Script 基礎文法編 1
- 投稿日:2020-09-08T18:05:16+09:00
javascriptで配列の要素がオブジェクトの場合に、全て値渡しでコピーしたいとき
vue.js等を使っていると、dataに定義している配列を、参照渡しじゃなくて、値のみコピーしたい場合ってよくありますよね。
例. 下記のような配列の要素がオブジェクトの場合。
const array = [{a: 1}, {a: 2}]下記のようにmapで各要素ごとをスプレッド構文で展開して、pushしていけばおk。
let newArray = [] array.map(a => { newArray.push({...a}) })また、そのタイミングで、新しいプロパティを追加したい場合は、下記のようにすればおk
let newArray = [] array.map(a => { newArray.push({...a, b: 0}) })もっといいコピーの仕方を知っている方がいれば教えて下さい!
- 投稿日:2020-09-08T17:47:08+09:00
【Vue.js】ボタンの切り替えテクニック
モダンなサイトだと、ボタンをクリックしたらアイコンが切り替わって、内容が見れたりするようなUIも増えてきていますよね。
そんなボタンの切り替えの構造をVue.js(Nuxt.js)で表現していこうと思います!
ボタンを押すと、記事内容の表示・非常時を切り替えられる
ボタンをそして、あるデータの真偽値を変化させる
<v-btn @click="show = !show" icon> </v-btn><script> expprt default { data () { return { show: false } } } </script>ボタンを押すと、falseの真偽値が切り替わる
showの真偽値によって変化する者たち
<v-btn @click="show = !show" icon> <v-icon>{{ show ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon> </v-btn>showの真偽値、そして三項演算子によって、アイコンの見た目が変わるようになっている。
<v-expand-transition> <div v-show="show"> <v-divider></v-divider> <v-card-text> <ul> <li v-for="item in items" :key="item.id"> <a href="item.url">{{ item.title }}</a> </li> </ul> </v-card-text> </div> </v-expand-transition>
v-show
の真偽値がtrueの場合は、div以下のコンポーネントが表示されて、falseの時は隠れるようになっている。パスワードの入力内容を見たり、隠したりする
<v-text-field label="パスワード" v-model="signupForm.password" @click:append="show = !show" ></v-text-field>
append
はボタンの配置を設定している<script> expprt default { data () { return { show: false } } } </script>例の如く、showの真偽値をボタンによって切り替えている。
showの真偽値によって、テキスト内容を見たり隠したりする
<v-text-field label="パスワード" v-model="signupForm.password" :type="show ? 'text' : 'password'" :append-icon="show ? 'mdi-eye' : 'mdi-eye-off'" @click:append="show = !show" ></v-text-field>
type
属性をshowによって紐づけ(bind)て、入力フォームのタイプを切り替えている
icon
もshowによって紐づけ(bind)て、アイコンの見た目を切り替えている
- 投稿日:2020-09-08T17:27:13+09:00
【Nuxt.js】Nuxt文法編:component①
? この記事はWP専用です
https://wp.me/pc9NHC-DX前置き
componentとは
使いまわしができる、
再利用ができるものです?シンプルなボタンでも
CSSが長くなったりして
まいかい書くのは面倒ですよね…?
それを解決してくれるのが
componentです?作り方、表示のさせ方を解説していきます✍️
目次
前置き
componentの種類と使用箇所
基本コード
Step1: 使い回すコンポーネントを作る
Step2: コンポーネントをインポートする
自動でインポートする
まとめcomponentの種類と使用箇所
種類
まずは種類を把握しましょう。
おおまかに全部で3つあります?
今回は1の自作のコンポーネント
について解説していきます?
前置きに書いたボタンのような場合は
自作のコンポーネントに該当します?♀️
- <好きに命名した名前 /> =自作のコンポーネント importが必要!
- layouts内でのみ使用
- ネストされたルート内で使用
nuxtとnuxt-childについては
別記事でご紹介します?使用箇所
componentsが使える場所
(インポートできる場所)は
ページを構成する部分です!・layout
・他のコンポーネント
・pageコンポーネント
自作のコンポーネントの場合は
この中のどれでも使用可能です?基本コード
基本コードがこちら。
あとは使いまわしたい物に
作り替えていきましょう?✨? 続きはWPでご覧ください?
https://wp.me/pc9NHC-DX
- 投稿日:2020-09-08T16:40:13+09:00
History APIでルーティングしてる時の存在しないページへのフォールバックってどうすんの?
JavaScriptでSPAのルーティングを実装するためHistory APIを使っていたところ、操作した後のパスでリロードをした時にこんな画面になりました。
404です(WARNINGは拡張機能起因のものなのでスルーします)。
ちなみにサーバーはVSCodeのLive Server Extensionを利用しています。なぜ?と思う前に冷静にこのアプリのディレクトリ構成を確認します。
|-- index.html |-- style.css |-- js |-- main.js |-- router.jsよく見れば当たり前の話です。
newPost.html
なんて存在せず、index.html
しかないので、ルートとなるhttp://127.0.0.1:5500/
以外はそもそも存在しません。そりゃ404にもなります。あれ、でもVueとかNuxt使ってる時に直接このパス見に行ってもページ表示できたよな?あれどうなってんだと思い調べてみることにしました。
Vue Routerのドキュメントをちゃんと読む
Vueのルーティングを司っているVue Routerのドキュメントを確認すると答えはありました。
どうやらhashとhistoryという2つのモードがあるみたいです。
hashモード
vue-router のデフォルトは hash モード です - 完全な URL を hash を使ってシミュレートし、 URL が変更された時にページのリロードが起きません。
hashモードってアレだ!URLが
https://example.com/#/user
みたいに#
が間に含まれてるやつだ。思い出した。昔Angular.jsで開発してたときもHistory APIがどうのこうのってこのhashモードを使ってました。hashモードが動く理由は、URLのアンカー
#
の動きを利用してhttps://example.com/#/user
の例で言えば実際はhttps://example.com
を見に行くという理解を得ました。となるとその中で更にアンカーでのスクロールを実装したいときどうなるんだろう?https://example.com/#/user#profile
みたいなケース。……これは別の機会に調査します。historyモード
history モードを使用する時は、URL は "普通" に見えます e.g. http://oursite.com/user/id。美しいですね!
しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。
まんまこの問題ですね。
心配する必要はありません。この問題を直すためには、単純な catch-all フォールバックのためのルートをサーバー側で追加するだけです。もし URL がどの静的なアセットにもマッチしなかった時はあなたのアプリケーションが動作しているのと同じ index.html ページで受け付けましょう。これも美しいですね!
なるほど!フロント側ではやはりどう足掻いても駄目みたいですね。Vue CLIやらNuxtでの開発は便利ですが、こういうのも隠蔽して裏側でやってくれていたということっぽい。たまにはフレームワーク抜きでJavaScript触るのも学びがあって良いですね。
History API利用時のフォールバックを設定する
上記のページに以下のケースでのフォールバックは設定例があるのでこのまま使えそうです。
- Apache
- nginx
- Node.js
- Express
- IIS
- Cady
- Firebase
これ以外で今回私が個人的に使ってるLive Server ExtensionとNetlifyでの実装も調べたのでここに残しておきます。
Liver Server Extension
VS Codeの設定ファイルに以下を追記します。
{ "liveServer.settings.file": "index.html" }念の為、個人設定ではなくワークスペース単位での設定をオススメします。
Netlify
_redirects
ファイルに以下を追記します。/* /index.html 200こによりどのパスでアクセスしてもindex.htmlが参照されます。
サーバーにフォールバックを設定した際の注意点
サーバーに上記のフォールバックを設定した時点で、あらゆるアクセスは
index.html
にリダイレクトされます。つまり、以下のようなディレクトリ構成で:|-- index.html |-- user.html |-- item.html
https://example.com/user
にアクセスしようが、https://example.com/item
にアクセスしようが,
実態はhttps://example.com/
にアクセスがリダイレクトされます(ただしURLは/user/
や/item
がついた状態)。なので、以下のようなSPAを前提とした実装が必要になります。
- 完全なるルーティングの制御
- ページロード時(初期表示時)、現在のパスを取得し対応するコンポーネントを表示する
- 対応するコンポーネントが存在しないパスを表示時、404を表すページを表示する
完全なるルーティングの制御の実装はなかなか骨が折れます。Historyの状態をpush/popする度にコンポーネントの描画を切り替える実装が必要になってきます。VueやReactなどのSPA作れるフレームワークを使う時はセットとなるルーターのライブラリを使いましょう。勉強以外の目的で素のJavaScriptでSPAつくることなんかせず、おとなしくフレームワーク使うことをオススメします。
- 投稿日:2020-09-08T16:24:49+09:00
失敗なんか気にせずとにかくPromise.all()でぜんぶやる
こんなことがあった
- あるAPIにアクセスすると本日執筆された記事IDの一覧が帰ってくる
- その記事IDを使って記事の内容を取得できるAPIにアクセスして、記事を取得したい
とりあえず実装する
前提: 以下で登場するインスタンス、メソッドには架空のものがあります。このようなことがしたいのだろう。程度に解釈してください。
何も考えず実装するとこのようになった。
const fetchEntryIds = (): Promise<Array<string>> => { return request.get('xxxxx/entries/new'); }; const fetchEntry = (entryId: string): Promise<Entry> => { return request.get(`xxxxx/entries/${entryId}`); }; const fetchTodaysEntries = async (): Promise<Array<Entry>> => { const entryIds: Array<string> = await fetchEntryIds(); return Promise.all<Entry>(entryIds.map<Promise<Entry>>((entryId: string) => { return fetchEntry(entryId); })); };期待したこと
Promise.all()
はArray<Promise<?>>
をPromise<Array<?>>
にしてくれる便利なメソッドであり、内部のPromise<?>
の動作を並列に行ってくれる。
-> 記事をまとめて取得できるのでは!??今回起きたこと
- 当たり前だがAPIリクエストは失敗することがある
Promise.all()
は内包するすべてのPromiseがfulfilledにならないとfulfilledにならないそのためリクエストがひとつでもrejectedになってしまうと
Promise.all()
はrejectedになり、その値が返却される。悲しいことにそれまでいくつかfulfilledになっているPromiseがあったとしてもなかったことになる。図解
以下の記号を使います。
- pending = ?
- fulfilled = ?
- rejected = ?
以下は左辺にこれらの絵文字を配列状に筆記したもので、右辺はそれを
Promise.all()
で実行した場合の結果です。[?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ? [?, ?, ?, ?, ?] => ?今回やりたいこと
失敗でいいから
Promise.all()
の動作を遮らずにすべて実行してほしい。"実装": "0.1.0"
今回の実装は外部パッケージを一切使わずにできました。
あとで見返せば当たり前じゃないかって思いますが、思いついたときは若干感動しました。const fetchTodaysEntries = async (): Promise<Array<void | Entry>> => { const entryIds: Array<string> = await fetchEntryIds(); return Promise.all<void | Entry>(entryIds.map<Promise<void | Entry>>((entryId: string) => { return fetchEntry(entryId).catch(() => {}); })); };こつ
一度rejectedになったPromiseをfulfilledに戻します。そのために
promise.catch()
で例外を捕捉し、その捕捉にしかるべき処理を施します。今回は握り潰しましたごめん。問題点
promise.catch()
の戻り値が記事のクラスEntry
ではないためPromise.all()
の戻り値のジェネリクスが<Entry>
から<void | Entry>
に変わりました。fetchTodaysEntries()
を使う人はその戻り値についてundefined
かどうかの判定が必要です。他にNull Object patternや番兵を使って動作を制御させる方法もありますが、以下はそれよりもより良いだろう方法の紹介です。
"実装": "1.0.0"
Discriminated unions
を使います。
Discriminated unions
複数の型が同名のプロパティを持ち、そのプロパティの型が違うことがわかっていれば、それらから構成される
Union type
はそのプロパティの型が何かを判定できればTypeScriptはUnion type
がどの型であるかを確定できます。よくある例
人気No.1モナドの
Optional<T>
を部分的に実装する例が最も有名です。type Some<T> = { present: true; value: T; }; type None = { present: false; }; type Optional<T> = Some<T> | None;この
Optional<T>
において複数の型が同名のプロパティを持ち、そのプロパティ型が違うことがわかっていればとはpresent
です。present
はSome<T>, None
では異なるLiteral type
を持っているので、この値の判定で変数がSome<T>
かNone
のどちらかであることを判定できます。if (optional.present) { // TypeScriptはoptional = Some<T>と解釈している optional.value // ? } else { // TypeScriptはoptional = Noneと解釈しているため以下はできない optional.value // ? }今回用意した
Discriminated unions
今回は
Try<T>
のモナドを模しました。type Success<T> = { success: true; value: T; }; type Failure = { success: false; err: unknown; }; type Try<T> = Success<T> | Failure;あとはこれに沿うように成功時は
Success<T>
、例外発生時はFailure
のオブジェクトリテラルで包みます。これが
1.0.0
です。const fetchTodaysEntries = async (): Promise<Array<Try<Entry>>> => { const entryIds: Array<string> = await fetchEntryIds(); return Promise.all<Try<Entry>>(entryIds.map<Promise<Try<Entry>>>((entryId: string) => { return fetchEntry(entryId).then((entry: Entry) => { return { success: true, value: entry }; }, (err: unknown) => { return { success: false, err }; }); })); };
promise.then()
使用例が珍しいですが
promise.then()
は第2引数を受けることができます。この第2引数のコールバックは例外発生時のことで、じつはpromise.catch()
の第1引数と同じです。そのため正常時と例外発生時をpromise.then()
ひとつで処理できます。もちろん
promise.catch()
を使う方法も可能ですが、オブジェクト生成コストが内部的には少し増えます。気持ちの問題程度の差だとは思うので読みやすさを考慮して適宜選択してください。const fetchTodaysEntries = async (): Promise<Array<Try<Entry>>> => { const entryIds: Array<string> = await fetchEntryIds(); return Promise.all<Try<Entry>>(entryIds.map<Promise<Try<Entry>>>((entryId: string) => { return fetchEntry(entryId).then((entry: Entry) => { return { success: true, value: entry }; }).catch((err: unknown) => { return { success: false, err }; }); })); };?async/awaitでやりたい!
できます。手を加えなければいけないところが少々変わります。
fetchEntry()
const fetchEntry = async (entryId: string): Promise<Try<Entry>> => { try { const entry: Entry = await request.get(`xxxxx/entries/${entryId}`); return { success: true, value: entry }; } catch (err: unknown) { return { success: false, err }; } };
array.filter()
できれいにできる
Type predicate
を戻り値に持つarray.filter()
がTypeScriptのArrayにはoverloadで定義されているのでSuccess<T>
だけ、Failure
だけが必要ならそれを使います。const extractSuccess = (arr: Array<Try<Entry>>): Array<Success<Entry>> => { return arr.filter((t: Try<Entry>): t is Success<Entry> => { return t.success; }); }; const extractFailure = (arr: Array<Try<Entry>>): Array<Failure> => { return arr.filter((t: Try<Entry>): t is Failure => { return !t.success; }); };
- 投稿日:2020-09-08T16:12:01+09:00
JavaScript if文の書き方
初投稿兼投稿の練習になります。プログラミングもまだまだ初心者ですので、内容も恐らく初歩的な内容になります。暖かく見守って頂けると幸いです。
if文の書き方
if(条件式) { 条件式がtrueの時に実行する処理 } else { 条件式がfalseの時に実行する時の処理 }上記の書き方をすることで、条件式が成立するか否かで処理を分けることができます。
例えば
if(window.confirm('確認できましたか?')) { console.log('確認済です'); } else { console.log('まだ確認できていません'); }window.confirmは確認ダイアログボックスを表示します。確認ダイアログボックスにはOKボタンとキャンセルボタンがあり、OKをクリックするとtrue、キャンセルをクリックするとfalseの値が返ってきます。
なので、OKをクリックすると条件式がtrueとなり、console.log('確認済です')
が実行され、キャンセルをクリックすると条件式がfalseとなり、console.log('まだ確認できていません')
が実行されます。ちなみに
if(true) { console.log('確認済です'); } else { console.log('まだ確認できていません'); }とすると、初めから条件式がtrueとなっているので、処理は分岐せず、true時の処理を実行することになります。条件式をfalseとすると同様に、false時の処理が実行されます。
以上です。ありがとうございました。
- 投稿日:2020-09-08T16:01:09+09:00
自分用メモ Fetch API javascript
はじめに
javascript含め、プログラミングを学び始めた初心者なのですが、javascriptのfetchのメソッドについて勉強したのでここでアウトプットしておこうと思います。
自分は周りにプログラミングをしている人がいないので情報収集やアウトプットをする機会がないのですが、この記事がそのような問題を解決してくれるようにQiitaを通して、アウトプットしておりますので、何か間違った点やアドバイスなどをしていただければ、大変助かれます。
FETCH APIとは
fetchとは、とってくるや呼び出す、引き出すなどの意味を持つ英単語
ITの分野だと情報などのデータを取ってくる動作のことを指すことが多い。FETCH APIとは、ページの外部からリソースを取得するためのインターフェースを定義した、Webブラウザの標準API。リソースを取得するためのAPIを定義している。
javascriptなどのいくつかあるHTTPメソッドの中の一つ。
そしてfetchを利用するとリクエストやレスポンスといったHTTPのパイプラインを構成する要素を操作できるようになる。また非同期のネットワーク通信を簡単にわかりやすく記述できるようになる。
往来ではXMLHttpRequestを使用して実現されていたが、fetchはそれの上位互換的存在で。他の技術から簡単に利用することができる。
そのためXMLHttpRequestに変わる、モダンで他の技術と連携しやすい非同期通信といえる。特徴
1.fetchが返されるPromiseはレスポンスがHTTP404や500だったとしてもrejectされない。そして代わりにResponse.okがboolean型で返される。
fetch自体エラー受け取ってエラーを表示するレスポンスを受け取って表示する役割がある。2.fetchはクッキーを受信できる
cookieとは
webサイトの提供者が、webブラウザを通じて訪問者のコンピューターに一時的にデータを書き込んで保存させる仕組み3.fetchはサーバ間でクッキーの送受信は行われない。サイトがユーザーのセッションに頼っている場合未認証のリクエストとなる。
headerの追加のカスタムで解決できる。使い方
一般的な利用方法
fetch('https://example.com/example.json') .then(response => response.json() .then(data => console.log(data)fetchした後の引数はresponseになる。
responseをJSON型で抽出するにはjsonメソッドを使う。引数にinitオブジェクト
initオブジェクトhttpのヘッダやメソッド名bodyの設定などの多数の設定が行えるオブジェクト。
async postData (url = "", data = {}) => { const response = await fetch(url, { method: 'POST', mode: 'cors', cache: 'no-cache', credentials: 'same-origin', headers: { content-type: 'application/x-www0form urlenceded' }, redirect: 'follow', referrerPolicy: 'no-referrer', body: 'Json.stringify(data)' }) return response.json() } postData(`https://example.com/answer`, {anser: 42}) .then(data => { console.log(data) });postDataでは最後にresponseをjsonにしてreturnしているので、呼び出し元ではjsonメソッドを使うことなくそのまま引数にとってコンソールで表示している。
認証情報付きリクエスト
initオブジェクトで
credentials: 'include'
リクエストURLと呼び出し元が同一のオリジンの場合は、
credentials:'same-origin'この代わりにブラウザがcredentialを含んでいないことを保証させたい時は、そして常にクッキーの送信をしたい時は、
credentials: 'omit'オリジン
ドメイン名、プロコル、ポート番号CORS
オリジン間リソース共有...あるオリジンで利用されているアプリケーションのアクセス権を異なるオリジンである選択されたリソースへのアクセス権を与えるようにブラウザに指示する仕組み。アップロード
メソッドはPOST
bodyはJson.stringify()でエンコードする。ファイルアップロード
のinput要素、FormData(),fetch()を使ってアップロードする
const formData = new FormData(); const fileField = document.querySelector('input[type="file"]'); formData.append('title', 'My Vegas Vacation'); formData.append('avatar', fileField.files[0]); fetch('https://example.com/users/search',{ method: 'PUT', body: formData }) .then(response =>//以下省略)複数のファイルのアップロードをする時は, input要素にmultipleを入れる。
fetchの成功判断
上記で述べたようにfetchには通信やネットワークなどの外部の妨げによるエラー以外では、エラーが返ってこないようになっている。
fetch()が成功したかどうかは、返ってきたresponseのなかのresponse.okプロパティを見て判断する。trueになっていた場合は、成功となる。fetch(url) .then(response => { if(!response.ok){ throw new Error('Network response was not ok'); } return response.json(); });上記のようにして、エラーが返ってこないためresponse.okプロパティをチェックして、falseの時はエラーを吐くようにしておく!
独自のrequestオブジェクトをfetchに渡す
fetchには、独自で作ったrequestのオブジェクトを引数として渡せられるようになっている。
const myHeaders = new Headers(); const myRequest = new Request('url', { method: 'GET', headers: myHeaders, mode: 'cors', cache: 'default', }); fetch(myRequest) .then(response => response.json()) .then(data => { mySrc.src = URL.createObjectURL(data) });fetch()メソッドの引数と全く同じ引数をRequest()に適用させることができる。
const anotherRequest = new Request(myRequest, myInit);これはリクエストとレスポンスの本文を一つだけ使用するのでとても有用。
必要に応じてinitオプションを変化させながらリクエスト/レスポンスを再利用できるようにコピーする。
一つ目の引数にはrequestをそして二つ目のinitオブジェクトにはresponseを書くことができる。Headers
Headersインターフェースでは、Headers()コンストラクターを使って、headerオブジェクトを作成することができる。
入ってくるのは、配列の配列かオブジェクトリテラルになる。//配列の配列 const myHeaders = new Headers(); myHeaders.append('Content-Type': 'text/plain'); myHeaders.append('Content-length': content.length.toString()); myHeaders.append('x-Custom-Header', 'ProcessThisImmediately'); //オブジェクトリテラル const myHeaders = new Headers({ 'Content-Type': 'text/plain', 'Content-Length': content.length.toString(); 'X-Custom-Header': 'ProcessThisImmediately' });これで追加することができる。そしてheaderを参照できるgetや削除するdeleteやあるかないかをbooleanで返すhasそしてsetなどもできる。
headerの使用例
fetch(myRequest){ .then(response => { const contentType = response.headers.get('content-type'); if(!contentType || !contentType.includes('application/json')){ throw new TypeError("Error"); } return response.json(); });上記のように処理を行う前にコンテントタイプが正しいか判断するときなどに使う!
Responseオブジェクト
fetch()のプロミスが解決(resolve)されたときに返り値としてResponseインスタンスが渡される。
以下がresponseで必ず生成されるプロパティResponse.status
HTTPステータスコード(デフォルト200)
Response.statusText
HTTPステータスコードのメッセージと一致する文字列(デフォルトはOK)
Response.ok
HTTPのステータスコードが200から299だったときにtrueを返すboolean型のプロパティ(fetchが成功したかの判断材料になる)そしてResponseオブジェクトはjavascriptで動的に作ることもできる。respondWith()メソッドを使ってカスタマイズされたレスポンスを返すようなときに役立つ。
const myBody = new Blob(); addEventListener('fetch', fucntion(event){ event.respondWidth( new Response(myBody, { headers: {'Content-Type': 'text/plain'} }) ); });Body
リクエストもレスポンスもボディを持っている。
ArrayBuffer
ArrayBufferView
Blob/File
文字列
URLSearchParams
FormData各コンテンツを抜き出すメソッド。プロミスを返す。
arrayBuffer()
blob()
json()
text()
formData()まとめ
まだまだわからないところも多く、知識も浅いためAPIの知識を深めるためにもfetchだけでなく、apiについては、深い知識がつけられるよう勉強を続けていきたいと思っております。多々至らない点多くあったと思いますが、暖かく見ていただければと思います。
何か自分自身気づいたことがあれば更新していきたいと思います。参照
- 投稿日:2020-09-08T15:45:04+09:00
日付をYYYY-MM-DDの書式で返す関数を理解しよう
書籍で解説もしてくれないので、自分でまとめてみた
どんな関数を使うのか
関数
formatDate.js#日付をYYYY-MM-DDの書式で返すメソッド date = new Date(); function formatDate(date) { var y = date.getFullYear(); var m = ('00' + (date.getMonth()+1)).slice(-2); var d = ('00' + date.getDate()).slice(-2); return (y + '-' + m + '-' + d); } formatDate(new Date()); => '2020-09-08'解説
new Date()
指定した日時を表す日付オブジェクトを生成する。指定をしなかったら今日の日付で生成されちゃう。
date.getFullYear();
年を4桁の整数で取得する
('0' + (date.getMonth()+1)).slice(-2);
getMonth()
で0-11を生成。0→1月、11→12月となるので、生成された数字に+1で正確な月にできる。また今回は「9」ではなく「09」という形で表示させたいので、先頭で「0」を付ける。
.slice(-2)
は後ろから2文字を取得でき、9月の「009」が生成されば「09」となってくれる。
('0' + date.getDate()).slice(-2);
date.getDate
はdate.getMonth
と違って、数字は「1」から始まる。
return (y + '-' + m + '-' + d);
最後にそれぞれを連結させて返すことで、「2020-09-08」が表示される。
- 投稿日:2020-09-08T13:10:49+09:00
JavaScript~配列の操作~その2
はじめに
JavaScriptでの配列の操作
filter
,map
,reduce
,join
について
この4つは、全て非破壊的
(元の配列は変更しない)メソッド使用する配列
const users = [ { name: "A", age: 23, country: "ja" }, { name: "J", age: 27, country: "usa" }, { name: "R", age: 64, country: "usa" }, { name: "Y", age: 48, country: "ja" }, { name: "B", age: 33, country: "ja" }, ]※これ以外の配列を使う場合はコード内に記載
filter (配列内から一致するものを配列で返す)
const countryFilter = users.filter(user => user.country === "usa") console.log(countryFilter) /* [ { name: 'J', age: 27, country: 'usa' }, { name: 'R', age: 64, country: 'usa' } ] */const ageFilter = users.filter((user) => { if (user.age > 30) {//ageが30より大きい return user } }) console.log(ageFilter) /* [ { name: 'R', age: 64, country: 'usa' }, { name: 'Y', age: 48, country: 'ja' }, { name: 'B', age: 33, country: 'ja' } ] */
第一引数
に配列要素、第二引数
にmap
と同様にインデックスが取れるmap (元の配列から新しい配列を作ったり、変更を加えたり)
const nameMap = users.map(user => user.name)//nameだけの配列を作る console.log(nameMap)//[ 'A', 'J', 'R', 'Y', 'B' ] const ageMap = users.map(user => user.age)//ageだけの配列を作る console.log(ageMap)//[ 23, 27, 64, 48, 33 ] const countryMap = users.map(user => user.country.toUpperCase())//大文字に変換 console.log(countryMap)// [ 'JA', 'USA', 'USA', 'JA', 'JA' ] const ageAddintion = ageMap.map(age => age + 10)//ageに10足す console.log(ageAddintion)//[ 33, 37, 74, 58, 43 ] //変更を加えた配列を合わせて新しい配列を作る const nameAndAge = nameMap.map((name, i) => ({name: name, age: ageAddintion[i], country: countryMap[i]})) console.log(nameAndAge) /* [ { name: 'A', age: 33, country: 'JA' }, { name: 'J', age: 37, country: 'USA' }, { name: 'R', age: 74, country: 'USA' }, { name: 'Y', age: 58, country: 'JA' }, { name: 'B', age: 43, country: 'JA' } ] */ console.log(users)//元の配列はそのまま /* [ { name: 'A', age: 23, country: 'ja' }, { name: 'J', age: 27, country: 'usa' }, { name: 'R', age: 64, country: 'usa' }, { name: 'Y', age: 48, country: 'ja' }, { name: 'B', age: 33, country: 'ja' } ] */長さの違う配列をいれると
const test = [ 33, 37, 74, 58, 43, 21, 40 ]//21, 40を増やした const sample1 = nameMap.map((name, i) => ({name: name, age: test[i], country: countryMap[i]}))//age部分を変える console.log(sample1)//nameMapの長さが基準になるので増やした二つは表示されない /* [ { name: 'A', age: 33, country: 'JA' }, { name: 'J', age: 37, country: 'USA' }, { name: 'R', age: 74, country: 'USA' }, { name: 'Y', age: 58, country: 'JA' }, { name: 'B', age: 43, country: 'JA' } ] */ //testを基準にすると const sample2 = test.map((test, i) => ({name: nameMap[i], age: test, country: countryMap[i]})) console.log(sample2) /* [ { name: 'A', age: 33, country: 'JA' }, { name: 'J', age: 37, country: 'USA' }, { name: 'R', age: 74, country: 'USA' }, { name: 'Y', age: 58, country: 'JA' }, { name: 'B', age: 43, country: 'JA' }, { name: undefined, age: 21, country: undefined }, { name: undefined, age: 40, country: undefined } ] */ //[5][6]にname, countryの値はないのでundefinedになる
第一引数
に配列要素、第二引数
にインデックスが取れる、第三引数
には配列そのものが入るreduce (配列内を合計したりとか)
const numbers = [3, 7, 1, 8, 15] const sum1 = numbers.reduce((a, num) => a + num, 0)//aの初期値は0 console.log(sum1)//34 //第一引数が初期値になりnum, 0としている部分の0がaに入る事になる const sum2 = numbers.reduce((a, num) => a + num, 100)//aの初期値は100 console.log(sum2)//134 //初期値が0などの場合省略できる const sum3 = numbers.reduce((a, num) => a + num)//aの初期値は配列の[0]の3になり、そこに順番に足していくイメージ console.log(sum3)//34 const hello = ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]//この様な文字列の配列も一つの値にできる const world = hello.reduce((a, b) => a + b) console.log(world)//HelloWorldイメージとしては
let a = 0 a += 3//0 + 3 a += 7//3 + 7 a += 1//10 + 1 a += 8//11 + 8 a += 15//19 + 15 console.log(a)//34join (配列内の文字列を一つの文字列にまとめる)
const hello = ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]//この様な文字列の配列も一つの値にできる const world = hello.join() console.log(world)//H,e,l,l,o,W,o,r,l,d第一引数はセパレータで、デフォルトは
,
文字列同士の間になにを入れるか指定できるconst world1 = hello.join("-")//-で区切る console.log(world1)//H-e-l-l-o-W-o-r-l-d const world2 = hello.join("")//空文字 console.log(world2)//HelloWorld以上です!
- 投稿日:2020-09-08T12:51:54+09:00
viewportでレスポンシブしている時にPC版表示をする
環境
初書:2020/09/08タイトルの付け方が適当な気がしてならないが、いいのが思いつかなかった
前提
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0">
viewport
でdevice-width
を使用していて、cssで@media screen and (max-width: 600px)
のような感じでレスポンシブウェブデザインに対応させている場合javascriptで動的変更する
…ことが出来る。しかも一行で
document.getElementsByName("viewport")[0].setAttribute('content','width=1080, initial-scale=1.0,minimum-scale=1.0');
document.getElementsByName("viewport")
で、metaタグを取得する事が可能なのでsetAttribute
でcontent
箇所を書き換える。
([0]
なのは、viewportという名前はおそらくこれしかないだろうという観点から。もし他の要素にviewportという名前を付ける場合は、単純に[0]にするとバグが生じるかもしれない)注意点
viewportを書き換える形の場合、スマホの横幅に合わせてサイズが細かくなるわけではなく、
画面外に伸びてサイズが変更されるので(下の図の感じ)、cssでvw
を使ってサイズを指定している箇所はレイアウトが崩れる。|---------------------------------------------| //width=1080に設定した時 |------------------| // スマホ画面(400くらい?左右にスクロールが出来るようになる)参考
- 投稿日:2020-09-08T10:45:17+09:00
Rubyの古いgemで自動ファイルアップロード Watirでの対処法
本稿について
WatirでWebサイトに対してファイルアップロードするスクリプトを書いていましたが、
古い環境で情報が見つけづらく困ったところ解決したので備忘録です。環境
CentOS Linux 7
Ruby 2.0.0p648
仕様gemはWatir
ブラウザはFirefoxを使っていますが、Chromeなど、別のブラウザでもやりたいこと
あるWebサイトに対して、Watirをつかって自動スクリプト起動でファイルアップロードさせたい。
アップロード画面ではSelect file というボタンを押して、ファイル選択画面が出た際にそこからファイルを選んでアップロードさせる仕様躓いたこと
アップロード画面からファイル選択をする挙動ができておりません。
OS標準のファイル選択画面が開いた時点で、ブラウザ外の動作になってしまうのかスクリプトの操作が及びません。試したこと
- watir内部でjavascriptを実行できるので、 クリップボードなどを使ってコピペができないか など試しましたがそもそも画面の仕様がスクリプトでのクリップボード経由の ペーストを受け付けていないでいるらしく、ペーストがエラーも吐かずに静かに何も起こらず終了してしまいます。
- 強引にキーイベントをjavascriptから発動させる。 下記のようなコマンドを実行して強制的にEngerキーやTabキーなど打つことできないか試しましたが、 ブラウザ自体にフォーカスがあたってしまっている?ようで、ファイル選択画面がキーコマンドを受け付けないでいます。
browser.execute_script(" document.dispatchEvent( new KeyboardEvent( 'keydown', { keyCode: 86 , ctrlKey: true , key: 'V' }) ); ")(参考:https://ameblo.jp/personwritep/entry-12456996738.html)
などなど試しましたが、対象のファイルを読みに行くなどの動作は行ってくれません。Webサイトの仕様・画面イメージ
ファイルアップロード用の画面が下記で、
Select file
ボタン押す事ができます。すると下記のようなOS標準のファイル選択画面が出て、そこからファイルをアップロードするような作りになっています。
人の手で操作する際は
Select file
ボタンを押してからファイルを選択するか、ドラッグ&ドロップでファイルアップロードすることも可能です。出来た
結果、これでできてしまいました。
$browser.file_field(:id, //).set("filepath\/...\/file.png")参考にした記事
https://stackoverflow.com/questions/15163816/automating-a-file-upload-with-watir-in-chrome-on-osx
https://www.rubydoc.info/gems/watir/1.8.1/Watir%2FContainer:file_field最後に一言
いやWatirて。
- 投稿日:2020-09-08T06:47:57+09:00
無料でSSR・ホスティング・API鯖を立てれるVercel。TypeScript・ExpressでAPI鯖を立てる。
Vercel
https://vercel.com無料で有名なFaaS(Function as a Service)の
Firebase FunctionsやNetlify Functionsより使い勝手がいいと思う。GitHubの捨てアカ作ってログインしようとしたら弾かれた。
たぶんアカを作ってからある程度時間が経たないと弾かれるっぽい。ソースコード
package.json{ "scripts": { "ts-build": "webpack --mode production" }, "devDependencies": { "@types/express": "^4.17.8", "ts-loader": "^8.0.3", "typescript": "^4.0.2", "webpack": "^4.44.1", "webpack-cli": "^3.3.12", "webpack-node-externals": "^2.5.2" }, "dependencies": { "express": "^4.17.1", "vercel": "^20.1.0" } }src/index.tsimport * as Express from 'express'; const app = Express(); // postリクエスト使えるようにする app.use(Express.json()); app.use(Express.urlencoded({ extended: true })); app.get('/get/:name', (req: Express.Request, res: Express.Response) => { try { res.send({ name: req.params.name }); } catch (error) { res.sendStatus(500); } }); app.post('/post', (req: Express.Request, res: Express.Response) => { try { res.send({ name: req.body.name }); } catch (error) { res.sendStatus(500); } }); if (!process.env.NOW_REGION) { app.listen(process.env.PORT || 3000); } export default app;tsconfig.json{ "compilerOptions": { "baseUrl": "./", "paths": { "src/*": ["src/*"] }, "strict": true } }vercel.json{ "version": 2, "builds": [ { "src": "index.js", "use": "@now/node-server" } ], "routes": [ { "src": "/.*", "dest": "/index.js" } ] }webpack.config.jsconst path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { // モード値を production に設定すると最適化された状態で、 // development に設定するとソースマップ有効でJSファイルが出力される mode: 'development', entry: './src/index.ts', // ファイルの出力設定 output: { path: `${__dirname}/`, filename: 'index.js', libraryTarget: 'this', }, module: { rules: [ { test: /\.ts$/, exclude: /node_modules/, loader: 'ts-loader', options: { configFile: path.resolve(__dirname, 'tsconfig.json'), }, }, ], }, // import 文で .ts ファイルを解決するため // これを定義しないと import 文で拡張子を書く必要が生まれる。 resolve: { // 拡張子を配列で指定 extensions: ['.ts', '.js'], alias: { src: path.resolve(__dirname, 'src'), }, }, externals: [nodeExternals()], };.gitignore.vercel node_modules index.jsコマンド
TypeScriptをビルド
$ npm run ts-build
ローカルで実行 ポート番号3000で鯖が立ち上がる
$ npx vercel dev
デプロイ
$ npx vercel --prodワイの成果物
https://qiita.com/yuzuru2/items/b5a34ad07d38ab1e7378
①コード共有サイト(SPA) React
https://code.itsumen.com②画像共有サイト(SPA) React
https://gazou.itsumen.com③チャット(SSR) Nuxt.js
https://nuxtchat.itsumen.com④チャット(SPA) React
https://chat4.itsumen.com⑤掲示板(SSR) Next.js
https://board.itsumen.com⑥掲示板(SPA) Vue
https://board.itsumen.com⑦レジの店員を呼ぶスマホアプリ(Android)
https://play.google.com/store/apps/details?id=com.itsumen.regi&hl=ja⑧ブログ(静的サイトジェネレータ) Hugo
https://yuzuru.itsumen.com
- 投稿日:2020-09-08T03:49:33+09:00
【JavaScript・関数学習・output】
◆JavaScriptの関数の種類や違い。
▼関数定義の種類:特徴
・関数宣言/関数式:標準的な関数の定義。
・無名関数:関数を多く使用する時にコードである時に使用する。関数名の重複を避けることができる。
・即時関数:流用する可能性がない関数を定義する時に使用する。別途関数を定義する必要がない。
・アロー関数:無名関数または即時関数において、より省力したい時に使用する。
以降は具体的な記述式を書いていきます。
▼関数宣言/関数式
関数宣言の記述↓function 関数名 ( 引数 ) { //関数内の処理 }関数式の記述↓
変数 = function ( 引数 ) { //関数内の処理 }・上記の関数宣言と関数式の違いは読み込まれるタイミングが異なることにあります。関数宣言が先に読み込まれます。実害はあまりないそうですが特徴として覚えておきます。
▼無名関数
無名関数の記述↓
const hello = function(){ console.log('hello') }・関数名無しで関数を定義できるためより簡潔にコードを記述できる。
▼即時関数
即時関数の記述↓
(function countNum(num) { console.log(num) })(1)・()の中にfunctionから始まる関数定義そのものを配置することで、配置した関数を即実行する。
関数を呼び出す手間がはぶけます。▼アロー関数
アロー関数の記述↓
const 変数名 = () => { 処理 }・近年採用される事例が増えた、関数式の記述様式・・・・らしい。
functionを省力し()=>と記述し関数を定義する構文。より短いコードで関数を定義できる。◆end
・私が学んだJavaScriptにある関数の種類outputは以上です。
最初ははてなブログをすすめられましたが、学んだ内容をoutputするにはQiitaが良いとあらためて感じました。
知らないメソッド、オブジェクトの概念、変数、関数、私はRuby,Ruby on Rails,JavaScript,GitHubを現時点で(2020/9/8)学んでいます。
学んだ内容をまとめたい時にまた書かせていただきます。
- 投稿日:2020-09-08T00:59:47+09:00
論理演算子はboolean以外も返す const = AA | BB みたいなの
JavaScriptの「&&」「||」について盛大に勘違いをしていた件 - Qiita
const _perview = Number($body.getAttribute('data-perview')) || 'auto';えっなにこれ、って思ってた。
Number($body.getAttribute('data-perview'))
がtrue
であればNumber($body.getAttribute('data-perview'))
を返す。そうでなければ'auto'
を返す。そういう使い方出来るのね…if文的な時でしか使えないと思ってたよ本当に…
- 投稿日:2020-09-08T00:59:47+09:00
論理演算子はboolean以外も返す const = AA || BB みたいなの
JavaScriptの「&&」「||」について盛大に勘違いをしていた件 - Qiita
const _perview = Number($body.getAttribute('data-perview')) || 'auto';えっなにこれ、って思ってた。
Number($body.getAttribute('data-perview'))
がtrue
であればNumber($body.getAttribute('data-perview'))
を返す。そうでなければ'auto'
を返す。そういう使い方出来るのね…if文的な時でしか使えないと思ってたよ本当に…
- 投稿日:2020-09-08T00:51:58+09:00
Microsoft Teamsにメッセージと画像を投稿(Node.js)
Teamsのチャンネルに投稿用アカウント(Incoming Webhook)を追加して、
チャンネルにメッセージや画像を投稿する1.投稿したいチャンネルにIncoming Webhookを追加
- Teamsの投稿したいチャンネルで右クリックして [コネクタ] を選択
- [incoming Webhook] を検索して [追加] をクリック、次画面で再度 [追加] をクリック
- incoming Webhookの名前とアイコン画像を設定して [作成] をクリック
- Webhook用のURLが表示されるのでコピーして [完了] をクリック
2.base64にデコードした画像をメッセージに埋め込んでpostで送信
- node.jsの環境つくり、"fs"と"request"をインストール
- index.jsと同じ階層に画像を準備
- 以下のindex.jsを実行
index.jsconst fs = require('fs'); const request = require('request'); fs.readFile('画像名.png', 'base64', (date) => { let base64date = date; let message = "魔法陣ぐるぐる" //画像の上に書きたいメッセージ let options = { uri: "teamsからコピーしたURL", headers: { "Content-type": "application/json", }, json: { "text": message + "<br>" + "![]" + "(" + base64date + ")" } }; request.post(options); // postリクエスト送信 });
注意
- プライベートチャンネルにはこの方法で送信できない
- 画像サイズが大きすぎると、base64にエンコードしたときに文字列が長過ぎて送れない
- 投稿日:2020-09-08T00:51:58+09:00
Microsoft Teamsにメッセージと画像を送る(Node.js)
Teamsのチャンネルに投稿用アカウント(Incoming Webhook)を追加して、
チャンネルにメッセージや画像を投稿する1.投稿したいチャンネルにIncoming Webhookを追加
- Teamsの投稿したいチャンネルで右クリックして [コネクタ] を選択
- [incoming Webhook] を検索して [追加] をクリック、次画面で再度 [追加] をクリック
- incoming Webhookの名前とアイコン画像を設定して [作成] をクリック
- Webhook用のURLが表示されるのでコピーして [完了] をクリック
2.base64にエンコードした画像をメッセージに埋め込んでpostで送信
- node.jsの環境つくり、"fs"と"request"をインストール
- index.jsと同じ階層に画像を準備
- 以下のindex.jsを実行
index.jsconst fs = require('fs'); const request = require('request'); fs.readFile('画像名.png', 'base64', (date) => { let base64date = date; let message = "魔法陣ぐるぐる" //画像の上に書きたいメッセージ let options = { uri: "teamsからコピーしたURL", headers: { "Content-type": "application/json", }, json: { "text": message + "<br>" + "![]" + "(" + base64date + ")" } }; request.post(options); // postリクエスト送信 });
注意
- プライベートチャンネルにはこの方法で送信できない
- 画像サイズが大きすぎると、base64にエンコードしたときに文字列が長過ぎて送れない
- 投稿日:2020-09-08T00:49:58+09:00
gatsby入門 ブログ作ってサーバーにアップしてみる
gatsbyの作業履歴
gatsby入門 チュートリアルをこなす 0.開発環境をセットアップする
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(1)
gatsby入門 チュートリアルをこなす 1. ギャツビービルディングブロックについて知る(2)
gatsby入門 チュートリアルをこなす 2. ギャツビーのスタイリングの概要
gatsby入門 チュートリアルをこなす 3. ネストされたレイアウトコンポーネントの作成
gatsby入門 チュートリアルをこなす 4. ギャツビーのデータ
gatsby入門 チュートリアルをこなす 5. ソースプラグインとクエリされたデータのレンダリング
gatsby入門 チュートリアルをこなす 6. 変圧器プラグイン※Transformer pluginsのgoogle翻訳
gatsby入門 チュートリアルをこなす 7. プログラムでデータからページを作成する
gatsby入門 チュートリアルをこなす 8. 公開するサイトの準備
今回:gatsby入門 ブログ作ってサーバーにアップしてみる基本のチュートリアルはこなした。。。
さて何しましょう?とチュートリアルを見ているとgithubにブログ用のソースが上がっているとの記載が
https://github.com/gatsbyjs/gatsby-starter-blog
とりあえずブログでも作ってみっか!
んで、netlifyとgithubを使うのが主流みたいだけど、nginxの勉強もしたいので
とりあえず自分のGCPのVM環境にアップしてみようと思います。
飽きたらGCPから移行するかな?ブログを構築
22:00作業開始 ローカルにgatsbyサイトを作成
まずは、ローカルにgatsbyサイトを作成します。
以下コマンドを好きなディレクトリで実行。そして風呂。
gatsby new 3s-laboo-blog https://github.com/gatsbyjs/gatsby-starter-blog
※3s-laboo-blogは任意です。
なんか、ダウンロードすげぇ時間かかったな。
ダウンロード後サイトを開発モードで起動しながら、ビールをプシュっと。
cd 3s-laboo-blog
gatsby develop
http://localhost:8000/
おー。。。超シンプル。
まぁ、こっからアレンジすればいいが、今回は最低限にだけとどめよう。
ビール、グビグビ。22:50 ブログをちょっとだけ修正
まず、顔が私じゃない!
content/assets/profile-pic.jpgを変更
あとアイコン
content/assets/gatsby-icon.pngを変更
確認するとアイコンだけ出ていない。
gatsby-config.jsを少しいじってみる。
リスタートされたみたい。
改行されてないな。
src/stylesディレクトリを追加
src/styles/global.cssを追加し、以下を記述src/styles/global.csshtml { background-color: #e0ffff; white-space: pre-wrap; }pre-wrapは改行コードが反映されるよ!
gatsby-browser.jsを修正gatsby-browser.js// custom typefaces import "typeface-montserrat" import "typeface-merriweather" import "prismjs/themes/prism.css" import "./src/styles/global.css"←これ追加
あと、マークダウンファイルをちょいと直して。。。
とりあえずはこんなとこでしょ!
(後でかなり修正しないとな。。。)
ビールなくなった。
とりあえずコンパイル・起動
gatsby build
gatsby serve
動確問題ない。00:00 winscpでgcpにログインし、nginxのフォルダにアップロード
nginxの配置フォルダはnginxで設定したフォルダです。
アップロードするものは、newしたディレクトリ(ここでいう3s-laboo-blog直下)にあるpublicディレクトリ内のファイル・ディレクトリ。
https://3s-laboo.com/
イエーイ!
ということでわたくし3S Labooのブログを立ち上がりました。
時刻は00:46
風呂入ってビール飲んで歯を磨いたのも含めて3時間位で作れたー今後修正していきまーす。
今回はここまで。ありがとうございました。
- 投稿日:2020-09-08T00:44:01+09:00
fitbitのapi連携と集中力判定(PHP,Javascript)
記事の概要
作成したポートフォリオの解説です。以下をまとめています。
- 背景
- 主な機能
- 開発手順
- 工夫点
- 課題
背景
「ポモドーロ・テクニック」という時間管理術が話題になっており、そのためのアプリなどもあるが、実際に集中しているかどうかを判断するのは単純なポモドーロタイマーだけでは難しい。
今回は心拍数と集中力の関係に着目し、集中力を判定するアプリを作成しました。スペック
- 言語
- PHP 7.4.2
- javascript
- DBMS
- MySQL 5.7.26
- 開発環境
- MacOS Catalina 10.15.5
- MAMP 5.7
- ライブラリ
- jquery
- フレームワーク
- Bootstrap 4.2
- バージョン管理
- Git 2.24.3
- 本番環境
- xserver
- ウェアラブル端末
- fitbit inspireHR
主な機能
ポモドーロタイマー
・ホーム画面
スタートを押すと25分タイマーが作動します。
タイマーの下には今日と今週の作業記録が表示されます。
・25分が終わると
「お疲れ様です。5分休憩しましょう!」が表示されます。
データ
・データ画面
「1週間のポモドーロ回数の推移」と「直近の作業の心拍数の推移が表示されます。
開発手順
- 要件定義
- 環境設定
- データベース設計
- コーディング
- xserverデプロイ
1.用件定義
今回作成するアプリに必要な機能
・タイマー機能
・データ記録
・データ表示
・チャート表示
・fitbitのAPIからのデータ取得php,Javascriptで開発を行う。
2.環境選定
本番環境では知名度の高い「xserver」を使用する。
GitとGitHubは練習として使っていく。3.データベース設計
カラムはシンプルに
・ID
・start_at(作業開始時間)
・stop_at(作業終了時間)4.コーディング
コーディングを実施
4.1データベース作成
MAMPのphpMyAdminを使ってデータベースを作成。
4.2ホーム画面
タイマーのカウント表示はJavasciptで動的に実装。
「START」、「STOP」、「RESET」ボタンを押した時の処理を実装。
作業終了時、DBに自動的に登録される。(Ajax)
画面のデザインはBootstrapを用いて時間短縮。4.3データ画面
グラフはchartistというjsのライブラリで作成。
作業記録はホーム画面同様、MAMPのDBから取得する。心拍数の記録はfitbitのAPIから取得。
以下サイトを参考にカスタマイズ
https://qiita.com/RINYU_DRVO/items/6607a0aa7ca3294f8e47MAMPのDBから直近のデータを取り出してAPIに渡すことで
直近の作業時間あたりの心拍数の推移を取得。
(ここが一番苦労した、、、)取得した心拍数のデータをもとに
平均心拍数と集中力判定を実施。集中力判定は以下を基準に作成。
1.高集中:平均心拍数が高く、心拍数の分散(以下、分散)が低い
2.高ストレス:平均心拍数が高く、分散が高い
3.安定集中:平均心拍数が低く、分散が低い
4.集中していない:平均心拍数が低く、分散が高い※閾値については今後調整します。
5.xserverデプロイ
本番環境ではxserverを使用。
xserver上にDBを立て、プログラムもDB接続部分の修正。工夫点
・世の中にない機能を考え、実装まで持って行った
・自分で使いたいと思うようなアプリにすることができた今後の課題
一通りの動作ができた時点で完成としました。
主な課題は以下の通りです。
- チャートの追加(1週間ごとの比較など)
- 作業内容の記録(どんな作業の時にどんな集中状態なのか評価するため)
参考文献
GitHubアカウント
freedog1
https://github.com/freedog1/pomodoro