- 投稿日:2020-11-26T23:41:11+09:00
JavaScript における配列コピー
JavaScript における配列コピーの問題
JavaScript における配列( array )はオブジェクトのため参照コピーされる。
つまり、コピー先の配列を変更すると、コピー元の配列が変更されてしまうといった事象が発生する。
例えば次のようになる。array = [1,2,3]; arrayCopy = array; arrayCopy[0] = 4; console.log(arrayCopy); // [4,2,3] console.log(array); // [4,2,3] : コピー元の値も変更されるJavaScript における配列コピーの解決策
2020年11月現在ではいくつかの方法があるので代表的なものを紹介する。
slice()
速度が早い、と言われている。
array = [1,2,3]; arrayCopy = array.slice();concat()
slice() 同様、速度が早い、と言われている。
array = [1,2,3]; arrayCopy = array.concat();スプレッド構文( ES2015 )
最も直感的な記述だと思われる。
array = [1,2,3]; arrayCopy = [...array];その他
for() や while() 、map() 、filter() 、reduce()、from() による方法あり。
参考
- 投稿日:2020-11-26T23:19:06+09:00
【Nuxt.js】Firebaseとの連携方法
firebaseパッケージのインストール
ターミナルプロジェクト名$ npm install --save firebasefirebase pluginの作成
ターミナルプロジェクト名$ touch plugins/firebase.jsplugins/firebase.jsimport firebase from 'firebase/app' import 'firebase/firestore' if (!firebase.apps.length) { firebase.initializeApp( { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, databaseURL: process.env.FIREBASE_DATABASE_URL, projectId: process.env.FIREBASE_PROJECT_ID, storageBucket: process.env.FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, appId: process.env.FIREBASE_APP_ID, measurementId: process.env.FIREBASE_MEASUREMENT_ID } ) } export default ({ app }, inject) => { inject('firebase', firebase); }環境変数の設定方法はこちら↓
【Nuxt.js】「dotenv」を使った環境変数の設定方法
nuxt.config.js
の設定nuxt.config.js//...省略 // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) plugins: [ '~/plugins/firebase' ], //...省略動作確認
pages/index.vue
内に送信ボタンを設け、
クリックでfirestoreにデータを送る。pages/index.vue<template> <div class="container"> <button @click="submit">送信</button> </div> </template> <script> export default { methods: { submit() { const db = this.$firebase.firestore(); db.collection('user').add({ name: 'hoge' }) .then(() => { console.log('成功'); }) .catch((e) => { console.log(e.message); }); } } } </script>
- 投稿日:2020-11-26T20:12:51+09:00
algolia(instantsearch.js)でイニシャル検索ってどうやるの??
クリスマスまであと13日!!ヽ(=´▽`=)ノ
本日は再び大野が担当致しますm(_ _)m
(1日目の記事も担当しておりました!)
algolia
(instantsearch.js
)でページ表示タイミングで検索ってどうやるの??はい、コレは純粋に私が直面した問題でした。
algolia
はとても便利なのですが如何せん日本語の情報が少ない気がしますね・・・結論から言うととても簡単です。
instantsearch.widgets.configure({ filters: filters, }),これを使えばいけます!!b(・∀・)
具体的なコードは以下
// お決まりの初期化 const search = instantsearch({ indexName: searchIndex, searchClient: algoliasearch('keystring', 'secretstring'), routing: true, }); // 各種検索設定 search.addWidgets([ // 検索ボックスの配置 instantsearch.widgets.searchBox({ container: '#search', placeholder: 'フリーワード...', showReset: true, showLoadingIndicator: true, }), // 初期検索条件をセット! instantsearch.widgets.configure({ filters: ['available: true AND (NOT uid: null)'], }), // 検索結果表示設定 instantsearch.widgets.hits({ ・・・こんな感じで初期検索条件を指定出来るようになっています。
(エアコーディングですが有効フラグtrueでuidがnullじゃないデータと言う検索条件を付与しています。)初期条件の書き方はコチラを参考にしています。
https://www.algolia.com/doc/api-reference/api-parameters/filters/
コレだと初期検索条件がずっと検索条件に付いちゃうんだけど・・・
はい、その通りです。
完全に初回の検索条件を指定したいだけなんだけど・・・と言う場合はsearchFunction
と言うのを初期化時に指定する事で可能です。// お決まりの初期化 var initialsearch = false; const search = instantsearch({ indexName: searchIndex, searchClient: algoliasearch('keystring', 'secretstring'), routing: true, // 検索前処理の追加 searchFunction: function(helper) { if (false === initialsearch) { initialsearch = true; // 最初の検索条件を指定 helper.setQuery(helper.state.query + '&available=true').search(); } } });これで初期化時の最初の検索の時だけ検索条件を変えるなどといった事が出来ます。
https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-searchfunction
参考になりましたら幸いですm(_ _)m
- 投稿日:2020-11-26T19:14:25+09:00
Riot.js V4系であえてロケーションハッシュ(#xxx)でルーティングをする
今年もアドベントカレンダーの季節がやってきました!!!ヽ(=´▽`=)ノ
弊社デジクエのカレンダー初日を担当させて頂きます、リードエンジニアの大野です。よろしくお願いしますm(_ _)m
Riot.js
V4系であえてロケーションハッシュ(#xxx)でルーティングをする弊社では私の好みも多分に含まれますが
Riot.js
を使ったSPA
を作ることが多いです。
最近ではSSR
等も流行なようなのでSSR
で昔ながらの 「/(スラッシュ)」によるディレクティブなルーティングをするSPA
が増えていますね。
Riot4
系のルーターも標準では「/(スラッシュ)」によるルーティングになっているようです。
しかしそんな中ではありますが、あえてディレクティブなものでなく「#(ハッシュ)」を使ったルーティングを設定して見たので、「#(ハッシュ)」派の人は是非ご参考下さい!m(_ _)m※管理ツールなどで特に
SSR
を必要しない要件で、さっくりSPA
したい派の方特に必見です!STEP1. Roit4系のルーターの読み込み
標準?で提供されているコチラをまずルーターとして使います。
https://github.com/riot/route<!DOCTYPE html> <html> <head lang="ja"> <meta charset="UTF-8"> <title>Riotルーターサンプル</title> </head> <body> <h1>Riot4ルーターサンプル</h1> </body> <!-- riot4本体 --> <script src="//unpkg.com/riot@4/riot+compiler.min.js"></script> <!-- Riot4用のルーター --> <script src="//unpkg.com/@riotjs/route@4.0.0-beta.1/route.js"></script> </html>シンプルにベースはこんな感じです。
STEP2. シンプルなHello worldサンプル
先ずは、シンプルに2ページを行き来するサンプルを用意
<!DOCTYPE html> <html> <head lang="ja"> <meta charset="UTF-8"> <title>Riotルーターサンプル</title> </head> <body> <h1>Riot4ルーターサンプル</h1> <ul> <li><a href="./">トップ</a></li> <li><a href="./sample">サンプル</a></li> </ul> <app></app> </body> <!-- riot4本体 --> <script src="//unpkg.com/riot@4/riot+compiler.min.js"></script> <!-- Riot4用のルーター --> <script src="//unpkg.com/@riotjs/route@7.0.0/route.js"></script> <!-- appタグをインライン定義 --> <template id="app"> <app> <router> <route path="{window.location.pathname}"> <h2>Hello world!</h2> </route> <route path="{window.location.pathname}sample"> <h2>サンプル!</h2> </route> </router> </app> </template> <script> // appタグを読み込み riot.inject(riot.compileFromString(document.getElementById('app').innerHTML).code, 'app', './app.html'); // riotをコンパイル riot.compile().then(() => { riot.register('route', route.Route); riot.register('router', route.Router); // ルートディレクトリ外でも動くようにベースパスを設定 route.setBase(`${window.protocol}//${window.host}${window.location.pathname}`); riot.mount('app'); }); </script> </html>ブラウザで実行したサンプルは以下
http://plnkr.co/plunk/IogODWhBcSlmVAzwルーターを入れた基本はこんな感じになってます。
サンプルを実際にローカルで実行してみるとわかると思いますが、デフォルトではルーターは「/(スラッシュ)」によるディレクティブなルーターになっていると思います。
本来はこれで十分に満足出来るんじゃないでしょうか。これを「#(ハッシュ)」ベースのルーターに変えるんですが、実は凄く簡単です。
STEP3. 「#(ハッシュ)」ベースのルーターに変えたサンプル
実はものすごく簡単で
route.setBase(`${window.protocol}//${window.host}${window.location.pathname}`);の行を#を含めた形に変えて上げて
route.setBase(`${window.protocol}//${window.host}${window.location.pathname}#`);後はリンクとpathを#式に変えてあげるだけでOKです。
実際の書き換えた後のサンプルは以下
<!DOCTYPE html> <html> <head lang="ja"> <meta charset="UTF-8"> <title>Riotルーターサンプル</title> </head> <body> <h1>Riot4ルーターサンプル</h1> <ul> <li><a href="#">トップ</a></li> <li><a href="#sample">サンプル</a></li> </ul> <app></app> </body> <!-- riot4本体 --> <script src="//unpkg.com/riot@4/riot+compiler.min.js"></script> <!-- Riot4用のルーター --> <script src="//unpkg.com/@riotjs/route@7.0.0/route.js"></script> <!-- appタグをインライン定義 --> <template id="app"> <app> <router> <route path="{window.location.pathname}"> <h2>Hello world!</h2> </route> <route path="{window.location.pathname}#sample"> <h2>サンプル!</h2> </route> </router> </app> </template> <script> // appタグを読み込み riot.inject(riot.compileFromString(document.getElementById('app').innerHTML).code, 'app', './app.html'); // riotをコンパイル riot.compile().then(() => { riot.register('route', route.Route); riot.register('router', route.Router); // ルートディレクトリ外でも動くようにベースパスを設定 route.setBase(`${window.protocol}//${window.host}${window.location.pathname}#`); riot.mount('app'); }); </script> </html>ブラウザでの実行結果は以下
http://plnkr.co/plunk/veYITpKjBuVEc91Aこんな感じで非常に簡単に「#(ハッシュ)」ベースに変えられました!
まとめ
「#(ハッシュ)」ベースのルーターの場合のいいところは
SSR
と逆行しますがサーバーサイドの設定などは特に不要になる点かなと思います。
管理ツールだったり、非公開のアプリケーションであったりの場合は特にSSR
に絶対にしないとならないシチュエーションでは無く、サックリSPA
が作れればいい!と言うことも多々あると思ってます。
(特にRiot
はブラウザコンパイル出来てとてもサクッとSPA
出来るので尚更)
そう言う時のTIPSとして是非ご参考下さいm(_ _)m
- 投稿日:2020-11-26T18:55:34+09:00
Google Chrome 開発ツールのwaring:'DevTools failed to load SourceMap' を非表示にする
- 投稿日:2020-11-26T18:31:12+09:00
javascriptのforEachを利用する際の注意点
javascriptのforEachについて
色々な言語には、for、foreachが用意されています。
javascriptで言うと、foreachはforEach()に当たるっぽいですね。最近までちょっと勘違いしていたことがあります。
その気付きを共有したいと思います。他言語とjavascriptのforEachはそもそも考え方が違う。
例えばPHPで言うと、foreachは、配列、オブジェクトで利用できるfor文ですね。
利用できる型が決められていますが、動作は大体forと一緒です。javascriptの.forEach()も同じだと思ってました。
ですが、そもそもforEach()は根本的な考え方が違うことに気がつきました。javascriptのforEach()は「繰り返し」の命令ではない!
使い方自体は、他言語と似ていますよね。
forEach.jsconst array = ['a','b','c']; array.forEach(element => console.log(element));じゃあこれって何が違うの?
もう少し分かり易く書いてみましょう。forEach2.jsconst array = ['a','b','c']; array.forEach(function(element){ console.log(element); });上記は書き方を変えただけです。
1番重要な点は、これがfunction(即時関数)であると言う点です。
javascriptのforEach()は、配列の順番通り即時関数を叩くと言う命令である。と言うことです。ふーん。だからどうなの?
ここで重要になってくるのは、即時関数は、javascriptの言語特性上、平行で実行されると言う点です。
for命令は、配列のインデックスの0番目から順番に実行されます。
0番目の処理が完了した後に、1番目の処理を始めます。一方、forEachで叩かれた即時関数は、即時関数が叩かれるタイミングこそ0番目からですが、0番目が終わった後に1番目を実行。と言う動作にはなりません。
平行処理が行われますので、1番目に叩かれた配列index0が一番最初に終わるとは限らないのです。
ここが最も重要な点です。forと動作は同じだと思って、forEachを使うと、想定していた結果にならないと言うのは、大体この特性のせいであると、最近気づきました。
javascriptの言語特性と、即時関数である。と言うことに気がつけばすぐたどり着ける内容ではありますが、以前かなりはまった部分なので、私と同じ悩みを抱えている方がいれば、参考にしていただけたらと思います。
- 投稿日:2020-11-26T18:15:14+09:00
Web Workerを実装してみる
Web Workerって?
Web Worker とは、ウェブアプリケーションにおけるスクリプトの処理をメインとは別のスレッドに移し、バックグラウンドでの実行を可能にする仕組みのことです。時間のかかる処理を別のスレッドに移すことが出来るため、 UI を担当するメインスレッドの処理を中断・遅延させずに実行できるという利点があります。
by MDN
JSはシングルスレッドですので、本当の意味での並列処理が行えません。
なのでめちゃ重な処理を書いてしまうと、その他のボタンやら何やら効かなくなって、サイトが完全に固まってしまいます。
これを解消してくれるのがWeb Worker
です。
昨今ではSPAなどによりでクライアント側で重たい処理をすることが増えていますので、ぜひ使いこなしておきたいと思っています。現在ブラウザ側の対応はどうなってる?
by Can I use
Opera以外はほとんどOKですね。とにかくコード
index.jsconst worker = new Worker('worker.js'); worker.addEventListener('message', (e) => { console.log('Workerから受け取ったデータは: ', e.data); }, false); worker.postMessage('Hello, world');worker.jsself.addEventListener('message',(e) => { self.postMessage(e.data); }, false);ちなみに、workerのスクリプト内のグローバルはワーカー自身なので、下記のように書いてもOKです。
worker.jsaddEventListener('message',(e) => { postMessage(e.data); }, false);結果Hello, world図でみるとわかりやすいです。
それぞれのjsの
onmesssage
で、受信時/送信時のイベントを記述することができます。
受信データ/送信データはいずれも messsageイベントのdata属性から取得できます。
思ったよりも非常にシンプルです。workerを終了したい場合。
worker.terminate();
terminate
メソッドを使って直ちに終了することができます。エラーハンドリング
Worker で実行時エラーが発生すると、 onerror イベントハンドラーが呼び出されます。これは、 ErrorEvent インターフェイスを実装している error イベントを受け取ります。
とのことです。(by MDN)
早速試してみましょう。index.jsconst worker = new Worker('worker.js'); worker.addEventListener('error', function (e) { console.log(e) }, false); worker.postMessage('Hello World');worker.jsself.addEventListener('message', (e) => { throw new Error('何かしらのエラー') self.postMessage(e.data) }, false)確かに
ErrorEvent
をキャッチしています。たくさんのプロパティがありますが、下記の3つが重要な項目です。
message
・・・ 人間用メッセージ
filename
・・・ エラー発生元のファイル名
lineno
・・・エラーが発生した行番号スクリプト/ライブラリのimport
Workerスレッド側で何かインポートしたい場合は
importScripts()
を使います。
ここで引数として指定したスクリプトが実行されます。importScripts('test.js') importScripts('foo.js', 'bar.js')別ファイルでなく埋め込みでWorker
ファイルの記述を文字列化しBlobを生成、そしてそのURLを渡せばOK。
実際の実装ではあまり使わなそうだと思ったので例は割愛します。注意点
Workerは直接DOMを操作できない点、そしてwindowのメソッドやプロパティは使用できない点に注意です。(worker自身がグローバルのため)
逆にそれ以外ならほとんど使用可能です。
MDNに使用できるクラス・関数がまとめられています。
https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Functions_and_classes_available_to_workersまた、Workerとやり取りする際のデータは共有しているのではなく、それぞれでコピーされています。
なので渡したデータをWorker側で操作しても、コピー元には関与できない点に注意です。次回はService Workerについて学習予定です。
参考資料
https://www.html5rocks.com/ja/tutorials/workers/basics/
https://www.codit.work/notes/ytq5ztvnhceco3jn2bkb/
- 投稿日:2020-11-26T17:17:53+09:00
jQuery学習#01
jQueryの基本
・jQueryまたは$でアクセス。
・基本的な使い方は、要素を選択し、選択した要素群に対して操作を行うというもの。
↓基本的な書き方
$('セレクタ').メソッド('パラメータ[引数]');
#1. マウスクリックで処理を実行
・まず下記のようにHTMLを書く。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>jQuery テスト</title> <style> .parent { height: 100px; } </style> </head> <body> <div class="parent"> <button type="button" class="my-button">ボタン</button> </div> <div class="parent"> <button type="button" class="my-button">ボタン</button> </div> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="main.js"></script> </body> </html>と、下のようにボタンが2つ配置されたページが出来上がる。この段階では、ボタンを押しても何も起こらない。
・次にJavaScriptのファイルを、jQueryを使って下記のように書く。
$('.my-button').on('click', (e) => { $(e.target) .parent() .css({ 'background-color': '#ff6666'}); });説明。
まず、セレクタにclass="my-button"を指定。onメソッドを使用。
<button type="button" class="my-button">
がクリックされたとき、下記関数を呼び出す。(e) => { $(e.target) .parent() .css({ 'background-color': '#ff6666'}); }↑関数の説明の前に、まずparent()メソッドとe.targetを簡単に説明。
・parent()メソッド
指定した要素の一階層上の親要素を取得することができるメソッド。・e.target(eventオブジェクトのtargetプロパティ)
イベントの発生元であるオブジェクトを返す。というわけで、↑関数は、
<button type="button" class="my-button">
の一階層上の親要素である<div class="parent">
のbackground-colorに#ff6666を指定するというもの。#2. マウスオーバーに反応する
HTML:
<div class="box"></div>
CSS:.box { width: 200px; height: 200px; background-color: #ddd; }で
box
という名前のclass属性
を持つdiv要素
に下記のような装飾を施す。
JavaScript:
const onMouseenter = (e) => { $(e.target).css({ 'background-color': '#ff9999', }); }; const onMouseleave = (e) => { $(e.target).css({ 'background-color': '#dddddd', }); }; $('.box') .on('mouseenter', onMouseenter) .on('mouseleave', onMouseleave);
<div class="box">
にマウスが乗ったとき(mouseenter)、下記のように背景色が変わる。
マウスが離れたとき、指定した(#dddddd)に変化する。つまり下の灰色に戻る。
#3. キーボードの入力に反応させる
HTML:
<input type="text" class="my-input"> <div class="box"></div>JavaScript:
$('.my-input').on('input', (e) => { const value = $(e.target).val(); $('.box').text(value); });・inputイベント
input要素やtextarea要素へ入力するたびに発生する。・val()メソッド
HTMLタグ内に記述されているvalue属性を取得したり変更することができるメソッド。・text()メソッド
HTML要素内にあるテキスト情報を取得・追加・変更することが可能なメソッド。説明。
<input type="text" class="my-input">
に入力するたびに下記関数を呼び出す。(e) => { const value = $(e.target).val(); $('.box').text(value); }↑定数
value
にe.target
、つまり<input type="text" class="my-input">
のvalue属性を取得したものを代入する。
<div class="box">
に上で取得したvalue属性をテキスト情報として追加する。
- 投稿日:2020-11-26T17:14:46+09:00
[Node.js][Deno] オブジェクト合成各種 ベンチマーク比較
先ほどの クラスのベンチマーク の続編。
オブジェクト合成でより良い(というか速い)手法ないかなと悩みつつ。実験環境
同じく
- Node.js 14.15.1
- Deno 1.5.4
実験設定
- 10要素で構成されるオブジェクト2組を1つのオブジェクトにまとめる
- 要素のうち5つは名前が重複しており、合成元を合成先に上書きする
- 合成先はプロトタイプではなく、独立したオブジェクトとして新規に生成される
という前提で
- 合成先オブジェクトを生成するファンクションと、合成元オブジェクトを用意
- 実行時間計測
- 合成先オブジェクト生成ファンクションだけの結果
- 合成先オブジェクト生成ファンクション+各種アルゴリズムの結果
- 結果表示
- 各種アルゴリズムの結果部分を抽出表示
という手順。
オブジェクト合成アルゴリズム ここに集う
今回調査するアルゴリズムを御紹介いたしましょう。
- assign
- Object.assign()
- spread
- スプレッド構文
- for
- for inループ
- forEach
- keys forEachコールバック
- map
- keys mapコールバック
- entries
- entries列挙ループ
- lowtech1
- (対照用) オブジェクト要素として個別に代入
- lowtech2
- (対照用) 連想配列要素として個別に代入
多少のオーバーヘッドは仕方ないにしても、できるだけ対照用で挙げた方法に
近い(というか速い)アルゴリズムを採用したいという趣旨。いざ、実験
今回は記述がコンパクトなので、1ソースでまとめていけます
bench_obj_merge.jsfunction CreateParent(){return {a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9};} var sub={f:10,g:11,h:12,i:13,j:14,k:15,l:16,m:17,n:18,o:19}; // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー create:()=>CreateParent(), assign:()=>Object.assign(CreateParent(),sub), spread:()=>{return {...CreateParent(),...sub};}, for:()=>{ var t=CreateParent(); for(var k in sub)t[k]=sub[k]; return t; }, forEach:()=>{ var t=CreateParent(); Object.keys(sub).forEach(k=>t[k]=sub[k]); return t; }, map:()=>{ var t=CreateParent(); Object.keys(sub).map(k=>t[k]=sub[k]); return t; }, entries:()=>{ var t=CreateParent(); for(var [k,v] of Object.entries(sub))t[k]=v; return t; }, lowtech1:()=>{ var t=CreateParent(); t.f=sub.f; t.g=sub.g; t.h=sub.h; t.i=sub.i; t.j=sub.j; t.k=sub.k; t.l=sub.l; t.m=sub.m; t.n=sub.n; t.o=sub.o; return t; }, lowtech2:()=>{ var t=CreateParent(); t['f']=sub['f']; t['g']=sub['g']; t['h']=sub['h']; t['i']=sub['i']; t['j']=sub['j']; t['k']=sub['k']; t['l']=sub['l']; t['m']=sub['m']; t['n']=sub['n']; t['o']=sub['o']; return t; }, }; // 計測結果を書き込むところ var rec={}; // 動作テスト //for(var k in funx)console.log([k,funx[k]()]); // 繰り返し実行時間計測 var loop=Array(1000000); for(var k in funx){ var f=funx[k]; var bgn=new Date; for(var i of loop)f(); var end=new Date; rec[k]=(end-bgn); } // 結果表示 for(var k in rec)console.log(k+' :'+(rec[k]-rec.create));ここで予想外の事態。当初は 前回 と同じ1千万ループだったのですが、なかなか処理終わらなかったので一旦止めて桁減らしちゃいました。
それでループ回数が1桁減っているので、前回の結果と比べるときは御注意ください。
Node.js Deno assign 255.7 265.4 spread 6906.3 6503.3 for 289.2 484.4 forEach 413.6 463.1 map 445.5 505.7 entries 417.3 428.4 lowtech1 10.1 8 lowtech2 10.3 8 まず目を疑ったのが、今(一部で)流行りのスプレッド構文、なんと1桁遅い。
記述が一番シンプルなだけに残念なところ。
で、とりあえず Object.assign() がベストというか幾分マシということで。
もっと素敵な手法ないものかしらん。
- 投稿日:2020-11-26T17:08:15+09:00
JavaScriptのデータ型について
JavaScriptで扱うデータの種類について
JavaScriptでは8種類のデータ型が定義されています。
よく出てくるものの例として以下の物が挙げられます。
- Number(数値)
- 3, 5, 18, 0 etc...
- String(文字列)
- 'Hello', 'Good' etc...
- Boolean(真偽値)
- true, false
- Object(オブジェクト)
またJavaScriptにはデータ型の種類を調べる
typeof
演算子というものが用意されています。console.log(typeof 5); console.log(typeof 'Hellow'); console.log(typeof true);出力結果
number string booleanまとめ
まだ初学者のため使い所がいまいちピンと来ていませんが(そういう人僕の他にもいますか?)いつかあ〜typeof演算子ね〜ウンチクウンチクくらいになれたらいいな、、、、
参考
- 投稿日:2020-11-26T17:05:59+09:00
Vue-CLI + Vuexでポケモンのパーティを構築するSPAをつくる。
概要
ポケモンのパーティを構築して遊べるSPAです。
VueでSPAを作りたいなーと思って作り始めた。
加えて、listの高速なフィルター機能も使いたい。
実装はGitHubPagesを使ってフロントエンドのみ。技術・ライブラリ
- Vue-CLI
- Vuex
- lodash
- moji
- axios
- ityped
- babel-polyfill
- reset-css
- progressbar.js
つくったもの
パーティをつくる
ポケモンずかんからポケモンをピックアップし、パーティを構築する。
パーティはVuexのstoreで管理し、てもち画面で確認できる。
ポケモンの詳細をみる
vue-routerを利用して詳細画面を生成。
ポケモンのステータスを確認できる。詳細設計
ポケモンのステータス、画像
axiosで外部APIから取得。
フォント
検索機能
computed()
でフィルター、ソートを実現。
早くてかっこいい。(全データを取得してるだけだから数が大きくなると問題だけど。)ソート
- なまえ
- ばんごう
フィルター
- なまえ検索
- タイプ
どれもjsで操作しているだけなのでレスポンスが早い。
さらになまえ検索は日本語入力未決定前でも検索できるようにしている。早い。vue<input @input="handleInput"></input>jshandleInput(event) { this.wd = event.target.value //未決定の値を取得 },タイプフィルターはチェックボックスから選ぶ。
おわり
細かい実装やその方針などは省略していますが、素人ながらかなり手こずりました。
良い勉強になりました。
- 投稿日:2020-11-26T15:52:23+09:00
Material-UIを使ってダイアログを表示するには?
Material-UIを使ってダイアログを表示するには一筋縄ではいかずパラメータを渡す必要があります。
なので今回は「Material-UIを使ってダイアログを表示する方法」を紹介します。この機能は3つのファイルで構成されています。
- 一覧画面(index.jsx)
- アイテムコンポーネント(item.jsx)
- ダイアログコンポーネント(dialog.jsx)
1.一覧画面
index.jsximport React from 'react'; import Box from '@material-ui/core/Box'; import Item from "@comp/item"; import Grid from '@material-ui/core/Grid'; import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme) => ({ gridItem: { marginTop: theme.spacing(2), }, })); // 表示するアイテムのリスト const bookList = [ { id: 1, title: "ファスト&スロー", page: 100, image: "http://books.google.com/books/content?id=tNDza_Pb0UMC&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api" }, { id: 2, title: "ファストフードの恐ろしい話", page: 200, image: "http://books.google.com/books/content?id=ghgzDwAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api" }, { id: 3, title: "英語でおもてなし・ファストフード食べ歩き", page: 300, image: "http://books.google.com/books/content?id=HC-gCAAAQBAJ&printsec=frontcover&img=1&zoom=5&edge=curl&source=gbs_api" }, ] export default function Index(props) { const classes = useStyles(); const compProps = { gridItem: { item : true, space : 5, xs : 12, md : 6, lg : 3, className : classes.gridItem } } return ( <> <Box mx={10} mt={4}> <Box display="flex" alignItems="center"> <h2>{props.title}</h2> </Box> <Grid container spacing={1}> {bookList.map(item => <Grid {...compProps.gridItem} key={item.id}> <Item bookParam={item} /> </Grid> )} </Grid> </Box> </> ); }一覧画面(
index.jsx
)では定義したリストを順番に並べて表示しています。まずは、表示するリストのデータを定義しています。通常はAPIやDBから取得したりしますが、今回は見本なので直接定義しています。
そして、定義したリストデータをループで回して各アイテムを
BookCard
というコンポーネントで表示しています。各データはbookParam
という名前でBookCard
コンポーネントに渡しています。2.アイテム
item.jsximport React, {useState} from 'react'; import Dialog from "@comp/dialog"; import Typography from '@material-ui/core/Typography'; import Box from '@material-ui/core/Box'; import Link from '@material-ui/core/Link'; const item = (props) => { /** 画面パラメータ */ const [dialogOpenF, setDialogOpenF] = useState(false); /** * コンポーネントに渡す引数 */ const compProps = { bookShowDialog: { open : dialogOpenF, //ダイアログの表示プローアティ bookParam: props.bookParam, onClose: () => setDialogOpenF(false), //ダイアログ非表示処理 }, showLink: { href: "#", onClick: () => setDialogOpenF(true) //ダイアログ表示処理 } } return ( <Box> {/* 画像の表示 */} <Link {...compProps.showLink}> <img src={props.bookParam.image}/> </Link> {/* タイトルの表示 */} <Typography> <Link {...compProps.showLink}> {props.bookParam.title} </Link> </Typography> {/* ページ数の表示 */} <Typography>{props.bookParam.page}ページ</Typography> {/* ダイアログの表示 */} <Dialog {...compProps.bookShowDialog} /> </Box> ); } export default item;各アイテム(
item.jsx
)では呼び出し元から渡されたデータの表示とダイアログの表示・非表示の処理を行っています。引き渡されたパラメータは
props.bookParam
に格納されているため画像URL(image
)、タイトル(title
)、ページ数(page
)をそのまま出力に利用しています。そして、最後の
Dialog
コンポーネントに各アイテムの情報を渡しています。2-1.ダイアログの表示・非表示
ダイアログ表示・非表示用のパラメータ
dialogOpenF
を用意しています。このパラメータをtrue・falseに更新することでダイアログの表示・非表示に切り替えているわけです。後は画像とタイトルがクリックされたときに
dialogOpenF
を更新するメソッドsetDialogOpenF
を使ってtrueに変更しています。これによりopen
プロパティーが更新されダイアログが表示されます。そして、ダイアログが閉じる処理を行ったときに
setDialogOpenF
でfalseを指定してダイアログを非表示に更新しています。3.ダイアログ
dialog.jsximport React from 'react'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogTitle from '@material-ui/core/DialogTitle'; import PropTypes from 'prop-types'; import Typography from '@material-ui/core/Typography'; const dialog = (props) => { const compProps = { dialog: { open: props.open, //ダイアログの表示・非表示プロパティ onClose: props.onClose, //ダイアログが閉じられた時の処理 scroll:'paper', }, dialogContent: { dividers: true }, dialogContentText: { tabIndex: -1 }, closeButton: { onClick: props.onClose, //閉じるボタンが押されたときの処理 color: "primary" }, } return ( <Dialog {...compProps.dialog}> {/* タイトル */} <DialogTitle>{props.bookParam.title}</DialogTitle> {/* 画像とページ数 */} <DialogContent {...compProps.dialogContent}> <img src={props.bookParam.image} /> <DialogContentText {...compProps.dialogContentText}> <Typography>{props.bookParam.page}ページ</Typography> </DialogContentText> </DialogContent> {/* 閉じるボタン */} <DialogActions> <Button {...compProps.closeButton}> 閉じる </Button> </DialogActions> </Dialog> ); } dialog.propTypes = { open: PropTypes.bool, //表示フラグ onClose: PropTypes.func, //閉じる処理 } export default dialog;最後にダイアログに呼び出し元から渡された情報を出力します。
DialogTitle
にはアイテムのタイトル。DialogContent
にはアイテムの画像とページ数を表示。そして、ダイアログを閉じる処理を行う「閉じるボタン」を定義します。閉じるボタンが押されたときは呼び出し元から引き渡された
props.onClose
を利用します。これにより、呼び出し元からダイアログを非表示にします。また、
propTypes
で呼び出し元からのパラメータの型をチェックしまています。open
はダイアログの表示・非表示でboolean型。onClose
は閉じる処理でfunc型。これにより異常な値が渡ってこれば警告が表示されるようになっているのです。
- 投稿日:2020-11-26T15:46:45+09:00
【Nuxt.js】Vuex基礎編②stateを複数使ってみよう
? この記事はWP専用です
https://wp.me/pc9NHC-en前置き
今回は前回の基礎編に続き、
stateが複数ある場合の書き方です✍️
基礎編でVuexの基本的な解説はしています!
また基礎編のコードに追記するので
そちらを確認しながらやってみてください?Vuex基礎編はこちら
https://note.com/aliz/n/n497914c981c8やりたいこと
基礎編のカウンターを2つに増やします!
これだけ!!!NGパターン
まずはNGパターンから。
まずはstateにsubCounterを追加。
mutationsなどにも同様に
subCounterについて追記します✍️が!
これだと後述したsubCounterに
全てがまとまってしまいます。。。? 続きはWPでご覧ください?
https://wp.me/pc9NHC-en
- 投稿日:2020-11-26T15:30:51+09:00
nextjs-auth0をdocker上でも使いたい
nextjsでも簡単にauth0を使いたい!
と思ってサンプルに従うと、Dockerビルドってかproduction buildするとエラーで落ちちゃいます。configのサンプル
auth0/nextjs-auth0: Next.js SDK for signing in with Auth0 (Experimental) - https://github.com/auth0/nextjs-auth0#runtime-configurationimport { initAuth0 } from '@auth0/nextjs-auth0'; import config from './config'; export default initAuth0({ domain: process.env.AUTHO_DOMAIN, clientId: process.env.AUTHO_DOMAIN, clientSecret: process.env.APP_SECRRET, scope: 'openid profile',エラー内容こんな感じ。process.env.XXXが見れないから落ちてるのかな。
Automatically optimizing pages... > Build error occurred Error: A valid Auth0 Domain must be provided at Object.createInstance [as default] (/usr/src/app/node_modules/@auth0/nextjs-auth0/dist/instance.node.js:10:15) at initAuth0 (/usr/src/app/node_modules/@auth0/nextjs-auth0/dist/index.js:8:46) at Object.xMDF (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:781:127) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at Module.1TCz (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:147:13) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at Object.0 (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:99:18) at __webpack_require__ (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:23:31) at /usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:91:18 at Object.<anonymous> (/usr/src/app/.next/server/static/GDDYxjIWKG2mlWWc4bTrE/pages/_app.js:94:10) error Command failed with exit code 1ボツ案
issue投げて聞いてみたら、
.env
を仮データで追加し実行時に再ビルドすると大丈夫らしい。試したらできた。Can't build on docker. · Issue #86 · auth0/nextjs-auth0 - https://github.com/auth0/nextjs-auth0/issues/86
DockerfileFROM node:14 as builder WORKDIR /app COPY .env.template .env COPY package.json yarn.lock ./ RUN yarn install COPY . . RUN yarn build # ------------------ FROM node:14-alpine as release WORKDIR /app COPY --from=builder /app/package.json /app/yarn.lock ./ RUN yarn install COPY --from=builder /app/.next ./.next COPY . . EXPOSE 3000 CMD ["yarn", "start:build"]package.json"scripts": { "start": "next start", "start:build": "yarn build && yarn start", "build": "next build",確かにできるんだけど、CI時にビルド2回ぐらい回しちゃうことになって、めっちゃ時間かかる・・・
改善案
initAuth0のオブジェクトを直接export defaultすると、環境変数がバインドされちゃうので、関数にしてあとからprocess.envを評価すればいいじゃんって案
Document best practices for using nextjs-auth0 with a nextjs production build. · Issue #154 · auth0/nextjs-auth0 - https://github.com/auth0/nextjs-auth0/issues/154
環境変数はgetConfig()経由で受け取り、バインドすると想定通り動いてくれた。やったね!
src/lib/auth0.jsimport getConfig from 'next/config'; import { initAuth0 } from '@auth0/nextjs-auth0'; const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); let auth0 = null; const proc = () => { if (!auth0) { auth0 = initAuth0({ clientId: publicRuntimeConfig.auth0.clientId, domain: publicRuntimeConfig.auth0.domain, scope: 'openid email profile', postLogoutRedirectUri: publicRuntimeConfig.baseUrl, redirectUri: `${publicRuntimeConfig.baseUrl}/api/auth/signed-in`, clientSecret: serverRuntimeConfig?.auth0?.secret ?? '', session: { cookieSecret: serverRuntimeConfig?.appSecret ?? '', // Set to 8 hours cookieLifetime: 60 * 60 * 8, storeIdToken: false, storeAccessToken: false, storeRefreshToken: false, }, oidcClient: { // Optionally configure the timeout in milliseconds for HTTP requests to Auth0. httpTimeout: 2500, // Optionally configure the clock tolerance in milliseconds, if the time on your server is running behind. clockTolerance: 10000, }, }); } return auth0; }; export default proc;next.config.jsconst nextConfig = { serverRuntimeConfig: { appSecret: process.env.APP_SECRET, auth0: { secret: process.env.AUTH0_CLIENT_SECRET, }, }, publicRuntimeConfig: { baseUrl: process.env.NEXT_PUBLIC_BASE_URL, auth0: { clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID, domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN, }, }, }; module.exports = nextConfig;page/auth/api/sign-in.jsdiff --git a/pages/api/auth/sign-in.js b/pages/api/auth/sign-in.js index 7b109a7..d271932 100644 --- a/pages/api/auth/sign-in.js +++ b/pages/api/auth/sign-in.js @@ -1,6 +1,8 @@ -import auth0 from '../../../src/lib/auth0'; +import Auth0 from '../../../src/lib/auth0'; import { checkHeaders } from '../../../src/lib/middleware/auth'; +const auth0 = Auth0(); + const login = checkHeaders(async (req, res) => { try { await auth0.handleLogin(req, res);
- 投稿日:2020-11-26T14:53:53+09:00
React-leafletの使い方メモ
React-leafletとは
Open Source Mapを表示するJavaScriptライブラリであるleafletを、React.js上で使えるように拡張するライブラリです。
React Leaflet公式
https://react-leaflet.js.org/本記事は公式のチュートリアルを順番にやっていくだけですので詳しくは公式サイトを参照してください
https://react-leaflet.js.org/docs/start-introduction1. プロジェクトの作成
terminalmkdir react-leaflet cd react-leaflet npx create-react-app my-app
2. インストール
今回は開発版をインストールしました
開発版をインストールする場合npm install leaflet react-leaflet@next --save
安定版をインストールする場合npm install leaflet react-leaflet --save
3. 地図を表示
こちらのコードを参考にやっていきます
https://qiita.com/sugasaki/items/d225cf548e9a787dbd9c地図を表示する為のクラスを作成
MapContainerで地図オブジェクトをつくり、TileLayerで地図タイルを取得できるようにすると地図が表示されます。
MapContainerがL.map()、TileLayerがL.tileLayer()、MarkerがL.marker()に対応していますので、leaflet触ったことがある方はイメージしやすいと思います。
my-app/component/simple.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' export default class SimpleExample extends Component { render() { const position = [51.505, -0.09]; return ( <MapContainer center={position} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Marker position={position}> <Popup> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </Marker> </MapContainer> ) } }App.jsでSimpleExampleクラスを呼ぶように変更
my-app/App.jsimport Leaflet from 'leaflet' import React, { Component } from 'react'; import './App.css'; import 'leaflet/dist/leaflet.css'; import SimpleExample from './components/simple' Leaflet.Icon.Default.imagePath = '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/images/' class App extends Component { render() { return ( <SimpleExample /> ); } } export default App;leafletコンテナの表示幅を指定
指定し忘れると高さゼロ(=表示されない)になってしまうので必ず設定します
my-app/index.js.leaflet-container { width: 600px; height: 300px; margin: 10px;サーバーを起動して確認
サーバーを起動します
terminalnpm start
以上でhttp://localhost:3000/ に地図が表示される筈です
4. イベント管理
useMapEvents()によりleafletのイベントハンドラを使うことができます。以下ではclick()イベントが発生したら端末の現在位置を取得してMarkerを置くという動作を追加しています。
my-app/components/event.jsimport React, { Component, useState } from 'react' import { MapContainer, TileLayer, useMapEvents, Marker, Popup } from 'react-leaflet' function LocationMarker() { const [position, setPosition] = useState(null) const map = useMapEvents({ click() { map.locate() }, locationfound(e) { setPosition(e.latlng) map.flyTo(e.latlng, map.getZoom()) }, }) return position === null ? null : ( <Marker position={position}> <Popup>You are here</Popup> </Marker> ) } export default class EventsExample extends Component { render() { const position = [51.505, -0.09]; return ( <MapContainer center={position} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <LocationMarker /> </MapContainer> ) } }my-app/App.jsimport Leaflet from 'leaflet' import React, { Component } from 'react'; import './App.css'; import 'leaflet/dist/leaflet.css'; import SimpleExample from './components/simple' import EventsExample from './components/events' Leaflet.Icon.Default.imagePath = '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/images/' class App extends Component { render() { return ( <div> SimpleExample <SimpleExample /> EventsExample <EventsExample /> </div> ); } } export default App;5. ポリゴンを表示
ポリゴンも簡単に追加できます
my-app/src/components/polygon.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, Popup, Circle, CircleMarker, Polyline, Polygon, Rectangle } from 'react-leaflet' export default class PolygonExample extends Component { render() { const center = [51.505, -0.09]; const polyline = [ [51.505, -0.09], [51.51, -0.1], [51.51, -0.12], ]; const multiPolyline = [ [ [51.5, -0.1], [51.5, -0.12], [51.52, -0.12], ], [ [51.5, -0.05], [51.5, -0.06], [51.52, -0.06], ], ]; const polygon = [ [51.515, -0.09], [51.52, -0.1], [51.52, -0.12], ]; const multiPolygon = [ [ [51.51, -0.12], [51.51, -0.13], [51.53, -0.13], ], [ [51.51, -0.05], [51.51, -0.07], [51.53, -0.07], ], ]; const rectangle = [ [51.49, -0.08], [51.5, -0.06], ]; const fillBlueOptions = { fillColor: 'blue' }; const blackOptions = { color: 'black' }; const limeOptions = { color: 'lime' }; const purpleOptions = { color: 'purple' }; const redOptions = { color: 'red' }; return ( <MapContainer center={center} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <Circle center={center} pathOptions={fillBlueOptions} radius={200} /> <CircleMarker center={[51.51, -0.12]} pathOptions={redOptions} radius={20}> <Popup>Popup in CircleMarker</Popup> </CircleMarker> <Polyline pathOptions={limeOptions} positions={polyline} /> <Polyline pathOptions={limeOptions} positions={multiPolyline} /> <Polygon pathOptions={purpleOptions} positions={polygon} /> <Polygon pathOptions={purpleOptions} positions={multiPolygon} /> <Rectangle bounds={rectangle} pathOptions={blackOptions} /> </MapContainer> ) } }6. SVG overlay
SVGを上に重ねることもできます
my-app/src/components/svg.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, SVGOverlay } from 'react-leaflet' export default class SVGExample extends Component { render() { const position = [51.505, -0.09]; const bounds = [ [51.49, -0.08], [51.5, -0.06], ]; return ( <MapContainer center={position} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <SVGOverlay attributes={{ stroke: 'red' }} bounds={bounds}> <rect x="0" y="0" width="100%" height="100%" fill="blue" /> <circle r="5" cx="10" cy="10" fill="red" /> <text x="50%" y="50%" stroke="white"> text </text> </SVGOverlay> </MapContainer> ) } }7. LayerGroupとFeatureGroup
leafletにあるLayerGroupとかFeatureGroupも使えます
my-app/src/components/group.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, Popup, LayerGroup, Circle, FeatureGroup, Rectangle } from 'react-leaflet' export default class LayerGroupExample extends Component { render() { const center = [51.505, -0.09]; const rectangle = [ [51.49, -0.08], [51.5, -0.06], ]; const fillBlueOptions = { fillColor: 'blue' }; const fillRedOptions = { fillColor: 'red' }; const greenOptions = { color: 'green', fillColor: 'green' }; const purpleOptions = { color: 'purple' }; return ( <MapContainer center={center} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <LayerGroup> <Circle center={center} pathOptions={fillBlueOptions} radius={200} /> <Circle center={center} pathOptions={fillRedOptions} radius={100} stroke={false} /> <LayerGroup> <Circle center={[51.51, -0.08]} pathOptions={greenOptions} radius={100} /> </LayerGroup> </LayerGroup> <FeatureGroup pathOptions={purpleOptions}> <Popup>Popup in FeatureGroup</Popup> <Circle center={[51.51, -0.06]} radius={200} /> <Rectangle bounds={rectangle} /> </FeatureGroup> </MapContainer> ) } }8. マウスオーバーで注釈を出す
MarkerやCircleにTooltipを追加しておくと注釈を出すことが出来ます
my-app/src/components/tooltips.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, Marker, Popup, Circle, CircleMarker, Polygon, Rectangle, Tooltip } from 'react-leaflet' import { useState, useMemo } from 'react' const center = [51.505, -0.09] const multiPolygon = [ [ [51.51, -0.12], [51.51, -0.13], [51.53, -0.13], ], [ [51.51, -0.05], [51.51, -0.07], [51.53, -0.07], ], ]; const rectangle = [ [51.49, -0.08], [51.5, -0.06], ]; function TooltipCircle() { const [clickedCount, setClickedCount] = useState(0) const eventHandlers = useMemo( () => ({ click() { setClickedCount((count) => count + 1) }, }), [], ) const clickedText = clickedCount === 0 ? 'Click this Circle to change the Tooltip text' : `Circle click: ${clickedCount}` return ( <Circle center={center} eventHandlers={eventHandlers} pathOptions={{ fillColor: 'blue' }} radius={200}> <Tooltip>{clickedText}</Tooltip> </Circle> ) } export default class ToolTipsExample extends Component { render() { return ( <MapContainer center={center} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <TooltipCircle /> <CircleMarker center={[51.51, -0.12]} pathOptions={{ color: 'red' }} radius={20}> <Tooltip>Tooltip for CircleMarker</Tooltip> </CircleMarker> <Marker position={[51.51, -0.09]}> <Popup>Popup for Marker</Popup> <Tooltip>Tooltip for Marker</Tooltip> </Marker> <Polygon pathOptions={{ color: 'purple' }} positions={multiPolygon}> <Tooltip sticky>sticky Tooltip for Polygon</Tooltip> </Polygon> <Rectangle bounds={rectangle} pathOptions={{ color: 'black' }}> <Tooltip direction="bottom" offset={[0, 20]} opacity={1} permanent> permanent Tooltip for Rectangle </Tooltip> </Rectangle> </MapContainer> ) } }9. LayersControl
レイヤーを選んで表示するようにすることもできます
my-app/src/components/leyersControl.jsimport React, { Component } from 'react' import { MapContainer, TileLayer, Marker, Popup, LayersControl, LayerGroup, Circle, FeatureGroup, Rectangle } from 'react-leaflet' export default class LayersControlExample extends Component { render() { const center = [51.505, -0.09]; const rectangle = [ [51.49, -0.08], [51.5, -0.06], ]; return ( <MapContainer center={center} zoom={13} scrollWheelZoom={false}> <LayersControl position="topright"> <LayersControl.BaseLayer checked name="OpenStreetMap.Mapnik"> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> </LayersControl.BaseLayer> <LayersControl.BaseLayer name="OpenStreetMap.BlackAndWhite"> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png" /> </LayersControl.BaseLayer> <LayersControl.Overlay name="Marker with popup"> <Marker position={center}> <Popup> A pretty CSS3 popup. <br /> Easily customizable. </Popup> </Marker> </LayersControl.Overlay> <LayersControl.Overlay checked name="Layer group with circles"> <LayerGroup> <Circle center={center} pathOptions={{ fillColor: 'blue' }} radius={200} /> <Circle center={center} pathOptions={{ fillColor: 'red' }} radius={100} stroke={false} /> <LayerGroup> <Circle center={[51.51, -0.08]} pathOptions={{ color: 'green', fillColor: 'green' }} radius={100} /> </LayerGroup> </LayerGroup> </LayersControl.Overlay> <LayersControl.Overlay name="Feature group"> <FeatureGroup pathOptions={{ color: 'purple' }}> <Popup>Popup in FeatureGroup</Popup> <Circle center={[51.51, -0.06]} radius={200} /> <Rectangle bounds={rectangle} /> </FeatureGroup> </LayersControl.Overlay> </LayersControl> </MapContainer> ) } }10. stateの値でRectangleの色を変える
この辺からleaflet単独ではなくReactからleafletを操作する手順になります。ここではEffectフックでRectangleの色を変えています。
my-app/src/components/panes.jsimport React, { Component, useState, useRef, useEffect } from 'react' import { MapContainer, TileLayer, Pane, Rectangle } from 'react-leaflet' const outer = [ [50.505, -29.09], [52.505, 29.09], ] const inner = [ [49.505, -2.09], [53.505, 2.09], ] function BlinkingPane() { const [render, setRender] = useState(true) const timerRef = useRef() useEffect(() => { timerRef.current = setInterval(() => { setRender((r) => !r) }, 1000) return () => { clearInterval(timerRef.current) } }, []) return render ? ( <Pane name="cyan-rectangle" style={{ zIndex: 500 }}> <Rectangle bounds={outer} pathOptions={{ color: 'cyan' }} /> </Pane> ) : null } export default class PanesExample extends Component { render() { return ( <MapContainer bounds={outer} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <BlinkingPane /> <Pane name="yellow-rectangle" style={{ zIndex: 499 }}> <Rectangle bounds={inner} pathOptions={{ color: 'yellow' }} /> <Pane name="purple-rectangle"> <Rectangle bounds={outer} pathOptions={{ color: 'purple' }} /> </Pane> </Pane> </MapContainer> ) } }横に長いRectangleが紫⇔青と交互に色を変えながら表示されます
11. Drag可能なMarker
MarkerをDraggableにするかどうかをReact stateにしておいて、途中でDrag可能にしたりDragできなくしたりする例です
my-app/src/components/draggable.jsimport React, { Component, useState, useRef, useMemo, useCallback } from 'react' import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' const center = { lat: 51.505, lng: -0.09, } function DraggableMarker() { const [draggable, setDraggable] = useState(false) const [position, setPosition] = useState(center) const markerRef = useRef(null) const eventHandlers = useMemo( () => ({ dragend() { const marker = markerRef.current if (marker != null) { setPosition(marker.getLatLng()) } }, }), [], ) const toggleDraggable = useCallback(() => { setDraggable((d) => !d) }, []) return ( <Marker draggable={draggable} eventHandlers={eventHandlers} position={position} ref={markerRef}> <Popup minWidth={90}> <span onClick={toggleDraggable}> {draggable ? 'Marker is draggable' : 'Click here to make marker draggable'} </span> </Popup> </Marker> ) } export default class DraggableMarkerExample extends Component { render() { return ( <MapContainer center={center} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <DraggableMarker /> </MapContainer> ) } }12. View Bounds
innerBoundsまたはouterBoundsの内側をクリックすると、クリックした側をinnerHandler、クリックしていない側をouterHandlerに登録して、その範囲が大きく表示されるように
my-app/src/components/viewBounds.jsimport React, { Component, useState, useMemo } from 'react' import { MapContainer, TileLayer, Rectangle, useMap } from 'react-leaflet' const innerBounds = [ [49.505, -2.09], [53.505, 2.09], ] const outerBounds = [ [50.505, -29.09], [52.505, 29.09], ] const redColor = { color: 'red' } const whiteColor = { color: 'white' } function SetBoundsRectangles() { const [bounds, setBounds] = useState(outerBounds) const map = useMap() const innerHandlers = useMemo( () => ({ click() { setBounds(innerBounds) map.fitBounds(innerBounds) }, }), [map], ) const outerHandlers = useMemo( () => ({ click() { setBounds(outerBounds) map.fitBounds(outerBounds) }, }), [map], ) return ( <> <Rectangle bounds={outerBounds} eventHandlers={outerHandlers} pathOptions={bounds === outerBounds ? redColor : whiteColor} /> <Rectangle bounds={innerBounds} eventHandlers={innerHandlers} pathOptions={bounds === innerBounds ? redColor : whiteColor} /> </> ) } export default class ViewBoundsExample extends Component { render() { return ( <MapContainer bounds={outerBounds} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <SetBoundsRectangles /> </MapContainer> ) } }13. Animated Panning
checkboxの値でsetViewのanimateを有効にしたり無効にしたりできます
my-app/src/components/animatedPanning.jsimport React, { Component, useRef } from 'react' import { MapContainer, TileLayer, useMapEvent } from 'react-leaflet' function SetViewOnClick({ animateRef }) { const map = useMapEvent('click', (e) => { map.setView(e.latlng, map.getZoom(), { animate: animateRef.current || false, }) }) return null } function AnimateExample() { const animateRef = useRef(false) return ( <> <p> <label> <input type="checkbox" onChange={() => { animateRef.current = !animateRef.current }} /> Animate panning </label> </p> <MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> <SetViewOnClick animateRef={animateRef} /> </MapContainer> </> ) } export default class AnimatedPanningExample extends Component { render() { return ( <AnimateExample /> ) } }14. External State
逆にleafletの情報をコンポーネント外で使うことも出来ます
my-app/src/components/import React, {useState, useCallback, useEffect, useMemo} from 'react' import { MapContainer, TileLayer } from 'react-leaflet' const center = [51.505, -0.09] const zoom = 13 function DisplayPosition({ map }) { const [position, setPosition] = useState(map.getCenter()) const onClick = useCallback(() => { map.setView(center, zoom) }, [map]) const onMove = useCallback(() => { setPosition(map.getCenter()) }, [map]) useEffect(() => { map.on('move', onMove) return () => { map.off('move', onMove) } }, [map, onMove]) return ( <p> latitude: {position.lat.toFixed(4)}, longitude: {position.lng.toFixed(4)}{' '} <button onClick={onClick}>reset</button> </p> ) } function ExternalStateExample() { const [map, setMap] = useState(null) const displayMap = useMemo( () => ( <MapContainer center={center} zoom={zoom} scrollWheelZoom={false} whenCreated={setMap}> <TileLayer attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" /> </MapContainer> ), [], ) return ( <div> {map ? <DisplayPosition map={map} /> : null} {displayMap} </div> ) } export default ExternalStateExample;まとめ
Reactを使う環境でleafletを使うなら単独で使うよりこちらを使った方が良さげです
- 投稿日:2020-11-26T14:42:35+09:00
mongoose の model と schema に TypeScript で型をつける
Migration mongoose models and schemas from JavaScript to TypeScript
今年は自宅にこもりがちになって腰椎椎間板ヘルニアと坐骨神経痛のコンボを決めた @algas です。
この記事は TypeScript Advent Calendar 2020 の1日目の記事として作成しました。
記事に登場するコードは github リポジトリで公開しています。
https://github.com/algas/typed-mongoose-example概要
mongoose という mongoDB の Node.js では有名なライブラリを使うアプリケーションコードを JavaScript から TypeScript に移行する作業を行いました。
JavaScript で定義済みの mongoose のモデルとスキーマに「正しく」型をつけることができたのでそのノウハウを共有します。
基本的には @types/mongoose を使って型をつけています。
スキーマの型定義の一部が不十分だったので独自の定義をして補いました。対象読者
この記事は次のような読者を対象に想定しています。
- mongoose または mongoDB を使っている
- TypeScript を書いたことがある
mongoDB や mongoose, TypeScript の詳しい説明はしません。
背景
この記事を書くに至った背景を説明します。
なぜ mongoose のコードに型をつけると良いのか
わざわざ書くまでもないとは思いますが、コードを静的型付きにすることでコンパイル時に不具合を見つけやすくなったり開発環境によるコード記述の補完を得られるようになります。適切に型をつけることができればスキーマやモデルに対して定義したフィールドやメソッドとして何が含まれているかやその型の情報を使って効率よく安心して開発をすることができます。
mongoose が TypeScript ネイティブではない
mongoose は JavaScript で古くから開発され続けているライブラリで、そのコードは TypeScript に対応していません。
@types/mongoose
で型情報を別途付与することはできますが、TypeScript のアプリケーションから呼び出すのに便利な実装になっているとは言えません。mongoose のスキーマやモデルに型をつけるのは難しい
mongoose スキーマやモデルに後から TypeScript で型をつけるのには工夫が必要です。mongoose の Collection スキーマには単純にデータベースに値を保持するフィールドだけではなく Virtual Property, Instance Method, Static Method, Plugin などの機能があります。これらに対応するモデルに型をつけ、さらにスキーマ自体にフィールドの型を付与するのが本記事の試みです。
初めから TypeScript で書く場合やこれから新しく mongoose スキーマを定義する場合には別のライブラリを使うなどの手法をオススメします。
実装例
具体的に mongoose で定義したスキーマとモデルに TypeScript で型をつけてみます。
https://github.com/algas/typed-mongoose-example/blob/main/src/model.ts執筆時点では次のバージョンのライブラリにそれぞれ対応しています。
typescript
: 3.xmongoose
: 5.x@types/mongoose
: 5.xmongoose の基本的な使い方は公式ドキュメントに書いてあります。
https://mongoosejs.com/docs/index.html
これを知っている前提で話を進めます。Schema
mongoDB の Collection に入れるデータのスキーマを定義します。
User
という Collection を作ることにします。
Instance method としてfoo
という関数を追加しています。import { Document, model, Model, Schema, Types } from 'mongoose'; interface UserSchemaFields { email: string; firstName: string; lastName: string; age?: number; friends?: Types.ObjectId[]; } const userSchemaFields: SchemaDefinition<UserSchemaFields> = { email: { type: String, required: true, unique: true }, firstName: { type: String, required: true }, lastName: { type: String, required: true }, age: { type: Number }, friends: [Schema.Types.ObjectId], }; const UserSchema: Schema<UserSchemaProperties> = new Schema(userSchemaFields); // Instance methods interface UserSchemaProperties extends UserSchemaFields { foo: () => void; } UserSchema.methods.foo = function() {};ここではスキーマとそのフィールドの定義に明示的に型を与えているのが一般的な方法との違いです。
Instance method の定義はSchema
だけに与えてSchemaDefinition
には与えません。
独自に定義し直したSchemaDefinition
は次のように書けます。
https://github.com/algas/typed-mongoose-example/blob/main/src/mongoose-util.d.tsimport { Schema, SchemaType, SchemaTypeOpts } from 'mongoose'; type SchemaPropType<T> = T extends string ? StringConstructor : T extends number ? NumberConstructor : T extends boolean ? BooleanConstructor : any; type SchemaTypeOptions<T> = Omit<SchemaTypeOpts<T>, 'type'> & { type?: SchemaPropType<T>; }; export type SchemaDefinition<T = any> = { [K in keyof T]: SchemaTypeOptions<T[K]> | Schema | SchemaType; };SchemaTypeOpts から 'type' の定義を取り除いて使いました。
元の実装は次のコードで定義されています。
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/mongoose/index.d.tsVirtual properties (Document)
mongoose schema には
virtual
という機能を使って仮想的な要素を追加できます。
ここではfullName
を取得する機能を実装します。
UserDocument
にDocument
とUserSchemaProperties
(fields + instance methods) を継承させて virtual propertyfullName
の型を足します。// Virtual properties interface UserDocument extends Document, UserSchemaProperties { fullName: string; } UserSchema.virtual('fullName').get(function () { return [this.firstName, this.lastName].join(' '); });mongoose
Document
の詳細は mongoose の API reference に書かれています。
https://mongoosejs.com/docs/documents.htmlStatics variables and functions
mongoose schema には static の変数や関数を追加できます。
UserModel
に上で定義したUserDocument
を指定したModel<UserDocument>
を継承させて static methodbar
の型を足します。// Static methods interface UserModel extends Model<UserDocument> { bar: () => string; } UserSchema.statics.bar = function(){ return 'bar'; }mongoose
Model
の詳細は mongoose の API reference に書かれています。
https://mongoosejs.com/docs/models.htmlPlugins
他のスキーマで定義された関数などを plugin として呼び出すことができます。
plugin で作った変数や関数の interface は本体とは別に定義して個別に呼び出せるようにすべきです。
たとえばSomePluginSchema
という名前のスキーマで定義されている static 関数を呼び出す場合には次のように書きます。interface SomePluginFunctions { somePluginFunction: () => void; } UserSchema.plugin(SomePluginSchema, {}); interface UserModel extends Model<UserDocument>, SomePluginFunctions { ... }Model
mongoose model を定義します。
引数にはモデルの名前とスキーマを渡します。
型指定にUserDocument
,UserModel
を明示することでUser
モデルの型情報が使えるようになります。export const User = model<UserDocument, UserModel>('User', UserSchema);モデルとスキーマを使ってみる
ここまででスキーマとモデルの定義はおしまいです。
モデルを使うにはインスタンス化する必要があります。const someUserData: UserSchemaFields { ... } const someUser = new User(someUserData);
someUser
はUserDocument
型になります。
User()
の引数に渡すオブジェクトにUserSchemaFields
の型を使えば厳密に定義できます。また
User
データを mongoDB から取得するコードは次のように書きます。User.find(function (err, users) { if (err) return console.error(err); console.log(users); })TypeScript が正しく設定された開発環境を使うと
User
やそのインスタンスにフィールドやメソッドが適切に補完されることを確認できます。まとめ
- 適切に型をつければ TypeScript でも mongoose を使った開発をできる
- mongoose に TypeScript で適切な型をつけるのはちょっと大変
- 新しく TypeScript + mongoDB を使った開発をするのであれば mongoose 以外のライブラリを使うべき
注意点
- 本記事内に登場するコードはMITライセンスで公開しています
https://github.com/algas/typed-mongoose-example- Schema の型定義の変更は
@types/mongoose
にもフィードバックする予定です参考文献
- 投稿日:2020-11-26T14:41:10+09:00
[javascript] switchを用いて3つ以上の条件分岐を行う。
この記事ではrails5.2.3を使用しています
概要
jsで条件分岐する際にswitchの使用方法で引っかかったので備忘録
if else との違いは?
こちらの記事で分かりやすく紹介されています。
https://qiita.com/taai/items/90cd190764f6dfcf6abd解説
switch文の基本書式switch(式){ case 条件1: 条件1に当てはまる場合の処理 break; case 条件2: 条件2に当てはまる場合の処理 break; default どのcaseにも当てはまらない場合: どのcaseにも当てはまらない場合の処理 }switch(式){ ・・・ }ここの式と条件が一致した場合に指定した処理が行われます。
条件は上から順に読み込まれ、最初にヒットした条件の処理が実行されます。例えば
let a = "あ" switch(a){ case "あ": console.log(0) break; case "い": console.log(1) break; case "あ": console.log(2) break; default: console.log(3) } >> 0条件に一致した最初のcaseが実行されました。
3つ目のcaseにも一致する条件がありますが、実行されるのは最初にヒットした1件のみなのでその後の処理は実行されません。様々な条件の指定方法
こんな使い方もできます。
let a = [1, 2, 3, 4, 5] switch(true){ case a.length == 4: console.log(0) break; case a.length == 5: console.log(1) break; default: console.log(2) } >> 1こうすると、細かい条件指定ができます。
参考記事にはさらに応用的な使い方も紹介されているので、確認してみてください。参考
- 投稿日:2020-11-26T14:21:47+09:00
Denoでshebangを使って実行する
JavaScrptでもTypeScriptでも、これでOK!
#!/usr/bin/env -S deno run console.log('hello deno!');
deno run
で実行する仕様なので、
#!/usr/bin/env node
と同じ感覚で
#!/usr/bin/env deno
と書いてもダメなんですねー。error: Found argument './hello.js' which wasn't expected, or isn't valid in this context USAGE: deno [OPTIONS] [SUBCOMMAND] For more information try --help
#!/usr/bin/env deno run
でも叱られます。
シェバンにオプションを渡すときは-S
が必要。/usr/bin/env: ‘deno run’: No such file or directory /usr/bin/env: use -[v]S to pass options in shebang lines参考
https://stackoverflow.com/questions/63043929/how-do-i-invoke-deno-from-a-shell-script
- 投稿日:2020-11-26T14:12:56+09:00
javaScriptのオブジェクトからkeyを取り出す方法
- 投稿日:2020-11-26T14:11:14+09:00
[Node.js][Deno] クラス定義各種 ベンチマーク比較
JavaScriptのクラス定義手法はいっぱいあって、これを解説している記事もいっぱいありますが、パフォーマンスに言及しているところがなかったので、自前で実験してみた。
実験環境
- Node.js 14.15.1
- Deno 1.5.4
なお、webクライアントでの実行コストはあまり気にしてないので、各種ブラウザでの比較はパス。
実験設定
- 継承のあるクラスで
- インスタンス生成コストだけでなく、親メンバへのアクセスコストも重要
- とにかくランタイムで高速なものを追求
- プロトタイプ定義コストは、あまり気にしてない
- 標準的なクラス定義方法との互換性も、あまり気にしてない
- 各メンバの読み書きができれば充分
- ただしもちろん、書き込みによってプロトタイプを破壊しないことは重要
そんなわけで、各手法それぞれ
- TestClassA 定義
- プロパティ a とメソッド getA() を含める
- TestClassB 定義
- TestClassA を継承
- プロパティ b~i を含める
- TestClassC 定義
- TestClassB を継承
- プロパティ j~z を含める (このぐらい余計に定義入れとけばプロトタイプへのアクセスコストに差が出てくること期待)
- TestClassC で最初のインスタンス生成
- ついでに正常にアクセスできるかテスト
- 実行時間計測
- 最初のインスタンスでget (プロパティ a 読み出し)
- 最初のインスタンスでcall (メソッド getA() 呼び出し)
- 新規生成インスタンスでget
- 新規生成インスタンスでcall
- 結果表示
- get call それぞれ
- 新規生成インスタンス版結果 - 最初のインスタンス流用版結果 → インスタンス生成コスト
- 10回実行して平均をとる
な手順で。
実験: 標準的なprototype定義方式
まずは普通に。
bench_class_standard.js// TestClassA 定義 function TestClassA(){ this.a=1; }; TestClassA.prototype.getA=function(){return this.a;}; // TestClassB 定義 function TestClassB(){ TestClassA.call(this); this.b=2; this.c=3; this.d=4; this.e=5; this.f=6; this.g=7; this.h=8; this.i=9; } Object.setPrototypeOf(TestClassB.prototype,TestClassA.prototype); // TestClassC 定義 function TestClassC(){ TestClassB.call(this); this.j=10; this.k=11; this.l=12; this.m=13; this.n=14; this.o=15; this.p=16; this.q=17; this.r=18; this.s=19; this.t=20; this.u=21; this.v=22; this.w=23; this.x=24; this.y=25; this.z=26; } Object.setPrototypeOf(TestClassC.prototype,TestClassB.prototype); // 既存インスタンス準備 var t=new TestClassC(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>new TestClassC().a, new_call:()=>new TestClassC().getA(), }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 3.7 427.8 call 7.7 434.7 後発品なのになんでこんな遅いんだよDeno
…というわけで詳細に調べたところ、どうやらコンストラクタで親クラスのcall呼んでるところでやたらと時間かかってる模様。
そんなわけでDeno移行は時期尚早かな…って結論出す前に、まず次いってみましょうか。実験: いまどきのclass方式
ECMAScript 2015以降の追加仕様ですね。
めんどいprototype定義ともおさらば、やったー
…って喜ぶ前に、パフォーマンスをみてみましょう。bench_class_modern.js// TestClassA 定義 class TestClassA{ constructor(){ this.a=1; } getA(){return this.a;} } // TestClassB 定義 class TestClassB extends TestClassA{ constructor(){ super(); this.b=2; this.c=3; this.d=4; this.e=5; this.f=6; this.g=7; this.h=8; this.i=9; } } // TestClassC 定義 class TestClassC extends TestClassB{ constructor(){ super(); this.j=10; this.k=11; this.l=12; this.m=13; this.n=14; this.o=15; this.p=16; this.q=17; this.r=18; this.s=19; this.t=20; this.u=21; this.v=22; this.w=23; this.x=24; this.y=25; this.z=26; } } // 既存インスタンス準備 var t=new TestClassC(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>new TestClassC().a, new_call:()=>new TestClassC().getA(), }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 145.4 91.3 call 139.8 89.3 …遅っ。
だめだこりゃ、次いってみよー実験: コンストラクタをinit()で代用方式
先ほど、親クラスのcallが遅いって書きました。
で、コンストラクタ使わずに自前で初期化メソッド書いちゃえばいんじゃね作戦。bench_class_noctor.js// TestClassA 定義 function TestClassA(){} TestClassA.prototype.init=function(){ this.a=1; }; TestClassA.prototype.getA=function(){return this.a;}; // TestClassB 定義 function TestClassB(){} Object.setPrototypeOf(TestClassB.prototype,TestClassA.prototype); TestClassB.prototype.init=function(){ Object.getPrototypeOf(this).init(); this.b=2; this.c=3; this.d=4; this.e=5; this.f=6; this.g=7; this.h=8; this.i=9; }; // TestClassC 定義 function TestClassC(){} Object.setPrototypeOf(TestClassC.prototype,TestClassB.prototype); TestClassC.prototype.init=function(){ Object.getPrototypeOf(this).init(); this.j=10; this.k=11; this.l=12; this.m=13; this.n=14; this.o=15; this.p=16; this.q=17; this.r=18; this.s=19; this.t=20; this.u=21; this.v=22; this.w=23; this.x=24; this.y=25; this.z=26; }; // 既存インスタンス準備 var t=new TestClassC(); t.init(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>{var u=new TestClassC(); u.init(); u.a}, new_call:()=>{var u=new TestClassC(); u.init(); u.getA()}, }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 1857.6 1949.5 call 1848.6 1949.3 OMG.
getPrototypeOf() さらに重かった。
しかもこれ、 console.log(t) でTestClassCのぶんしか出ないですよ。
console.log(t.getA()) はちゃんと1って出てるので、継承は正常っぽい。実験: 親クラスのメソッド転記方式
では、 getPrototypeOf() にも頼らず、親クラスのメソッドを自前転記作戦。
bench_class_selflink.js// TestClassA 定義 function TestClassA(){} TestClassA.prototype.init=function(){ this.a=1; }; TestClassA.prototype.getA=function(){return this.a;}; // TestClassB 定義 function TestClassB(){} Object.setPrototypeOf(TestClassB.prototype,TestClassA.prototype); TestClassB.prototype.init_TestClassA=TestClassA.prototype.init; TestClassB.prototype.init=function(){ this.init_TestClassA(); this.b=2; this.c=3; this.d=4; this.e=5; this.f=6; this.g=7; this.h=8; this.i=9; }; // TestClassC 定義 function TestClassC(){} Object.setPrototypeOf(TestClassC.prototype,TestClassB.prototype); TestClassC.prototype.init_TestClassB=TestClassB.prototype.init; TestClassC.prototype.init=function(){ this.init_TestClassB(); this.j=10; this.k=11; this.l=12; this.m=13; this.n=14; this.o=15; this.p=16; this.q=17; this.r=18; this.s=19; this.t=20; this.u=21; this.v=22; this.w=23; this.x=24; this.y=25; this.z=26; }; // 既存インスタンス準備 var t=new TestClassC(); t.init(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>{var u=new TestClassC(); u.init(); u.a}, new_call:()=>{var u=new TestClassC(); u.init(); u.getA()}, }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 5.6 6.9 call 9.5 1.4 Denoで見違える結果になりました。
で、getよりcallの方が速い謎が増えた。
あと、 console.log(t) は一通り出てきてます。実験: メソッドをアロー演算子で簡略表記方式
prototypeでアロー演算子使ってもうまくいかないんだよねぃ。
というわけで、 getA() の定義を init() 内に移してみた。bench_class_lambda.js// TestClassA 定義 function TestClassA(){} TestClassA.prototype.init=function(){ this.a=1; this.getA=()=>this.a; }; // TestClassB 定義 function TestClassB(){} Object.setPrototypeOf(TestClassB.prototype,TestClassA.prototype); TestClassB.prototype.init_TestClassA=TestClassA.prototype.init; TestClassB.prototype.init=function(){ this.init_TestClassA(); this.b=2; this.c=3; this.d=4; this.e=5; this.f=6; this.g=7; this.h=8; this.i=9; }; // TestClassC 定義 function TestClassC(){} Object.setPrototypeOf(TestClassC.prototype,TestClassB.prototype); TestClassC.prototype.init_TestClassB=TestClassB.prototype.init; TestClassC.prototype.init=function(){ this.init_TestClassB(); this.j=10; this.k=11; this.l=12; this.m=13; this.n=14; this.o=15; this.p=16; this.q=17; this.r=18; this.s=19; this.t=20; this.u=21; this.v=22; this.w=23; this.x=24; this.y=25; this.z=26; }; // 既存インスタンス準備 var t=new TestClassC(); t.init(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>{var u=new TestClassC(); u.init(); u.a}, new_call:()=>{var u=new TestClassC(); u.init(); u.getA()}, }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 10.8 12.9 call 712.6 556.8 init() の度に再定義しているわけだから重いのは必然だし、
console.log(t) で getA まで出てくる代物なのですが、
定義するだけで呼ばないならさほど大きなコストではないらしい。実験: proto詰め込み方式
console.log(t) を気にしないでいいなら、こんな手法もあり。
プロパティの初期値もprototypeにぶっ込んでしまえば、そのぶんインスタンス生成コストも軽くなるわけで。bench_class_fullproto.js// TestClassA 定義 function TestClassA(){}; TestClassA.prototype.a=1; TestClassA.prototype.getA=function (){return this.a;} // TestClassB 定義 function TestClassB(){} Object.setPrototypeOf(TestClassB.prototype,TestClassA.prototype); TestClassB.prototype.b=2; TestClassB.prototype.c=3; TestClassB.prototype.d=4; TestClassB.prototype.e=5; TestClassB.prototype.f=6; TestClassB.prototype.g=7; TestClassB.prototype.h=8; TestClassB.prototype.i=9; // TestClassC 定義 function TestClassC(){} Object.setPrototypeOf(TestClassC.prototype,TestClassB.prototype); TestClassC.prototype.j=10; TestClassC.prototype.k=11; TestClassC.prototype.l=12; TestClassC.prototype.m=13; TestClassC.prototype.n=14; TestClassC.prototype.o=15; TestClassC.prototype.p=16; TestClassC.prototype.q=17; TestClassC.prototype.r=18; TestClassC.prototype.s=19; TestClassC.prototype.t=20; TestClassC.prototype.u=21; TestClassC.prototype.v=22; TestClassC.prototype.w=23; TestClassC.prototype.x=24; TestClassC.prototype.y=25; TestClassC.prototype.z=26; // 既存インスタンス準備 var t=new TestClassC(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>new TestClassC().a, new_call:()=>new TestClassC().getA(), }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get -6 7.2 call 1.5 3.5 Denoの結果がいまいち。
どうもprototypeが重い御様子。
てゆかNode.js、マイナスってなんだよおい。実験: Object.create() 方式
prototypeが重いなら、 Object.create() ならどうだ。
bench_class_create.js// TestClassA 定義 var TestClassA={a:1}; TestClassA.getA=function(){return this.a;}; // TestClassB 定義 var TestClassB=Object.create(TestClassA); TestClassB.b=2; TestClassB.c=3; TestClassB.d=4; TestClassB.e=5; TestClassB.f=6; TestClassB.g=7; TestClassB.h=8; TestClassB.i=9; // TestClassC 定義 var TestClassC=Object.create(TestClassB); TestClassC.j=10; TestClassC.k=11; TestClassC.l=12; TestClassC.m=13; TestClassC.n=14; TestClassC.o=15; TestClassC.p=16; TestClassC.q=17; TestClassC.r=18; TestClassC.s=19; TestClassC.t=20; TestClassC.u=21; TestClassC.v=22; TestClassC.w=23; TestClassC.x=24; TestClassC.y=25; TestClassC.z=26; // 既存インスタンス準備 var t=Object.create(TestClassC); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>Object.create(TestClassC).a, new_call:()=>Object.create(TestClassC).getA(), }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get -2.8 74 call 2.7 74.9 没。
実験: 空オブジェクトに詰め込み方式
おまけ。
既にprototype以外でfunction定義するとすごい重いって結論出ちゃったからあまり意味なくなっちゃったんだけど…bench_class_pureobj.js// TestClassA 定義 function TestClassA(){ var t={a:1}; t.getA=()=>t.a; return t; } // TestClassB 定義 function TestClassB(){ var t=TestClassA(); t.b=2; t.c=3; t.d=4; t.e=5; t.f=6; t.g=7; t.h=8; t.i=9; return t; } // TestClassC 定義 function TestClassC(){ var t=TestClassB(); t.j=10; t.k=11; t.l=12; t.m=13; t.n=14; t.o=15; t.p=16; t.q=17; t.r=18; t.s=19; t.t=20; t.u=21; t.v=22; t.w=23; t.x=24; t.y=25; t.z=26; return t; } // 既存インスタンス準備 var t=TestClassC(); //console.log(t); //console.log(t.getA()); // 実行内容 var funx={ dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー get:()=>t.a, call:()=>t.getA(), new_get:()=>TestClassC().a, new_call:()=>TestClassC().getA(), }; // 計測結果を書き込むところ var rec={}; // 繰り返し実行時間計測 var loop=Array(10000000); for(var k in funx){ var f=funx[k]; var a=0; var bgn=new Date; for(var i of loop)a+=f(); var end=new Date; rec[k]=end-bgn; } // インスタンス生成時間を抽出表示 console.log(rec.new_get-rec.get); console.log(rec.new_call-rec.call);
Node.js Deno get 7.4 8.4 call 1173.1 803.3 ということで。
因みに、プロパティ定義で横着して
Object.assign(TestClassA(),{b:2,...})
みたいな書き方すると、さらに悲惨な結果になります。
JavaScriptでのオブジェクト合成って、 何やっても重い ので困ったもんだ。結論
邪道実装でいい案件なら、proto詰め込み方式を推し進めたいところ。
あと、やはりDenoはパフォーマンス改善まで様子見。
- 投稿日:2020-11-26T13:34:19+09:00
リンクを別タブで開かせる際、target="_blank"だけだと危険らしい
はじめに
webアプリケーション作成時に、リンクを別タブで開かせたいと思ってググってみたところ、
target="_blank"
だけの記述では危険だという記事がヒットした。リンクを別タブで開かせる基本的な記述
<a href="リンク先" target="_blank">別タブで開く</a>aタグでリンクを別タブで開かせたいときの基本的な記述。これだけでリンクを別タブで開かせることができる。しかしこれだけだと危険性があるらしい。
セキュリティ面とパフォーマンス面で問題がある
target="_blank"
だけの記述では、セキュリティ面とパフォーマンス面で問題があると、googleのデベロッパーサイトで指摘されている。具体的な問題内容と対策についてまとめてくれている日本語記事があったのでこちらも参考に
対策方法
上のリンク先でも紹介されているが、
target="_blank"
と併用してrel="noopener"
またはrel="noreferrer"
を記述することが推奨されている。<!-- 記述例 --> <a href="リンク先" target="_blank" rel="noopener">別タブで開く</a>
rel="noopener"
、rel="noreferrer"
はどちらもリンク先からリンク元が参照できなくなるようにする。
これでリンク先からリンク元情報を抜き取られたり操作されなくなる。さいごに
今回は個人的なwebアプリケーションの中で別タブリンクを貼りたかったので大きく影響のある話ではなかったが、これからwebアプリを開発していくにあたって頭に入れておいたほうが良さそうな内容だと思った。
- 投稿日:2020-11-26T11:52:03+09:00
JavaScript勉強日記#3
①繰り返し処理
<script> //console.log(1 + '枚'); //console.log(2 + '枚'); //console.log(3 + '枚'); //console.log(4 + '枚'); //console.log(5 + '枚'); //console.log(6 + '枚'); //console.log(7 + '枚'); //console.log(8 + '枚'); //console.log(9 + '枚'); //console.log(10 + '枚');素直に書く事もできるが記述が多くなってかなり面倒...
'use strict'; let i =1; while(i <= 10) { console.log(i + '枚'); i += 1; } </script>while文を使う事で同じ記述を使わない様にできます。
②何回繰り返すかを決めずに繰り返し処理をする
<script> 'use strict'; let enemy = 100 let count = 0; window.alert('戦闘開始!'); while(enemy > 0) { const attack = Math.floor(Math.random() * 30) + 1; console.log('怪物に' + attack + 'の損傷!'); enemy = enemy - attack; count += 1 } console.log(count + '回怪物討伐!') </script>③無限ループの抜け方
私は②のコードを書く時にenemy = enemy = attack;
と書いてしまい無限ループに陥りましたので、ついでにループの抜け方(強制終了)も記載しておきます。
Macの場合はcommand + option + esc
でウィンドウを開き、WindowsはCtrl + Alt + Delete
でタスクマネージャーを開きます。https://gyazo.com/38bd87ffea1c2a4d5b83583dc3f6365a
④functionの使い方
https://gyazo.com/1094994e4e432944c458459049c305b7<section> <p id="output1"></p> <p id="output2"></p> <p id="output3"></p> </section> </main> <script> 'use strict'; function total(price) { const tax = 0.1; return price + price * tax; } console.log('コーヒーメーカーの値段は' + total(8000) + '円です。'); document.getElementById('output1').textContent = 'コーヒーメーカーの値段は' + total(8000) + '円(税込)です。' document.getElementById('output2').textContent = 'コーヒー豆の値段は' + total(900) + '円(税込)です。' document.getElementById('output3').textContent = 'コーヒーフィルターの値段は' + total(250) + '円(税込)です。' </script>
function total(price) { 加工の内容の記述(priceはこの中だけで使えます) }
今回は、totalと言う名前のファンクションを使用しました。この記述以降total(price)の記述を書くと何度でも呼び出す事ができます。
functionは資源です。
- 投稿日:2020-11-26T09:46:14+09:00
【JavaScript】for文の入れ子を理解したい!
プログラミング勉強日記
2020年11月26日
for文の入れ子の処理について学習したので、記録します。
間違えている自信しかないので、何かあればコメントください?for文とは
指定された回数だけ繰り返し処理を行う命令です。
(for-Javascript | MDN)書き方
for文.for(初期化式; ループ継続条件式; 増減式) { ループ内で実行する命令 }処理の流れ
1.初期化処理
2.条件判定
3.判定がtrueの場合、ループ内の処理実行、falseの場合、ループの終了
4.増減処理
5.3の判定がfalseになるまで繰り返し
(例) 変数iが10未満の間、コンソールに数値を出力
script.jsfor (let i =0; i < 10; i ++) { console.log(i); }for文の入れ子
for文の中にfor文を書くことが可能です。
例
※わかりやすい例文が思いつかなかったので、許してください
script.jsfor (let i =0; i < 2; i ++) { console.log('外側のループです'); for(let n =0; n < 2 ; n ++) { console.log('内側のループです'); } }処理の流れ
1.変数iに0を代入
2.条件判定( i < 2 )
3.コンソールに
外側のループです
と文字列を出力する4.内側のループに入る
5.変数nに0を代入
6.条件判定( n < 2 )
7.コンソールに
内側のループです
と文字列を出力する8.変数nに1を加算する( n ++ )
9.内側のループが終了すると、外側のループの変数iに1を加算する( i ++ )
10.外側のループが終わるまで繰り返す
結果
- 投稿日:2020-11-26T02:34:53+09:00
title: <input type="file">を初期化する方法
- 投稿日:2020-11-26T02:34:53+09:00
<input type="file">を初期化する方法
- 投稿日:2020-11-26T01:19:59+09:00
静的サイトジェネレーター Gatsby.js 入門
Gatsby.js入門
第1回は、Gatsby.jsのインストールの仕方について書いていこうと思います
Gatsby.jsを利用するための準備
開発環境の用意
・Node.js(nvm)
・yarn
・gitのインストールが必要です
インストールの仕方については、次の記事を参照してくださいGatsby CLIをインストールする
yarn global add gatsby-cli
と入力して、Gatsby.jsをインストールしていきます。スターターをダウンロードしてみる
gatsby new
と入力して、サイトを構築していきます。入力すると、プロジェクト名と使用するスターターを聞いてくるので、自分にあったプロジェクト名とスターターを選んであげましょう。
※スターターについては、https://www.gatsbyjs.org/starters/?v=2
を参照してみてくださいダウンロードできたら、
cd プロジェクト名
でプロジェクトフォルダに移動し、gatsby develop
で開発サーバーを起動してみます。開発サーバーが起動したら、URLからページに飛んでページの表示を確認してみましょう!
サイトを公開する
gatsby build
と入力するとpublic/フォルダにサイトのデータができあがります。
これを公開すればサイトの公開は完了です。※ちなみにビルドしたサイトの表示を確認する場合、
gatsby serve
と入力するば、ビルドしたサイトを確認することが可能です。参考文献:Webサイト高速化のための静的サイトジェネレーター活用入門 GatsbyJSで実現する高速&実用的なサイト構築
- 投稿日:2020-11-26T00:15:26+09:00
firebaseのsetメソッドの使い方と活用方法
firebaseって何?
個人的には費用面、手軽さなど、多方面で最強のアーキテクチャだと思っています。
firebaseのデータ登録、更新について
処理内容 登録 更新 addメソッド 〇 × updateメソッド × 〇 setメソッド 〇 〇 上記のように、新規登録にも更新にも使えるsetメソッドですが、addメソッド、updateメソッドとちょっと使い方が違うんですよね。
そのあたりをわかりやすくまとめられたらと思っています。(データの新規追加)addメソッドとsetメソッドの違いについて
一番大きな違いは、ドキュメントIDを自動発行するかどうかという違いです。
addメソッド:ドキュメントIDを自動発行する。
setメソッド:ドキュメントIDを自動発行しない。使い分け!!
ドキュメントIDにこだわらない場合(どんな値でも良いっす!)はaddメソッドを使うのが楽!
ドキュメントIDを指定したい場合は、setメソッドを利用する。いろんなところで言われている内容ではあると思いますが、ドキュメントIDを自動発行して、setメソッドを利用することもできます。
set.js// ドキュメントをランダムIDで発行 const ref = firebase.firestore().collection('test').doc(); // 先ほど取得したドキュメントのIDを明示的に指定 await firebase.firestore().collection('test').doc(ref.id).set({ name: テスト太郎, phone: 080-0000-0000 }) .then(function(){ // データ登録成功時の処理 // firebaseコマンドは、promiseオブジェクトなので、thenでつなげられる。 }) .catch(function(err){ // データ登録失敗時 });addメソッドの場合は、上記のソース上から、ドキュメントIDを取得してくる処理がまるまる省略できるってことですね。
ざっくり分けて、firebaseのデータ登録機能には以下の2工程に分けられます。
①ドキュメントIDを発行
②データ更新上記を一つにまとめているのが、addメソッド、別々に分けているのがsetメソッドです。
(データ更新)updateメソッドと、setメソッドの違い
一番大きな違いは、特定のフィールドのみ更新するか、全部上書きするかの違いです。
updateメソッド:指定したフィールドのみ更新
setメソッド:全て更新例えば、以下のようなコレクションがあったとします。
コレクション名:test
ドキュメントID:1234567890abcdefg
name:テスト太郎,
phone:080-0000-0000,
address:東京都千代田区updateメソッド.jsfirebase.firestore().collection('test').doc('1234567890abcdefg').update({ name:テスト次郎 });setメソッド.jsfirebase.firestore().collection('test').doc('1234567890abcdefg').set({ name:テスト次郎 });単純にメソッドが違うだけですね。
これが、それぞれどうなるのかを見てみましょう。updateメソッド
コレクション名:test
ドキュメントID:1234567890abcdefg
name:テスト次郎, ← ここのみ更新
phone:080-0000-0000,
address:東京都千代田区setメソッド
コレクション名:test
ドキュメントID:1234567890abcdefg
name:テスト次郎,
※全上書きされ、明示的に記載したものだけが残る。setの活用方法を考えてみた
以下の条件なんかの時はsetメソッドが輝きます。
・ドキュメントIDが指定したIDである。(自動生成ではない)
・新規作成と更新が同ソース内に両方とも含まれる。
→ データがなかった場合は、新規作成、あった場合は更新みたいなケースですね。上記のようなときは、setメソッドを利用することによって、新規作成なのか、更新なのかを考慮する必要がなく、ソースを記載することができます。
普通に利用しようとすると、ちょっと使いづらいsetメソッドですが、設計次第でかなり便利なメソッドになります。
- 投稿日:2020-11-26T00:09:51+09:00
ポケモンとTypeScriptの超入門編(型エイリアスまで)
型エイリアス typeとは
型宣言をtypeで記述することができる。
type Age = number let age: Age = 15オブジェクト
もちろんオブジェクトもできる。
?
を使うと必ず記述しなくても良くなる。let o: { a: number, b?: string } = { a: 2 } console.log(o.a) // -> 2 console.log(o.b) // -> undefinedreadonly
オブジェクトのプロパティを書き換え禁止にできる。
変数で言うconst。let o3: { readonly id: number } = { id: 3 } o3.id = 2 // -> エラーポケモンとtypeでいちゃいちゃする
以上を踏まえてあれこれ触る。
type Action = {1: string, 2?: string, 3?: string, 4?: string } type Pokemon = { readonly abst: string, readonly name: string, actions: Action } let pityu: Pokemon = { abst: 'ピカチュウの進化前', name: 'ピチュー', actions: {1: 'たいあたり', 2: 'しっぽをふる'} } pikatyu.actions[3] = "でんきショック"
- 投稿日:2020-11-26T00:09:51+09:00
ポケモンでTypeScriptおもしれーってなる(型エイリアスまで)
型エイリアス typeとは
型宣言をtypeで記述することができる。
type Age = number let age: Age = 15オブジェクト
もちろんオブジェクトもできる。
?
を使うと必ず記述しなくても良くなる。let o: { a: number, b?: string } = { a: 2 } console.log(o.a) // -> 2 console.log(o.b) // -> undefinedreadonly
オブジェクトのプロパティを書き換え禁止にできる。
変数で言うconst。let o3: { readonly id: number } = { id: 3 } o3.id = 2 // -> エラーポケモンとtypeでいちゃいちゃする
以上を踏まえてあれこれ触る。
type Action = {1: string, 2?: string, 3?: string, 4?: string } type Pokemon = { readonly abst: string, readonly name: string, actions: Action } let pityu: Pokemon = { abst: 'ピカチュウの進化前', name: 'ピチュー', actions: {1: 'たいあたり', 2: 'しっぽをふる'} } pikatyu.actions[3] = "でんきショック"