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

YoutubeDataAPIで好きな情報を取得する

概要 YouTube見る人多いですよね!YouTubeのAPIがあると知ったとき、使えたら面白そうだと思ったので触っていこうと思います! 扱う言語はJavaScriptでVueを用いて進めて行きます。 (なのでVueの記述方法がわからないと少し読みにくいかもです、その場合はコードは気にせず、どういうことをしているのか雰囲気がわかれば大丈夫です!) YoutubeDataAPIv3 これ↑が今回使うものの公式ドキュメントです。 目次 APIキー 特定チャンネルの動画情報を取得する リクエスト内容 レスポンス内容を見てみる 任意チャンネルの情報を取得してブラウザに表示する リクエスト内容 レスポンス内容を見てみる ブラウザ上に表示 おわり APIキー Google Cloud ConsoleへアクセスしてAPIキーの発行が必用なのでまずはここからです。 この記事など情報はたくさんあるので活用していきましょう。そんなに悩むことなくできました。 特定チャンネルの動画情報を取得する まず、あの有名ラーメン系youtuberの動画情報を取ってきてブラウザに表示させるところを目標にします? 今回使うリクエスト先のURLの記述方法は基本的に以下のようになります。 "https://www.googleapis.com/youtube/v3/search?&key=[APIキー]....." 「.....」の部分に欲しい情報についてのパラメータを設定します。[APIキー]の部分はさきほど発光したAPIキーを入れます。 今回の目標は、検索して動画の情報を取得するという動きなのでリクエストで指定した検索パラメータに一致する情報を持ってきてくれる「search」を使います。検索機能は「search」ということでしょうか。詳しくはsearchリファレンスに。 このsearchリファレンスの中に使えるプロパティの説明がありました!そこに良さそうなのがあったので今回はこれ↓を使います。 リクエスト内容 そして最初のリクエストURLは以下のようにしました! "https://www.googleapis.com/youtube/v3/search?&key=" + this.myKey + // はじめに取得した自分のAPIキー "&channelId=" + this.channelId + // ラーメンyoutuberのchannelID "&part=snippet&maxResults=50&order=date" 補足説明します。this.myKeyとthis.channelIDはString型変数です。パラメータは以下のようになっています。 パラメータ 値 内容説明 channelId 文字列(this.channelId) チャンネルIDは、YouTubeチャンネルのURLに記載されています。それをコピーしてthis.channelIdに入れています。 part snippet オブジェクトで情報が返され、基本情報(タイトル、説明など)が格納されます。 maxResults 50 50本の動画の情報を取ってきます。 order data 新規アップロード順に動画の情報を取ってきます。 なお、リクエスト部分のコードはこのように methods: { async getYoutubeApi() { const res = await fetch( "https://www.googleapis.com/youtube/v3/search?&key=" + this.myKey + "&channelId=" + this.channelId + "&part=snippet&maxResults=50&order=date" ) try { const value = await res.json() console.log(value.items) } catch (e) { console.log("error:" + e) } }, }, ここ↓では、valueというものを設定して、そこにAPIレスポンス(取得データ)を入れています。 const value = await res.json() レスポンス内容を見てみる Google Chromeの検証ツールで見てみました。コード的に言うと上のconsole.log(value.items)と書いてある部分にあたります。 メインとなる部分はitemsというオブジェクトに入っていたのでその中身を出しました。 ※APIレスポンスの中にはめちゃ色々なデータがありましたが、今回使えそうなデータは、その中でもitemsというオブジェクトに入っていたということです。イメージしずらいかもですがその場合はあまり考えなくて大丈夫です。 そうすると、50本分あることが確認できました!さらにsnippetオブジェクトの中に動画のタイトル(気になったらyoutubeで検索してみてください!)や概要欄の文章がありました!これで目標達成です。上手く動作したときは気持ちいいですね! 任意チャンネルの情報を取得してブラウザに表示する さっきは動画についてでしたが、次はチャンネルについてやりたいと思います。また、任意のチャンネルを検索して情報を取得できるようにしたいと思います!さらにその情報をブラウザに表示するまでが目標です! リクエスト内容 欲しいものが違うので、リクエストするURLも違ってきます。今回のリクエスト先のURLの記述は以下のようになりました。 "https://www.googleapis.com/youtube/v3/search?&key=" + this.myKey + "&part=snippet&type=channel&q=" + this.q 先ほどのURLとの違いは、channelIDがないことです。任意のチャンネルを持ってくるので特定のIDは不必要ですね。また、qという変数が現れました。ここにチャンネル名が入ることでそのチャンネル情報を持ってこれます。 パラメータの設定はこのようにしてます。 パラメータ 値 内容説明 part snippet さっきのと同じ type channel チャンネルに対して検索することを指示します。 q 任意の文字列(this.q) this.qは自分で設定した変数でブラウザ上で入力します。中身は任意の文字列です。この文字列にヒットするチャンネルが対象になります。 コードは基本的にさきほどと同じですが、ブラウザに表示させる必用があるため、とってきたデータを自分で設定した配列に格納する必用があります。なので次の一文を追加しています。 (valueはレスポンス内容で、その中でitemsというオブジェクトが今回のメインデータでしたね!) this.infos = value.items レスポンス内容を見てみる 検証ツールをつかって内容を見てみます。なお、qは今回「釣り」としました。 どうやら1回のリクエストで5つのチャンネルまで持ってこれることがわかりました(もうちょっと欲しかった、、)。データとしてチャンネル名、チャンネルの説明文、チャンネルアイコンの画像データなどがありました! このデータをブラウザに表示していきます。 ブラウザ上に表示 取得したデータはinfosという変数に入れました。このinfosの中から表示したい情報を選びます。snippetにある以下の3つを今回は使います。 title(チャンネル名) thumbnails(チャンネルのアイコン情報、さらにこの中から画像urlを使います) description(チャンネルの説明文) 表示させる部分のコードはこのようになりました。 template> <div id="youtube"> <h1>YoutubeAPIをつかってみる</h1> <div> <input type="text" v-model="q" /> <button v-on:click="getYoutubeApi">チャンネル検索</button> <div v-for="info in infos" :key="info.id"> <img v-bind:src="info.snippet.thumbnails.default.url" /> <h3> {{ info.snippet.title }} </h3> {{ info.snippet.description }} <div>------</div> </div> </div> </div> </template> 簡単に説明すると、5つのチャンネルの情報を上から順番に「------」という区切りをつけながら並べて表示させています。こんなかんじに表示されています! タイトルの下に入力欄と検索ボタンがあり、ボタンが押されるとチャンネルの情報がばばばっと出てきます。これで目標達成です! 入力はなんでも大丈夫なので、全然知らないチャンネルも出てくるので面白いと思いました。表示させる情報を工夫したらオリジナルの面白い検索機能がつくれちゃいます! ※ここで一つ気がついたのですが、動画の概要欄や説明文を見ると文字数に限界があるみたいですね、、、このAPIはそういった制約が多そうだと思いました。 おわり YouTubeってかなり人気なコンテンツですよね!そのAPIを触ってみたかったのでこのような内容になりました。 また、この記事は大学生限定クリエイティブコミュニティGeekSalon Advent Calendar 2021の企画の一部として投稿させていただきました! GeekSalonは大学生限定のプログラミングスクールです、私もメンターとして所属しています。下のリンクからご覧になってください! 私の所属するコースの特設サイトはこちら!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsのeventemitterとそのデザインパターンについてまとめてみた

はじめに この記事ではeventEmitterとeventEmitterに関わるデザインパターンについて紹介しています。 evenEmitterをマスターすることで、よりプロのプログラマー(略してプロプロ)になれると思います。 eventemitterとは まずイベントエミッターとはなにか説明します。 これ、独自にイベントを作成し、指定したタイミングに関数を発火することができます イベント駆動開発でよく用いられます。 イメージで言うと、フロントエンドでいうと、ボタンを押したときにonpushに登録された関数が発火しますよね。それです! eventemitterを用いると、自作でイベントを作って、好きなタイミングで発火することができるようになります。 Node.jsではeventemitterがストリームの裏側で作られ、実行されています。 EventEmitterの実行例 const EventEmitter = require("events") const eventEmitter = new EventEmitter() const bakuhatsu=() => { console.log("ドッカーーーン") } eventEmitter.on("爆発",bakuhatsu) eventEmitter.once("爆発", () => { console.log("一回だけドッカーーーン") }) eventEmitter.emit("爆発") //ドッカーーーン //一回だけドッカーーーン eventEmitter.emit("爆発") //ドッカーーーン console.log("爆発解除") eventEmitter.off("爆発",bakuhatsu) eventEmitter.emit("爆発") //(何もなし) EventEmitter の主要なインスタンスメソッド 最初に簡単に用法をまとめておきます。 on(イベント名、関数名)でイベントを登録し、 emit(イベント名)でイベントに指定した関数を実行! off(イベント,関数名)でイベントに登録した関数の削除 on(event,listener) 指定したイベントに新しいリスナを登録します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 第一引数に登録したイベントが呼ばれたときに発火する) 例 eventEmitter.on("爆発",()=>{console.log("ドッカーーーン")}) once(event,listener) 指定したイベントに新しいリスナを登録します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 第一引数に登録したイベントが呼ばれたときに発火する) これはon()メソッドと同様です。 このリスナは、イベントが一回発行されるとリスナが削除されます。なので、そのイベントの最初の一回しか第二引数に登録したコールバック関数が実行されません。 例 eventEmitter.once("爆発",()=>{console.log("一回だけドッカーーーン")}) off(event,listener) on()メソッドで登録したイベントの特定のリスナを削除します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 on()メソッドで登録した第二引数) 例 eventEmitter.off("爆発",()=>{console.log("ドッカーーーン")}) emit(event,[...args]) on()やonce()メソッドで追加したイベントを発火します。 (第一引数に文字列 イベントの名前(任意)  第二引数には引数 on()やonce()で登録したコールバック関数に引数が必要な場合のみ使用する) 例 eventEmitter.emit("爆発") //今回は引数なし 使い方は、以上です ここからは、eventemitterを利用するデザインパターンについて説明していきます。 まずかんたんにデザインパターンについて説明しておきます。 デザインパターンとは オブジェクト指向言語で使われる設計パターンのことです。オブジェクト指向では再利用性の高いクラスが必須です。 ではどのような方向性で設計していけばよいのか。。。。 それを示してくれるのがデザインパターンです。 代表的なデザインパターンは23種類あります。(今回は詳しく説明しない) ここではeventemitterで用いられるObserverパターンについて解説していきます。 Observerパターンについて ObserverパターンはNode.js以外でもよく用いられる代表的なデザインパターンです。英語のObserverとは、観察者や監視役という意味があります。 このパターンでは、監視対象に対して発生した何らかのイベントが、監視役(Observer)に逐一通知されます。 監視役(Observer)はあらかじめ監視対象に対して監視を行うための登録処理を行い、監視対象はイベントの発生タイミングで登録済みの監視役(Observer)に対して通知処理を実行します。 それぞれのSubjectには複数のObserverを登録できます。 簡単な例を上げて説明します。 中学校を想定します。ここでは、一人の生徒が監視対象です。先生は一人の生徒「たかひろ」くんに対してそれぞれの処理を仕込みます。 それぞれの先生は、自分の専門の処理を「たかひろ」くんに仕込みます。 そして「たかひろ」くんは登録された処理に従って、 数学の勉強がわからないときは数学の先生に、 野球の打撃の成績が悪いときは、筋肉の先生に、(「たかひろ」くんは野球部) 熱があって、吐き気もするときは、保健室の先生に、 通知を出します。 ここまでがObserverパターンです。 先生が生徒に処理を仕込み、生徒が通知を出すところまででひとくくりです。 ここでこの例に従ってeventemitterのコードを書いてみます。 const EventEmitter = require("events") const eventEmitter = new EventEmitter() const helpme=(teacher)=>{ console.log(teacher+"、助けてください") } eventEmitter.on("数学がわからない", helpme) eventEmitter.on("最近打率が悪い", helpme) eventEmitter.on("体調が悪い", helpme) // 先生(Observer)によってイベントが仕込まれる class takahiro { static suugaku_nervous() { eventEmitter.emit("数学がわからない","数学得意な先生") } static sports_nervous() { eventEmitter.emit("最近打率が悪い","筋肉ムキムキな先生") } static condition_nervous() { eventEmitter.emit("体調が悪い","保健室の先生") } } //「たかひろ」くんが先生にイベントを通知する takahiro.suugaku_nervous() //***実行結果*** //数学得意な先生、助けて! デザインパターンとeventemitterの話はここまでです。 実際のコード 最後に実際に使われているコードの紹介をしておきます。 const http = require("http") var fs = require("fs") //serverのeventEmitterを作成 const server = http.createServer() //requestというイベントに関数を仕込む //serverにアクセスがあったときに発火する server.on("request", (req, res) => { fs.readFile("./helloWorld.html", "utf-8", function (error, data) { res.writeHead(200, { "Content-Type": "text/html" }) res.write("requestに登録されたイベント発火") res.write(data) res.end() }) }) //listeningというイベントに関数を仕込む //これはserver.listen(8000)したときに発火する server.on("listening", (res, req) => {}) //errorというイベントに関数を仕込む //エラーが起こったときに発火する server.on("error", err =>{ console.error(err); }) //closeというイベントに関数を仕込む //これはserver.close()したときに発火する(ここではserver.close()はこめんとあうとした) server.on("close", () => {}) server.listen(8000) // server.close() helloEarth.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Hello,Earth! And,Hello universe!</h1> </body> </html> localhost:8000にアクセスした結果 何気なくhttpモジュールで使っていた関数にもeventEmitterが仕込まれていました。自分が使っている関数や処理がeventEmitterのものかどうか見極める事ができれば、プログラミングのレベルもぐんぐん上がっていくと思いますl 最後に Streamの記事を書くつもりがいつマニカeventEmitter の記事を書いていました。 次回はStreamの記事を書きます!! おいらの記事を最後まで見てくれてありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【準備時間5分】JavaScriptを学びつつ戦う!RoboCodeやろうぜ!

JavaScriptを学びつつ戦う!RoboCodeやろうぜ! ※Windows + Node.js環境を想定して書いています。 Git https://github.com/mokemokepyonpyon/robocode-js/tree/gh-pages Download https://github.com/mokemokepyonpyon/robocode-js/archive/refs/heads/gh-pages.zip RoboCodeとは Robocodeとは元々Java言語でプログラミングする対戦型ロボットシミュレータです。 自分プログラミングしたロボットを、世界中のプログラマが開発したロボットと闘わせることができるんです! youchenleeさんが作成したJS言語バージョンを 更に初心者向けに改良したものが本記事で紹介しているものです。 はじめかた クローンor解凍したZIPの中にある、install.batを起動します。 ※中身はただのnpm installです。   start.batを起動します。 ※中身はただのnode server.jsです。   RoboCodeを起動します。 ※中身はただの http://localhost:3000/index.html へのショートカットです   画面上で戦車が戦い始めたら準備完了です。 遊び方 基本はオートバトルでステージ0~2 とファイナルステージの敵機を倒していく遊びとなります。     自機の強化 ステージ0はデフォルトのままでは勝てると思いますが、ステージ2以降は自機を強化しなければいけません。 public / robot / myrobot.jsをカスタマイズしましょう。 ★参考情報★ 自己情報 ・ me.id ・ me.x ・ me.y ・ me.hp ・ me.angle-現在の角度(タンク角度+タレット角度) ・ me.tank_angle ・ me.turret_angle 敵情報 ・ enemy-spot [N] .id ・ enemy-spot [N] .hp ・ enemy-spot [N] .angle-敵に対する角度(方向) シーケンシャルアクション: ・turn_left(角度) ・turn_right(角度) ・move_forwards(距離) ・move_backwards(距離) ・move_opposide(distance)-このアクションはOnWallCollide()でのみ使用できます 並列アクション: ・turn_turret_left(角度) ・turn_turret_right(角度) ・shoot(攻撃) ・yell(メッセージ) イベント: OnIdle()-アイドル時にトリガーされます(実装する必要があります) OnWallCollide()-タンクが壁に衝突したとき OnHit()-弾丸が当たったとき OnEnemySpot()-砲塔が敵に直接面している場合(発砲しない理由はないようです!)     おわりに 一通りクリアしたら、攻撃と防衛に分かれて、友人と対戦してみるのはいかがでしょう。 攻撃 myrobot.jsをカスタマイズして、ボスを倒す 防衛 boss-0.js~boss-3.jsのいずれかをカスタマイズして、倒されないように防衛する 地味に盛り上がりますよ。 私が学生だった頃はこの遊びはめちゃめちゃ流行ってました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML、CSS、JavaScriptで電卓をつくりました。

作ったアプリ https://xenodochial-clarke-ebb79c.netlify.app/ 四則演算だけできるシンプルな電卓です。できるだけWindows10の標準電卓と同じ動作になるように作りました。 今回は外部ライブラリとして、fittyとbig.jsを使いました。 今回学んだこと 数字が枠からはみ出る問題 電卓のディスプレイ部分ですが、入力する数字の桁が大きすぎると枠からはみ出てしまいます。 [1]を連打しまくる... で、数字が何桁入力されても幅が固定された枠の中に収まるように文字サイズを調整したい!と思っていたところ、fittyというとっても便利なライブラリを見つけました。fittyを使うと、次のようにフォントサイズが自動調整されて桁数が多い場合でも、1行に収まるようにしてくれます。 index.html <div class="result-display"><div><div id="result">0</div></div></div> script.js import fitty from './fitty.module.js' fitty('#result', { maxSize: 48 // 最大フォントサイズを設定。最小も設定できる。 }) 使い方はとても簡単で、fittyに文字サイズを自動調整したい要素を渡して、最大/最小フォントサイズなどを設定するだけです。一見必要なさそうな<div>は、.result-displayにpaddingを設定しているためで、公式の次の注意に従っています。 If the parent element of the fitty element has horizontal padding the width calculation will be incorrect. You can fix this by wrapping the fitty element in another element. 電卓としては1行に収めてくれるとはいえ、あまり桁数が多くなっても困るので数字を入力するたびにその桁数を評価して16桁以上は入力できないようにしています。 script.js if (currentNum.length > 16) { currentNum = currentNum.slice(0, -1) // この時のCurrentNumはStringです } 小数点を含む場合に正しく計算できない 原因はあまり詳しく調べていないですが、JavaScriptでは小数点を含む場合に正しく計算できないことがあるようで、これに対処するために色々やり方はあるみたいですが、今回はbig.jsというライブラリを使いました。 普通に計算した場合 big.jsを使った場合 アプリでの実装は次のような感じで、[+]や[-]が押されたときにbig.jsのメソッドで計算して、Number型に変換しています。 script.js if (ope === '+') { answer = Big(firstTerm).plus(secondTerm).toNumber() } else if (ope === '-') { answer = Big(firstTerm).minus(secondTerm).toNumber() } else if (ope === '×') { answer = Big(firstTerm).times(secondTerm).toNumber() } else if (ope === '÷') { answer = Big(firstTerm).div(secondTerm).toNumber() } キーボード入力で、ボタンをクリックしたときと同じ処理をさせる 例えば、キーボードの[1]を押したときに、画面に表示してある[1]のボタンに登録してあるイベントハンドラを実行させるという実装をしました。dispatchEventメソッドを使って、clickイベント発生時に実行するイベントハンドラをkeydownイベント発生時にも実行させています。 script.js one.addEventListener('click', appendNum) // oneは[1]ボタン要素への参照。クリック時に[1]を計算式に追加する // 同じ動作をキーボードの[1]キーが入力されたときに実行したい window.addEventListener('keydown', getKeyboardValue) // 何らかのキーが押されたときに、getKeyboardValueを実行 const getKeyboardValue = e => { switch (e.key) { // 入力されたキーを判断 case '0': zero.dispatchEvent(new Event('click')) break case '1': // 入力されたキーが[1]であれば、'click'イベントが発火したと捉える→appendNumが実行される one.dispatchEvent(new Event('click')) break // 以下、入力されたキーごとに条件分岐 } type="module"設定時にローカル環境でテストする 今回はscript.jsに外部ライブラリを読み込んでいるので<script>のtype="module"を設定しますが、ローカル環境で普通にindex.htmlを開いて...とやると、エラーが出ます。 index.html <script type="module" src="script.js" defer></script> エラー Access to script at 'file:///path/to/script.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https. で、これはfile://でアクセスしているのが原因らしく、http://でアクセスしてやればいいらしいです。やり方については、下記のMDNの記事がとても分かりやすいです。pythonでローカルサーバを立ち上げればいいらしい。 参考文献・記事 fitty big.js How do you set up a local testing server? JavaScript コードレシピ集
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【p5.js】『Generative Design with p5.js』P_1_2_1_01 で登場する関数 part.2 map()

この記事は Qiita p5js アドベントカレンダー3日目の記事です。 これはなに 書籍『Generative Design with p5.js』P_1_2_1_01に登場する関数について理解を深める記事です。 今回はmap()。 map() mapといえば他の言語だと配列操作の印象がありますが、 p5のmap関数は全然違う目的で使用されるようです。 Type map( value: number, start1: number, stop1: number, start2: number, stop2: number, withinBounds?: boolean ): number; 引数については下記の通り。 value 動的な値 start1 valueの下限 stop1 valueの上限 start2 valueの下限に対して返す値の下限 stop2 valueの上限に対して返す値の上限 withinBounds trueのとき、stop2の値を超えることがなくなるオプション ↑ mapを理解してこれを書くまで結構苦しみました。 リファレンスより Re-maps a number from one range to another. In the first example above, the number 25 is converted from a value in the range of 0 to 100 into a value that ranges from the left edge of the window (0) to the right edge (width). 数値をある範囲から別の範囲に再マッピングします。 上の最初の例では、25という数字は、0から100の範囲の値から、ウィンドウの左端(0)から右端(幅)までの範囲の値に変換されます。(DeepL翻訳) これだけではわからん(素直な気持ち) 試してみる リファレンスを参考に下記のコードを試してみました。 コンソールにポインタのX座標とmapの結果を出力します。 (Vueで使用しているため関数の前に'p'があります) p.setup = () => { p.createCanvas(500, 500); // p.colorMode(p.HSB); }; p.draw = () => { p.background(204); let x1 = p.map(p.mouseX, 0, p.width, 25, 75); console.log(`mouseX: ${p.mouseX} x1: ${x1}`); p.ellipse(x1, 25, 25, 25); }; 出力結果がこちら。 mouseXが522のとき、mapの結果は77.2と出ています。 mouseX: 522 x1: 77.2 mapの結果はellipse()の第一引数にセットされているため、 ポインタが横に動けば描画された楕円も横に動きます。 p.ellipse(x1, 25, 25, 25); では何がおこっているのか。 まずstart1は0、stop1はwidthです。 つまりmapにおけるvalueの開始地点は0、 終着地点は500となります。 それを踏まえてstart2とstop2の結果が返る値となります。 start2は25、ということはmouseX(value)が0のときに25を返します。 stop2は75なので、mouseX(value)が500(width)のときに75を返します。 ここでもう一度コンソールに出力した結果を確認しましょう。 mouseX: 522 x1: 77.2 ポインタのX座標が522、x1が77.2と出ています。 つまり、現在のポインタはcreateCanvasの範囲からはみ出た場所におり、 targetの位置もstop2の値を少し超えた場所に位置しているということになります。 今回はつけていないですが、第五引数のwithinBoundsにtrueを渡せば mouseXが500を超えても、mapの返り値は75でストップします。 いやー難しかった!! どんなときに使えそうか 「変動する値をもって、範囲を指定したい別の値に置き換えたいとき」 でいいんじゃないでしょうか。 実際にP_1_2_1_01で使用している箇所をみてみましょう。 tileCountX = int(map(mouseX, 0, width, 2, 100)); tileCountY = int(map(mouseY, 0, height, 2, 10)); リンクから挙動を試してみてください。 横軸の正確な目視は難しいですが、 マウスが右いっぱいの時にタイルの数が1行に対して100列、 マウスが下いっぱいの時にタイルが10行になっていることが分かります。 動的な要素で制御したいときはmap()の使用を検討してみると 面白い動きができるかもしれませんね。 参考 p5.js reference | map() 『Generative Design with p5.js』P_1_2_1_01
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NodeListをループする

document.querySelectorAllした後のループでちょっとわからなくなって、 まあここ見たら解決するけど よく使うのはこの辺り メモメモφ(・ω・`). (function(d){ Object.keys(NodeList).forEach(function (key) { console.log(NodeList[key]); }); Array.prototype.forEach.call(NodeList, function (node) { console.log(node); }); Array.from(NodeList).forEach(function (el) { console.log(el); }); })(document);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

importの使い分け

自分用のメモ。 先達のコードを読んでいて、何が違うのかと混乱した部分。 import { sample } as from './sample'; import * as sample from './sample'; import sample = require('./sample'); 一番上は分かる。 下二つの違いがよくわからない。 ならば実際に書いて挙動を確認しよう。 importのmoduleとして下記sample.tsがある場合で記述。 sample.ts export interface firstParams { id: string; } export const sampleNumber = "123"; それぞれの挙動結果を確認する。 1.import { firstParams } as from './sample';の場合 sample.tsの中のfirstParamsのみを読み込む。 example.ts import { firstParams } as from './sample'; const sampleConst: firstParams[] = []; // firstParamsの型を付与 2.import * as sample from './sample';の場合 sample.tsの中のexport全てを読み込む。 example.ts import * as sample from './sample'; const sampleConst: sample.firstParams[] = []; // firstParamsの型を付与 const sampleConst2 = sample.sampleNumber; // 123 3.import sample = require('./sample');の場合 sample.tsの中のexport全てを読み込む。 example.ts import sample = require('./sample'); const sampleConst: sample.firstParams[] = []; // firstParamsの型を付与 const sampleConst2 = sample.sampleNumber; // 123 同じじゃないか? では、単にimportするのとrequire()をかませて読み込むのとでの違いを考えればいいのか。 さらっと調べるとrequire()は推奨されてないことが多い。 敢えてそれを使って記述しているのは何故? そんな中、素敵な先人の記事を発見。 Node.js: require()は同期型ロード、importは非同期型ロード 詳しくは上の記事をお読みいただきたいのだが、結論はタイトル通り。 require()は同期型ロード、importは非同期型ロードを行う。 つまり、今回の場合はimport先のmoduleが多数の処理をしていると、この設定が効いてくる。 同期的なロードをしたい場合にはrequire()をかませてimportをする必要があると。 なるほど結論 import * as sample from './sample'; // 非同期型のロード(CommonJS) import sample = require('./sample'); // 同期型のロード(ES Modules) 表に見える挙動は同じでも、連鎖的に繋がっていることで変わる挙動がある。 連携先の挙動も踏まえた上で使うコードを選ばなければならないと、そういう話でした。 もし何か解釈が間違っているようだったら、是非ともご指摘いただきたい。 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slackに通知してくれる日直リマインダーを作る

背景 弊社では、エンジニアの進捗管理や情報共有なとを目的とした日次定例MTGがあります。 日次定例MTGでは、日ごとに日直を決めて司会進行と気になる記事(ニュース)を共有することになっているのですが、 「あ、やべ、今日は日直だった!なにも準備してない!」 ということが度々起きたので、日直リマインダーを作ることにしました。 前提 日次定例MTGで使っているツール 日報やニュースの共有:Slack MTG時刻の管理:Googleカレンダー 日直の管理:スプレッドシート ビデオ通話:Zoom 日直の管理方法 日次定例MTGではスプレッドシート上に簡単なカレンダーが作られていて、日毎に日直の名前を入れる。という方法で管理されていました。 後述しますが、このシートから日直リマインダーを作る場合、今日の日付から日直の名前を参照する必要があったので、少し複雑な処理ができるツールを利用することになりました。 スプレッドシート上のカレンダーは下記画像のような形式です。 ツールの選定 Slackはとても便利で、手軽にリマインダーを作れそうなツールがいくつかあったので、選定からはじめました。 Slackのリマインド機能 特別なツールやインストールを必要とせず、簡単に設定できる。 しかし、スプレッドシートと連携できないため、いつ、誰にリマインドするかを毎度設定しなければならない。 → 不採用 Slackワークフロー ワークフロービルダーにスプレッドシートアプリをインストールする必要があるが、ノーコードで設定が完結する。 スプレッドシートと連携できてCRUDの機能が一通り用意されているが、「日毎に参照するセルの値を変える」などの細かい操作はできなかった。 いまの日直管理シートの書き方では、うまく通知できなさそう。 → 不採用 GAS(Google Apps Script) Googleによって開発されたスクリプト言語で、JavaScriptでプログラムを記述して複雑な処理を実行できる。 スプレッドシートやカレンダーとも連携でき、SlackAPI経由でSlack側にメッセージ送信等も可能。 → 採用 リマインダの作成 1. SlackAPIトークンを取得する 大まかに下記の3ステップで取得できます。 取得方法について、こちらの記事の説明が詳しくて分かりやすいです。  1. Slack Botを作成する  2. Slackのワークスペースに作ったSlack Botをインストールする  3. メッセージ送信用トークンを取得する 2. Apps Scriptのエディタを開く ブラウザでスプレッドシートを開き、拡張機能 > Apps Script を選択すると、GASの編集画面が起動します。 3. コードを書く ②で開いたApp ScriptのエディタにJavaScriptでリマインド機能を実装します。 詳細は省きますが、大まかに下記の用件を満たす機能を作成しました。 ・ 土日、祝祭日に通知をしないように、Googleカレンダーを参照して日次定例MTGの予定のある日だけ実行する ・ 当日に通知されても準備が間に合わないケースがあるため、日直がある日の2営業日前から毎日通知する ・ 日直にはメンションで通知する 実装したコードも載せておきます。 // 毎日の日直を通知する function dailyFacilitatorReminder() { const date = new Date(); // カレンダーを参照して定例がある日だけ実行 if (!hasDailyMeeting(date)) { console.log('本日は定例がありません。'); return; } // スプレッドシード上にある日直カレンダーから最大10日先までの日程を参照し、 // 直近の最大3名に日直のリマインドメッセージを生成する。 let messages = []; for (let i = 0; i < 10; i++) { const facilitator_name = findFacilitatorNameFromSpreadsheet(date); if (facilitator_name) { if (messages.length == 0) { messages.push('本日の日直は<@' + facilitator_name + '>さんです。'); } else if (messages.length == 1) { messages.push('次の日直は<@' + facilitator_name + '>さんです。'); } else if (messages.length <= 2) { messages.push('その次の日直は<@' + facilitator_name + '>さんです。'); break; } } date.setDate(date.getDate() + 1); } // メッセージをSlackに送信する sendSlackMessage(messages.join('\n')); } // 技術部日次定例があるか function hasDailyMeeting(date) { const calendar_id = 'foobar@gmail.com'; const event_name = '技術部日次定例'; const calendar = CalendarApp.getCalendarById(calendar_id); const calendar_events = calendar.getEventsForDay(date, {search: event_name, max: 1}); return Boolean(calendar_events.length); } // スプレッドシート上に作成されたカレンダーから日直の名前を取得する function findFacilitatorNameFromSpreadsheet(date) { const year = date.getFullYear() % 100; const month = date.getMonth() + 1; const day = date.getDate(); const sheet_name = year + '-' + month + '月'; const targeet_date = month + '/' + day; const sheet_id = '/** シートID **/'; const ss = SpreadsheetApp.openById(sheet_id); const sheet = ss.getSheetByName(sheet_name); const text_finder = sheet.createTextFinder(targeet_date); const cell = text_finder.findNext().getA1Notation(); const target_cell = cell.slice(0, -1) + (Number(cell.slice(-1)) + 2); const facilitator_name = sheet.getRange(target_cell).getValue(); return getSlackName(facilitator_name); } // カレンダーに記載されている名前からSlackでの名前を取得する function getSlackName(name) { const user_name_map = { '〇〇': 'U01A0DFSQ2Y', '△△': 'ECGE22YGAHT', '◇◇': 'P3E1ALVMEWD', }; return user_name_map[name] ?? ''; } // Slackにメッセージを送信 async function sendSlackMessage(message) { const url = 'https://slack.com/api/chat.postMessage'; const method = 'post' const payload = { 'token' : '/** SlackAPIのトークン **/', 'channel' : '/** チャンネル名 **/', 'text' : message, }; const params = { 'method' : method, 'payload' : payload }; UrlFetchApp.fetch(url, params); } 4. スクリプトを自動で実行してくれるように、トリガーを設定する App Scriptのエディタ画面の左のタブメニューから[トリガー]を押下し、トリガー管理画面に遷移 画面右下の[+トリガーを追加]ボタンを押下して、トリガー登録に遷移 毎日定時に実行してくれるように入力後に[保存]ボタンを押下し、トリガーを登録  今回作成したスクリプトの場合)  実行する関数:dailyFacilitatorReminder  イベントのソース:時間主導型  トリガーのタイプ:日付ベースのタイマー  時刻:午前9時〜10時 5. 動作テスト ここまで来たら、リマインダーは一通り完成です。 トリガーが発火するのを待つか、エディタから直接実行することで動作を確認することができます。 うまくいくと下記画像のようになります。 最後に 今回初めてGASに触れましたが、GASでもっといろいろなことができそうでワクワクしました。 こんなに便利な機能をタダで提供してくれるGoogleさまには頭が上がりません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでURL取得する

split() メソッドを使用して取得してみる 例えば現在のページが https://??.com/xx/yy/zz だったとする このURLのyyだけを取得したい 方法としては, locationオブジェクトのhrefプロパティで,現在のページURLを取得し, split()メソッドで指定した文字列で分割 というやり方です. var url_split = location.href.split('/'); // /(スラッシュ)で取得したURLを分割し配列化 var url_split_yy = url_split[4]; console.log(url_split_yy); //出力"yy"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマートフォンで魚釣りして夢が釣れるwebアプリ

フィーーーーーーーーーッシュ 年齢がバレてしまいますね。 スマホ一つで直感操作。楽しく魚を釣りましょう。 操作方法 スマートフォンから以下のQRコードを読み取ってください。 加速度センサーが必要なので、ブラウザでは動作しません。 スマートフォンを振り上げる動作を検知して、y方向の加速度を緯度、z方向の加速度を経度として、スプレッドシートに記録していきます。 開発中に撮影したものなので、画面上にはフィッシュと出力されていますが、ここに緯度・経度として設定する数値が出力されるイメージです。 その後シートに記録された経緯と東京の経緯差から距離を測定、LINE notifyに返してくれます。 まるで旅をしているような気分になれる、夢を釣るアプリです。 スマホにいきなり6件も通知来て何かと思ったら、” 夢 ”でした。 開発環境、使用アプリ・ライブラリ CodePen integromat Netlify Stein-js-client Vue.js 作り方詳細 CodePen部分 See the Pen 釣り by watanabe-tsubasa (@watanabe-tsubasa) on CodePen. 27行目から32行目で加速度の最高値を記録し、加速度がyかつz方向で15m/s^2を記録すると、スプレッドシート上にその時のy,z方向の加速度が、緯度・経度として出力されます。 動かしてみるとわかりますが、加速度変化はdevicemotionで常時変化を観測しております。 出力後、再度最高値を0に戻す処理をしてプログラムを継続するので、勢いよく振り抜くと、1振りで複数回スプレッドシート上に出力されます。 加速度の閾値(今回は15m/s^2)を変更することで、振り抜く速度に応じた出力個数を調整可能です。 複数回転記されれば、複数個所の距離が返ってくるので、世界中を旅した気分が味わえます。 Vue.js, steinを使用しているのでCodePenにライブラリを埋め込んでください。 詳細はこちら展開ください 編集画面上部 Settingsを開く JS Add External Scripts/Pensに以下のURLを入力 https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js //Vue.js https://unpkg.com/stein-js-client //stein-js-client iOSで加速度センサーを有効にするためにはデプロイが必要です。今回はNetlifyを使用しています。 詳細はこちら展開ください Export .zipより圧縮ファイルをダウンロードし解凍 解凍されたファイルのうちdistをフォルダごとNetlifyのサイトにドラッグ&ドロップ 黄色の中にドロップ URLが出力されるので、デプロイされています。 integromat部分 スプレッドシートにはあらかじめA1にlatitude,B1にlongitude を記載しておきます。 GPS Toolsはoriginに東京、destinationに取得した経緯を記載しています。 最後に計算した距離が最大15分後にLINEに帰ってきます。 但し、QRリンク先のアプリで釣りをしても、残念ながら通知先は私のスマートフォンです。 誰かが釣りをしているんだな、とうれしくなりますので、ぜひ実施していただけるとありがたいです。 釣果については、15分後に聞いてください。 今後の改善点 フィーーーッシュと叫んで、釣りをスタートさせる スプレッドシートに、一定数のログがたまったら消去する機能を、GASで付随する。 釣りだからと言ってこれをリテールテックと主張するのは流石に怒られる気がします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webの勉強はじめてみた その7 ~JavaScript編 変数名と関数~

「N予備校のプログラミング入門 Webアプリ」のアーカイブ動画と一緒に勉強を進めています。 今日は第一章の11節を受講しました。 変数名 キャメルケース:userInformation スネークケース:user_infromation ケバブケース :user-information JavaScriptではキャメルケースを使うのがいいらしい。 HTMLのidやclassなんかはケバブケース、Pythonはスネークケースみたい。 とにかく、わかりやすいのが一番いいってことかな。 関数 気になったところ 全く同じ関数名(引数も同じ)になった場合 複雑になってくるとこういうことも起こりうるんじゃないのかなと。 エディタで重複を教えてくれると嬉しいけど、どうだろう。 Visual Studioなら教えてくれたけど。 return num % 2 === 0 戻り値もこういう書き方がある。 でもちょっとわかりにくいと言えばわかりにくいかな。 return true でもよさそう? 練習 //円の面積を求める function circleArea(hankei){ var area = hankei * hankei * 3.14; //var rslt2 = '<p>半径' + hankei + 'cmの円の面積は' + area + '㎠です。</p>'; var rslt2 = '半径' + hankei + 'cmの円の面積は' + area + '㎠です。'; //document.write(rslt2); //document.getElementById('circle').innerHTML = rslt2; var circleChild = document.createElement('p'); var cNode = document.getElementById('circle').appendChild(circleChild); cNode.innerText = rslt2; } //半径カウンター let cnt = 5; //1000ミリ秒ごとに半径加算して面積を求める setInterval(function(){ circleArea(cnt); //カウントアップ cnt += 5; }, 1000) コメントアウトは試行錯誤した結果です。 本筋と関係ない部分で悩むっていう。 なんとなく自動で計算させたい&せっかくだから全部表示したいと思いまして。 けど止め方がわからない。 タグ追加のところ、参考にさせてもらいました。 まとめ いまいちタイマーの挙動というか、ふわっとした理解しかしてないのが。 もう少し頭をやわらかくしたい今日この頃。 次回で一旦JS編は終わりのようなので、気合いれてがんばります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsのeventemitterとそのデザインパターンについてまとめてみた

はじめに この記事ではeventEmitterとeventEmitterに関わるデザインパターンについて紹介しています。 evenEmitterをマスターすることで、よりプロのプログラマー(略してプロプロ)になれると思います。 eventemitterとは まずイベントエミッターとはなにか説明します。 これ、独自にイベントを作成し、指定したタイミングに関数を発火することができます イベント駆動開発でよく用いられます。 イメージで言うと、フロントエンドでいうと、ボタンを押したときにonpushに登録された関数が発火しますよね。それです! eventemitterを用いると、自作でイベントを作って、好きなタイミングで発火することができるようになります。 Node.jsではeventemitterがストリームの裏側で作られ、実行されています。 EventEmitterの実行例 const EventEmitter = require("events") const eventEmitter = new EventEmitter() const bakuhatsu=() => { console.log("ドッカーーーン") } eventEmitter.on("爆発",bakuhatsu) eventEmitter.once("爆発", () => { console.log("一回だけドッカーーーン") }) eventEmitter.emit("爆発") //ドッカーーーン //一回だけドッカーーーン eventEmitter.emit("爆発") //ドッカーーーン console.log("爆発解除") eventEmitter.off("爆発",bakuhatsu) eventEmitter.emit("爆発") //(何もなし) EventEmitter の主要なインスタンスメソッド 最初に簡単に用法をまとめておきます。 on(イベント名、関数名)でイベントを登録し、 emit(イベント名)でイベントに指定した関数を実行! off(イベント,関数名)でイベントに登録した関数の削除 on(event,listener) 指定したイベントに新しいリスナを登録します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 第一引数に登録したイベントが呼ばれたときに発火する) 例 eventEmitter.on("爆発",()=>{console.log("ドッカーーーン")}) once(event,listener) 指定したイベントに新しいリスナを登録します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 第一引数に登録したイベントが呼ばれたときに発火する) これはon()メソッドと同様です。 このリスナは、イベントが一回発行されるとリスナが削除されます。なので、そのイベントの最初の一回しか第二引数に登録したコールバック関数が実行されません。 例 eventEmitter.once("爆発",()=>{console.log("一回だけドッカーーーン")}) off(event,listener) on()メソッドで登録したイベントの特定のリスナを削除します。 (第一引数に文字列 イベントの名前(任意)  第二引数にコールバック関数 on()メソッドで登録した第二引数) 例 eventEmitter.off("爆発",()=>{console.log("ドッカーーーン")}) emit(event,[...args]) on()やonce()メソッドで追加したイベントを発火します。 (第一引数に文字列 イベントの名前(任意)  第二引数には引数 on()やonce()で登録したコールバック関数に引数が必要な場合のみ使用する) 例 eventEmitter.emit("爆発") //今回は引数なし 使い方は、以上です ここからは、eventemitterを利用するデザインパターンについて説明していきます。 まずかんたんにデザインパターンについて説明しておきます。 デザインパターンとは オブジェクト指向言語で使われる設計パターンのことです。オブジェクト指向では再利用性の高いクラスが必須です。 ではどのような方向性で設計していけばよいのか。。。。 それを示してくれるのがデザインパターンです。 代表的なデザインパターンは23種類あります。(今回は詳しく説明しない) ここではeventemitterで用いられるObserverパターンについて解説していきます。 Observerパターンについて ObserverパターンはNode.js以外でもよく用いられる代表的なデザインパターンです。英語のObserverとは、観察者や監視役という意味があります。 このパターンでは、監視対象に対して発生した何らかのイベントが、監視役(Observer)に逐一通知されます。 監視役(Observer)はあらかじめ監視対象に対して監視を行うための登録処理を行い、監視対象はイベントの発生タイミングで登録済みの監視役(Observer)に対して通知処理を実行します。 それぞれのSubjectには複数のObserverを登録できます。 簡単な例を上げて説明します。 中学校を想定します。ここでは、一人の生徒が監視対象です。先生は一人の生徒「たかひろ」くんに対してそれぞれの処理を仕込みます。 それぞれの先生は、自分の専門の処理を「たかひろ」くんに仕込みます。 そして「たかひろ」くんは登録された処理に従って、 数学の勉強がわからないときは数学の先生に、 野球の打撃の成績が悪いときは、筋肉の先生に、(「たかひろ」くんは野球部) 熱があって、吐き気もするときは、保健室の先生に、 通知を出します。 ここまでがObserverパターンです。 先生が生徒に処理を仕込み、生徒が通知を出すところまででひとくくりです。 ここでこの例に従ってeventemitterのコードを書いてみます。 const EventEmitter = require("events") const eventEmitter = new EventEmitter() const helpme=(teacher)=>{ console.log(teacher+"、助けてください") } eventEmitter.on("数学がわからない", helpme) eventEmitter.on("最近打率が悪い", helpme) eventEmitter.on("体調が悪い", helpme) // 先生(Observer)によってイベントが仕込まれる class takahiro { static suugaku_nervous() { eventEmitter.emit("数学がわからない","数学得意な先生") } static sports_nervous() { eventEmitter.emit("最近打率が悪い","筋肉ムキムキな先生") } static condition_nervous() { eventEmitter.emit("体調が悪い","保健室の先生") } } //「たかひろ」くんが先生にイベントを通知する takahiro.suugaku_nervous() //***実行結果*** //数学得意な先生、助けて! デザインパターンとeventemitterの話はここまでです。 実際のコード 最後に実際に使われているコードの紹介をしておきます。 const http = require("http") var fs = require("fs") //serverのeventEmitterを作成 const server = http.createServer() //requestというイベントに関数を仕込む //serverにアクセスがあったときに発火する server.on("request", (req, res) => { fs.readFile("./helloWorld.html", "utf-8", function (error, data) { res.writeHead(200, { "Content-Type": "text/html" }) res.write("requestに登録されたイベント発火") res.write(data) res.end() }) }) //listeningというイベントに関数を仕込む //これはserver.listen(8000)したときに発火する server.on("listening", (res, req) => {}) //errorというイベントに関数を仕込む //エラーが起こったときに発火する server.on("error", err =>{ console.error(err); }) //closeというイベントに関数を仕込む //これはserver.close()したときに発火する(ここではserver.close()はこめんとあうとした) server.on("close", () => {}) server.listen(8000) // server.close() helloEarth.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Hello,Earth! And,Hello universe!</h1> </body> </html> localhost:8000にアクセスした結果 何気なくhttpモジュールで使っていた関数にもeventEmitterが仕込まれていました。自分が使っている関数や処理がeventEmitterのものかどうか見極める事ができれば、プログラミングのレベルもぐんぐん上がっていくと思いますl 最後に Streamの記事を書くつもりがいつマニカeventEmitter の記事を書いていました。 次回はStreamの記事を書きます!! おいらの記事を最後まで見てくれてありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript] 乱数の生成方法

JavaScriptで、乱数を使用したので乱数の生成方法を簡単にまとめておく。 乱数の生成 JavaScriptで乱数を生成する場合、MathオブジェクトのMath.random()を使用する。 Math.random()は、0.0以上1.0未満の範囲での浮動小数点数を返す。基本的な乱数生成の書き方は以下となる。 const random = Math.random(); console.log(random); // 0.9986124832248591 ここで、JavaScriptのMath.random()は、C言語とは異なりシード値を指定しなくても実行ごとに異なる値が生成される。 さらに、乱数の値の範囲指定や整数値の乱数を生成する方法についてまとめておく。 値の範囲を指定して乱数を生成 ある範囲内の値(min以上max未満)で乱数を生成したい場合は、以下のように書ける。 // min = 3, max=5 の場合 const random = Math.random()*(max - min) + min; console.log(random); // 4.186149518789809 整数の乱数を生成 乱数の範囲指定に加えて、生成される乱数の値が整数になるようにする場合は、Math.floor()を使用する。Math.floor()は、引数の値以下で最大の整数を返すメソッドである。これを用いて以下のように書くことができる。 // min = 5, max=30 の場合 const random = Math.floor(Math.random()*(max - min) + min); console.log(random); // 17
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js devtoolsの使い方まとめ

はじめに こちらでは vue.js のdevtoolsについて使い方を簡単に解説します。 devtoolsとは Vue.js devtoolsは、Vue.jsの開発をサポートする Chromeブラウザの拡張機能 になります。こちらを導入すると、consoleを開かなくてもdataの中身などを確認/編集することができる ようになります。言い換えると、デバッグしやすくなるということです。 ★ devtools は以下からインストールが可能です インストールができていれば、Vue.jsを読み込んでいるファイルをブラウザ上で開くと、Vue.js Devtoolsのアイコンがアクティブになります。また、ディベロッパーツールを開くと、vueというタブが追加されます。 ↑アイコンがアクティブな状態 ↑ディベロッパーツールのタブ devtools の主な機能 今回は主な機能を3つ紹介します。 1.Components 2.Vuex 3.Events 1. Components Components機能を使用することで、各コンポーネントの親子関係や保持しているdataの内容などを確認することが可能になります。 ・操作方法は3つです。 1. ディベロッパーツールのvueタブからComponentsを選択します。 2. Componentsの左下にあるselectsを選択します。 3. カーソルを任意のコンポーネントへ移動させます。 以上の操作で、任意のコンポーネントの親子関係や、保持しているdataの内容を確認することができます。 2. Vuex Vuex機能を使用することで、Vuex※を使用している場合、storeの中の状態を確認することが可能になります。 ※Vuexとは何か-公式ドキュメントより参照 ・操作方法は2つです。 1. ディベロッパーツールのタブからVuexを選択します。 2. ディベロッパーツール内に表示されている任意のデータを選択して内容を確認します。 以上の操作で、Vuexを使用している場合、storeの中の状態を確認することができます。 3. Events Events機能を使用することで、イベントがトリガーされた履歴を確認することが可能になります。 ・操作方法は3つです。 1. ディベロッパーツールのタブからEventsを選択します。 2. 任意のイベントをトリガーします。※画面をスクロールするなど 3. ディベロッパーツール内に表示されている任意の履歴を選択して内容を確認します。 まとめ 今回は、Vue.js devtools の使い方を簡単にまとめました。今後新しい使い方などを発見した際は追記します。 実際に私が使ってみた感想としては、わざわざコンソールでデータの中身を確認したりする必要がなくなり、デバッグが楽にできるなと思いました。ぜひみなさんも使ってみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Hook Formで数値を処理する際にハマったこと

本記事は、React Advent Calendar 2021の6日目の記事です。 Reactでフォームを作成するとき、簡単に作成できるReact Hook Formを使うことがよくあるかと思います。 本記事では、私がReact Hook Formで数値を処理する際にハマったこととその解決方法を紹介します。 なお、記事中のコードは一部分の引用です。記事の最後にCodeSandboxのリンクを記載しています。 はじめに React Hook Formでは下記のようにuseForm()を使って必要な関数や値を取得します。 また、フォーム送信のハンドラ(ここではonSubmit)を定義して引数dataを与えることで、フォームに入力された値をdataで取得して、フォーム送信時の処理を記載することができます。 const App = () => { const { control, handleSubmit } = useForm(); const onSubmit = (data) => { // フォーム送信時の処理を記載する console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> {/* ここにフォームを作る */} <input type="submit" /> </form> ); }; TypeScriptを使う場合、useForm()に型を与えてdataの型を定義することができます。 この場合はIFormInputという型を定義しています。 interface IFormInput { firstName: string; lastName: string; } const App = () => { const { control, handleSubmit } = useForm<IFormInput>(); // 型を定義 const onSubmit = (data: IFormInput) => { console.log(data); }; さらに、React Hook FormはMUI(Material-UI)と組み合わせて使うこともできます。 ここでは、MUIのTextFieldと組み合わせて利用してみます。 作成したフォームはこちらです。 コードはこちらです。 下記のように、Controllerコンポーネントを用いることで、React Hook FormとMUIと組み合わせて使うことができます。 interface IFormInput { firstName: string; lastName: string; } const App = () => { const { control, handleSubmit } = useForm<IFormInput>(); const onSubmit = (data: IFormInput) => { console.log(data); }; // Controllerコンポーネントを用いることでMaterial-UIと組み合わせて利用可能 return ( <form onSubmit={handleSubmit(onSubmit)}> <Stack spacing={2}> <label>First Name</label> <Controller render={({ field }) => <TextField {...field} />} name="firstName" defaultValue="" control={control} /> <label>Last Name</label> <Controller render={({ field }) => <TextField {...field} />} name="lastName" defaultValue="" control={control} /> <input type="submit" /> </Stack> </form> ); }; より詳細な使い方は本記事では省略させていただきますので、公式ドキュメントなどをご参照ください。 発生した問題 今回、私はフォームで金額を入力したかったため、IFormInputにnumberのpriceを追加しました。 また、priceをカンマ区切りで表示するためにtoLocaleString()を使いました。 type FormData = { firstName: string; lastName: string; price: number; // 追加 }; const App = () => { const { control, handleSubmit } = useForm<IFormInput>(); const onSubmit = (data: IFormInput) => { console.log(data.price.toLocaleString()); // カンマ区切りで表示したい return ( <form onSubmit={handleSubmit(onSubmit)}> <Stack spacing={2}> <label>First Name</label> <Controller render={({ field }) => <TextField {...field} />} name="firstName" defaultValue="" control={control} /> <label>Last Name</label> <Controller render={({ field }) => <TextField {...field} />} name="lastName" defaultValue="" control={control} /> {/* 追加 */} <label>Price</label> <Controller render={({ field }) => <TextField {...field} />} name="price" defaultValue="" control={control} /> <input type="submit" /> </Stack> </form> ); }; しかし、100000を入力してもコンソールには100000と出力され、うまく動きませんでした。(期待値は100,000) 原因を調査したところ、typeofでpriceの型を出力すると原因が分かりました。 interface IFormInput { firstName: string; lastName: string; price: number; } const App = () => { const { control, handleSubmit } = useForm<IFormInput>(); const onSubmit = (data: IFormInput) => { console.log(typeof data.price); // string なんとpriceはstringになっていました。 なので、toLocaleString()を使ってもカンマ区切りにはならなかったのです。 IFormInputでnumberを指定してすっかり安心してしまっていました。 フォームからの出力なのでよく考えればstringしか定義できないような気はしますが、numberで指定して特にエラーにもならず、VSCodeの型表示もnumberとなっていたこともあり、ハマってしまいました。 異なる型でも代入できるのは、TypeScriptの型付けの闇な気がします。 修正版 priceはstringにして、キャストして使うように修正しました。 interface IFormInput { firstName: string; lastName: string; price: string; } const App = () => { const { control, handleSubmit } = useForm<IFormInput>(); const onSubmit = (data: IFormInput) => { console.log(Number(data.price).toLocaleString()); // カンマ区切りで表示される }; 全体のコードはこちらをご確認ください。 数値以外を入力できないようにバリデーションを追加しています。 以上になります。 最後までお読みいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useReducerとuseContextでglobal stateを作る場合のおすすめなやり方

はじめに React Contextでglobal stateを実現する際、useStateやuseReducerを使ってcontextの伝搬で更新するやり方が知られていますが、contextを一つだけにすると、無駄なレンダリングが生じることがあります。そこで、contextの伝搬を使う場合は、contextを細かく分けることがおすすめです。 コード 最初にuseReducerでstateを作るカスタムフックを作ります。 const useValue = () => useReducer((state, action) => { if (action.type === 'INC_COUNT1') { return { ...state, count1: state.count1 + 1 }; } if (action.type === 'INC_COUNT2') { return { ...state, count2: state.count2 + 1 }; } if (action.type === 'INC_BOTH_COUNTS') { return { ...state, count1: state.count1 + 1, count2: state.count2 + 1 }; } throw new Error('unknown action type: ' + action.type) }, { count1: 0, count2: 0 }) 次にそれぞれの項目を伝搬させるためのcontextを作ります。 const DispatchContext = createContext(() => {}); const Count1Context = createContext(0); const Count2Context = createContext(0); Providerコンポーネントは次のようになります。入れ子にならないようにreduceRightを使います。 const Provider = ({ children }) => { const [{ count1, count2 }, dispatch] = useValue(); return [ [DispatchContext.Provider, { value: dispatch }], [Count1Context.Provider, { value: count1 }], [Count2Context.Provider, { value: count2 }], ].reduceRight( (a, c) => createElement(c[0], c[1], a), children ); }; 実際にstateを使う2つのコンポーネントがこちらです。2つともほぼ同じです。 const Counter1 = () => { const count1 = useContext(Count1Context); const dispatch = useContext(DispatchContext); const inc = () => dispatch({ type: 'INC_COUNT1' }); return ( <div> Count1: {count1} <button onClick={inc}>+1</button> </div> ); }; const Counter2 = () => { const count2 = useContext(Count2Context); const dispatch = useContext(DispatchContext); const inc = () => dispatch({ type: 'INC_COUNT2' }); return ( <div> Count2: {count2} <button onClick={inc}>+1</button> </div> ); }; 最後に全体をまとめるコンポーネントです。 const App = () => ( <Provider> <div> <Counter1 /> <Counter1 /> <Counter2 /> <Counter2 /> </div> </Provider> ); CodeSandbox 課題 今回はシンプルな例でしたが、オブジェクトの構造が複雑でも、必要な数だけcontextを作ればなんとかなります。しかし、contextは動的には作れない(作ることはできるが、全部再マウントすることになってしまう)ので、あらかじめ分かっている固定数のものにしか対応できません。 おわりに これをすんなり使おうと思える方はぜひどうぞ。こんなことするくらいならライブラリを使おうと思う方は、これとかこれとかこれとかをどうぞ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JS]連想配列の代入で Invalid left-hand side in assignment expression ... が出た時の原因と解決。

連想配列で、なぜかInvalid left-hand side in assignment expressionが出てきてしまった。 調べても左辺が無効。とだけでて、コードを見てもなぜ無効なのかが分からなかった。 原因は単純だった。 array?.[0]=値 の?.がいけないらしく。 代入する先を決めているのに、?で保険?をかけてくんなや!みたいな感じ。 ?.は基本的に、取得するときに使う。 ...執筆途中。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Open Source Summit Japan + Automotive Linux Summit

12月14日&15日に開催されるOpen Source Summit Japan + Automotive Linux Summitに登壇します。沢山のスピーカーが知見を披露されるので、オープンソースについて学ぶ絶好の機会です。 ぜひご参加いただき、お気軽にお声がけください!SnykSec ご参加はこちら:Open Source Summit
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クリスマスプレゼントをARで宝探し★おまけつき

ただプレゼントを渡すだけでは楽しくない 私には7歳と8歳の姪っ子と甥っ子がいるのですが、 「クリスマスプレゼント、今年は何がいい?」と聞くと 「Nintendoのプリペイドカード」と言われました(笑) すごく現実的・・・!! 大人になったなと思いつつも、見た目はシンプル。 カードだけでは楽しくないので、面白くプレゼントを渡せないか?ということで、 最近学んだARで宝探しをしよう!と思いました。 宝探しの方法 ①マーカーを部屋に貼り付ける ②ARで読み込むと矢印が出現する ③矢印の先にクリスマスプレゼントがある 完成品 こちらのAR矢印.jsのリンクを開いて 下記のマーカーを読み取ってください! リンクに貼ったコードペンはこちらです。 See the Pen AR矢印.js by HaranakaIzumi (@IHaranaka) on CodePen. 3Dの矢印の素材をつくる・・・!! CGTrader - VR / ARとCGプロジェクトのための3Dモデルに無料で glbファイルの矢印の素材があればよかったのですが見つからず。。。 自分で作成に挑戦しました。 「Blender」の易しい使い方を参考にしました! なんとか矢印をつくることに成功! おまけ①メッセージカード メッセージカードも作っちゃえ!ということで、 自作の3Dモデルを「A-Frame」で読み込んで「AR.js」上に表示させてみたを参考に 「MerryChristmas!!」のARも作ってみました。 クリスマスAR.jsから使っていただけます! See the Pen クリスマスAR.js by HaranakaIzumi (@IHaranaka) on CodePen. おまけ②iPaasで連携して運動もしてもらう とはいえ、家にこもってゲームばかりでは体によくないので、 外で1時間くらいは運動してほしいので、 ゲームの前にミッションを出すことにしました。 Integromatでつい最近「Strava」という運動を記録するSaasが追加されていておもしろそうだったので、 家の外に出たら、運動を記録するようにiPaasでつないでみました。 Webhookを使えば、なんでもつながって幅が広がるよ!とは聞いていたのですが、 なるほど!これはめちゃくちゃ便利で幅が広がる!! WebhookでIFTTTとIntegromat繋いじゃったら、最強ですね・・・!!! こちらの記事を参考に繋ぎました。 Nature Remo連続実行を『Integromat』で実現する方法 子どものいる同年代の友人に聞いてみた おもしろいね! 宝探しはすごく喜びそう!!と嬉しい言葉をいただきました!! 一方で、子どもが3歳だから、上手くつかえるかな。。。 とはいえ、現代っ子で勝手にYoutube開く、ゲームはやるので、 意外にいけるかもしれない・・・(笑) とのフィードバックでした。 確かに、結構小さな子でもスマホを使いこなせているので、 サポートがあれば、デジタルネイティブな子どもたちは、 想像以上に使いこなせるかもしれないというのは気付きでした。 クリスマスまであと・・・ クリスマスまであと23日になりました!(今日は12月2日) シンプルなプレゼントでも、企画をプラスして、 かわいい子どもたちに、クリスマスをたくさん楽しんでもらいましょう!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Temporal で JavaScript の次世代の日時処理に触れてみる

この記事は Recruit Advent Calendar 2021 の 7 日目の記事です。 イントロダクション Web アプリケーションの新規開発をしていて、先日、日時処理のライブラリ選定をする機会がありました。直近のプロジェクトで date-fns を使っていたので、今回もそれでいいかと考えていたのですが、Temporal が TC39 プロポーザルではあるが stage 3 になっているから試してみてもいいんじゃないかという話になりました。stage 3 であれば  API 変更のハードルは非常に高いそうです。stage 3 になるまでの提案はこちらの記事が参考になります。少し見ただけでも大きく変わっていることがわかります。 プロジェクトはまだ開発段階ですが、実際に Temporal をプロジェクトに導入しながら、検証兼開発を進めていいます。しかし、Temporal を実際に使ってみると、思ったように手が動きませんでした。はじめて使う API ではあるので、慣れていないのは当然ですが、Moment.js や date-fns などの日時ライブラリにや Date に飼い慣らされているので、感覚的にとっつきにくさを感じました。幸い Temporal はドキュメントがかなりしっかりしているので、このドキュメントだけで(時間があれば)十分に学習できます。 なお、アドベントカレンダー 9 日目を担当する sititou70 により、一部ドキュメントが日本語訳されています。すばらしいですね! ref. TC39 Temporal のドキュメントを一部翻訳しました この記事は、Temporal の勘所をおさえて、日時処理の基本となりそうなユースケースを説明します。現場で使う時のとっかかりになり、ドキュメントを理解しやすくなることが狙いです。 Temporal について Temporal は日時を操作する API を提供するグローバルオブジェクトです。既存の Date の問題点を解決が Temporal 策定における 1 つのモチベーションとのことです。 現在のところ、Temporal を使用するにはポリフィルが必要です。stage 3 まできているので、そろそろ TypeScript が実装してくれないかなと期待を抱いています。 Temporal の肝 Temporal の API をすんなり使うためには、以下の 2 点をおさえておくといいと感じました。 Exact Time と Wall-Clock Time の違いを理解する Temporal 各 API の用途を理解する Exact Time と Wall-Clock Time Temporal におけるタイムゾーンとサマータイム、曖昧性の解決から引用します。 Temporal の中核となるコンセプトは、wall-clock time(ローカル時間 や clock time とも呼ばれる、タイムゾーンに依存した時刻)と exact time(UTC 時間 とも呼ばれる、地球上のどこでも同じ時刻)を区別することです。 wall-clock time は地方政府によって制御されているため、突然変更される可能性があります。サマータイムが導入されたり、ある国のタイムゾーンが他のものに変更されたりすると、ローカル時間は即座に変更されます。exact time は変更されない国際的な定義を持っており、UTC という特別なタイムゾーンで呼ばれています。 exact time は ISO 8601 / RFC 3339 規格であれば 2020-09-06T17:35:24.485Z のように表現するか、Unix 時間で表現します。一方で、wall-clock time は +09:00 のような UTC オフセットで 2020-09-06T17:35:24.485+09:00 のように表現します。Temporal の API 群はこの 2 つの time を区別する設計になっています。 Temporal API とにかく多いです。日付けライブラリの API に慣れている人はどれを使ったらいいか迷うと思います。ドキュメントにある Temporal 直下の API を列挙します。 Temporal.Instant Temporal.ZonedDateTime Temporal.PlainDate Temporal.PlainTime Temporal.PlainDateTime Temporal.PlainYearMonth Temporal.PlainMonthDay Temporal.TimeZone Temporal.Calendar Temporal.Duration Temporal.Now 以下はドキュメントから引用した各オブジェクトの関係図です。 図の中に Temporal.Now が存在しません。Temporal.Now は名前の通り現在の日付や時間を扱うメソッド群が集約されています。図中のオブジェクトを使って現在の日時と時間を扱うだけなので、他のオブジェクトを理解していればとくに問題ありません。 関係図から、Timezone、Calendar および、Duration は、exact time や wall-clock time を扱う上で補助的な役割になることが伺えます。ポイントは exact time を表現するのは Instant と ZonedDateTime、wall-clock time を表現するのは ZonedDateTime と Plain から始まるクラス群であるということです。 つまり、日時処理で中心的になるのは以下の 3 つです(Plain で始まるクラスを PlainXXX としています)。日時処理をする際は、これら 3 つの API を中心に考えましょう。 Temporal.Instant Temporal.ZonedDateTime Temporal.PlainXXX Temporal では exact time や wall-clock time を扱うクラスを分けていたり、Plain から始まるクラスがいくつかあったりと、ユースケースによってクラスが分かれていることをおさえておくことが重要です。ユースケースを限定することにより、日時処理コードの可読性向上や、タイムゾーンが不明な時間に対して間違ったオフセットを与えてしまうようなバグを防ぐことが狙いのようです。 ここまで各 API の説明をしていません。それぞれのオブジェクトは固有の文字列表現をもっているので、その文字列表現で違いを確認します。こちらもドキュメントにある図を引用します。 Plain 群は UTC オフセットやタイムゾーンが関連付けられていないことがわかります。Instant には UTC オフセットがあるが、タイムゾーンは関連付けられておらず、ISO 8601 / RFC 3339 規格で表現できることがわかります。ZonedDateTime はすべての範囲をカバーしています。ZonedDateTime がすべての情報をもっているので、これだけ使えばいいと短絡的に考えてしまいそうです。しかし、すべての情報を揃えられるケースは少ないですし、ユースケースを限定してバグを防ぐ狙いもあるので、適切な API を使うようにしましょう。 各オブジェクトに文字列表現があるということは、toString() でどのような情報をもっているかがおおよそ把握できるということです。 import { Temporal } from "@js-temporal/polyfill"; const dateString = "2020-08-05T20:06:13+09:00[Asia/Tokyo]"; const plainDate = Temporal.PlainDate.from(dateString); console.log(plainDate.toString()); // 2020-08-05 const plainDateTime = Temporal.PlainDateTime.from(dateString); console.log(plainDateTime.toString()); // 2020-08-05T20:06:13 const instant = Temporal.Instant.from(dateString); console.log(instant.toString()); // 2020-08-05T11:06:13Z、timeZone オプションなしだと UTC 時間になる console.log(instant.toString({ timeZone: "Asia/Tokyo" })); // 2020-08-05T20:06:13+09:00 const zonedDateTime = Temporal.ZonedDateTime.from(dateString); console.log(zonedDateTime.toString()); // 2020-08-05T20:06:13+09:00[Asia/Tokyo] また、図に補足されているように、Calendar 拡張部分は Instant 以外のオブジェクトであれば使うことができます。 console.log(plainDate.toString({ calendarName: "always" })); // 2020-08-05[u-ca=iso8601] ユースケース 上述の Temporal の勘所をおさえたら、アプリケーション開発でよくありそうなユースケースをみていきましょう。 現在日時を扱う 現在の日付や時間を扱う場合は Temporal.Now のメソッドを使います。 ローカル時間(wall-clock time)は Plain 系のオブジェクトを使います。Temporal.Now.plainXXX のようなメソッドがあるのでそれを使います。 Temporal.Now.plainDateTimeISO().toString(); // ex. 2021-12-02T18:14:28.704468703 ISO8601 カレンダーを使うので ISO とついています。通常の日付を使うのであれば、ISO8601 カレンダーで問題ありません。ISO がついていないメソッドもありますが、こちらは使用するカレンダーを指定する必要があります。 // ISO8601 カレンダーを指定しているので、plainDateTimeISO と同じ Temporal.Now.plainDateTime({ calendar: "iso8601" }).toString(); 上記では、タイムゾーンを指定していないので、システムのタイムゾーンが使用されます。引数でタイムゾーンを指定することもできます。 Temporal.Now.plainDateTimeISO("Asia/Tokyo").toString(); また、システムのタイムゾーンを取得することもできます。 Temporal.Now.timeZone().toString(); // ex. Asia/Tokyo 余談ですが、システムのタイムゾーンを使う場合は注意する必要があります。JavaScript が動く環境はブラウザだけでなく、Node.js サーバーであったり、最近では Edge クラウドであったりします。同じシステムでも CSR のときは Asia/Tokyo なのに、SSR のときは UTC になっているということもあるかもしれません。 exact time の場合は、Instant オブジェクトを使います。Instant オブジェクトは Temporal.Now.instant が利用できます。 console.log(Temporal.Now.instant().toString()); // ex. 2021-12-02T09:38:18.689898688Z Unix タイムスタンプを取得することもできます。秒からナノ秒までの API が用意されているようですね。 const instant = Temporal.Now.instant(); console.log(instant.epochSeconds); // ex. 1638437898 console.log(instant.epochMilliseconds); // ex. 1638437898698 console.log(instant.epochMicroseconds); // ex. 1638437898698898n console.log(instant.epochNanoseconds); // ex. 1638437898698898689n 日時文字列をパースして日時オブジェクトを生成する 日付や時刻の文字列をパースして、オブジェクトを生成することは、よくあるユースケースかと思います。Temporal の API 各 API は厳格に定義された文字列をパースします。日時を扱う各クラスには from という static メソッドが用意されているので、このメソッドで文字列をパースします。扱う日時文字列の情報量を意識して文字列をパースする必要があります。 Instant は exact time を表現します。ISO 8601 形式を渡す必要があります。タイムゾーンは渡せますが無視されます。ISO 8601 以上の情報を持っていない文字列を渡すとインスタンス化に失敗します。 let instant = Temporal.Instant.from("2020-08-05T20:06:13+09:00[Asia/Tokyo]"); console.log(instant.toString()); // 2020-08-05T11:06:13Z、タイムゾーンは無視される instant = Temporal.Instant.from("2020-08-05T20:06:13+09:00"); console.log(instant.toString()); // 2020-08-05T11:06:13Z instant = Temporal.Instant.from("2020-08-05T20:06:13"); // throw RangeError、UTC オフセットが足りないのでエラーが発生する [Asia/Tokyo] のようなタイムゾーン表記は、ISO-8601 / RFC 3339 ではカバーされていませんが、事実上の業界標準として採用しているそうです。 ref. ECMAScript 拡張の ISO-8601 と RFC 3339 ZonedDateTime でも同様の結果になります。 let zonedDateTime = Temporal.ZonedDateTime.from( "2020-08-05T20:06:13+09:00[Asia/Tokyo]" ); console.log(zonedDateTime.toString()); // 2020-08-05T20:06:13+09:00[Asia/Tokyo] zonedDateTime = Temporal.ZonedDateTime.from("2020-08-05T20:06:13+09:00"); // throw RangeError console.log(zonedDateTime.toString()); // 2020-08-05T11:06:13Z Plain 系のクラスでも同様の結果です。以下は PlainDate と PlainTime の例です。 let plainDate = Temporal.PlainDate.from( "2020-08-05T20:06:13+09:00[Asia/Tokyo]" ); console.log(plainDate.toString()); // 2020-08-05 plainDate = Temporal.PlainDate.from("2020-08"); // throw RangeError let plainTime = Temporal.PlainTime.from( "2020-08-05T20:06:13+09:00[Asia/Tokyo]" ); console.log(plainTime.toString()); // 20:06:13 plainTime = Temporal.PlainTime.from("20:06:13"); console.log(plainTime.toString()); // 20:06:13 plainTime = Temporal.PlainTime.from("20"); console.log(plainTime.toString()); // 20:00:00 いずれにしても、それぞれの日時オブジェクトを表現するために必要な情報を from メソッドの引数に ISO 8601 形式ベースの文字列で渡す必要があります。 日時文字列をフォーマット こちらのユースケースも頻出だと思いますが、残念ながら日時ライブラリでお馴染みの以下のようなフォーマット方法は用意されていません。 import { format } from "date-fns"; format(new Date(2016, 0, 1), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); // 2016-01-01T00:00:00.000+09:00 Temporal で実現できるのは以下の 3 点です。 各日時オブジェクトの toString() のオプションを使う DateTimeFormat を使う 自力でフォーマットする 各日時オブジェクトの toString() のオプションを使う こちらはすでに出てきた toString() にオプションを渡す方法です。ただ、ISO 8601 形式を逸脱することはできず、タイムゾーンの表示や秒や分部分の調整ができるだけです。 let instant = Temporal.Instant.from("2020-08-05T20:06:13+09:00[Asia/Tokyo]"); console.log(instant.toString()); // 2020-08-05T11:06:13Z console.log(instant.toString({ timeZone: "Asia/Tokyo" })); // 2020-08-05T20:06:13+09:00 console.log(instant.toString({ smallestUnit: "minute" })); // 2020-08-05T11:06Z console.log(instant.toString({ smallestUnit: "second" })); // 2020-08-05T11:06:13Z console.log(instant.toString({ smallestUnit: "millisecond" })); // 2020-08-05T11:06:13.000Z DateTimeFormat を使う 日時オブジェクトの toLocaleString() メソッドで Intl の DateTimeFormat を使うことができます。ただ、こちらも DateTimeFormat の範囲内でフォーマットするので、完全に自由なフォーマットができるわけではありません。 let instant = Temporal.Instant.from("2020-08-05T20:06:13+09:00[Asia/Tokyo]"); console.log( instant.toLocaleString("ja-jp", { year: "2-digit", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", }) ); // 20/08/05 20:06:13 console.log( instant.toLocaleString("ja-jp", { year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", }) ); // 2020/8/5 20:06:13 自力でフォーマットする こちらは Temporal の日時オブジェクトから得られる年月日や時間の情報から自力で組み立てる方法です。このとき使えるオブジェクトは ZonedDateTime か Plain 系のオブジェクトであり、Instant はそのままでは使えません。Instant は exact time のみを扱うので、年月日や時間、およびタイムゾーンの情報が存在しません。 const zonedDateTime = Temporal.ZonedDateTime.from("2020-08-05T20:06:13+09:00[Asia/Tokyo]") console.log(zonedDateTime.year) // 2020 console.log(zonedDateTime.month) // 8 console.log(zonedDateTime.day) // 5 console.log(zonedDateTime.hour) // 20 console.log(zonedDateTime.minute) // 6 console.log(zonedDateTime.second) // 13 console.log(zonedDateTime.offset) // +09:00 console.log(zonedDateTime.timeZone.toString()) // Asia/Tokyo フォーマットに必要な情報はすべて取得できますが、実際にフォーマットする場合はゼロ埋めなどの処理も必要になってくるので、毎回手動でフォーマットしていくのは厳しいですね。関数などに切り出す必要がありそうです。 おわりに Temporal の概要と使い方について簡単に説明しました。日時処理のユースケースは他にもまだまだあると思いますが(例えば、日時オブジェクトの変換・比較、タイムゾーンの変換、日時の差分取得など)、Temporal の概要を理解していればドキュメントをみて調べることができると思います。日本語訳はされていませんが、ドキュメントにクックブックがあるので、そちらも目を通すとよさそうです。個人的は Temporal を通して、日時処理を学習できたことがよかったです。Temporal を使わなくても Temporal のドキュメントは非常に有用だと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

喝! あっぱれ! サンモニ張本氏ARを作る

喝! あっぱれ! は今年まで 日曜日の朝といえば「サンデーモーニング」 名物コメンテーターの張本勲さん。 自分の意見を言うのが苦手な私。 各ニュースに対して偏見全開で「喝!」「あっぱれ!」と 小気味よく叫ぶ姿が印象的で、好きでした。 なんと年内で卒業とのこと。 このまま「喝」「あっぱれ」がなくなるのは寂しいので、 私が張本氏の後を継いでみようと考えました。 喝! あっぱれ! ひとりサンモニやってみた やりたいこと  ①「喝」「あっぱれ」という声に反応してテロップを出す  ②「喝」はインパクトを出すためにARで3Dテロップにする できたものはこちら 【音声有りで見てください?】 遊び方 ①「喝 あっぱれサイト」を画面の右下当たりに準備 ②「張本なりきりブラウザ」をスマホで立上げる サイト・ブラウザはこちら 張本なりきりブラウザQRコード ③「喝あっぱれサイト」の開始ボタンを押し 「喝」 「あっぱれ」 と叫ぶ ④「喝」に反応してARの3Dモデルが立ち上がる (どうかゆくっり話してあげてください。。。) 作り方 ①ARモデルを作成 喝のモデルなどあるわけないので(結構探しましたが)、自作することに。 Blenderというソフトで作ろうとしたが、そもそもPCにダウンロードできず。 他の方法を模索したところ、ペイント3Dでglbファイルを作れることが判明! 10分くらいでめちゃくちゃ簡単にできました!! しかしマーカー上に出してみると問題が・・・ PC画面にマーカーを表示させると マーカーの真上にモデルが出るため モデルが正面を向きません そこでデフォルトでモデルを寝かせ、 ARが画面上で立ち上がった時には正面を向くようにしました。 ②ARサイトの作成 CodePenで構築 3DモデルをNetlifyにデプロイし URLを取得 そのURLとglbファイルをHTMLの11行目に入れ込みます gltf-model="httpsから始まるNetlifyで取得したURL/glbファイル名" See the Pen 張本なりきりブラウザ by okinakamasayoshi (@okinakamasayoshi) on CodePen. ③音声認識サイトの作成 こちらもCodePen JSのコードでif文を構築  52行目 「かつ」と認識したら 「hiroマーカーの画像」  64行目 「あっぱれ」なら   「張本氏のあっぱれ画像」 if (e.results[0][0].transcript === 'かつ') { let newwin = open('https://raw.githubusercontent.com/AR-js-org/AR.js/master/data/images/hiro.png'); } See the Pen 張本ゾーン by okinakamasayoshi (@okinakamasayoshi) on CodePen. 知り合いに使ってもらった結果 「・・・・」 「すごいけど 俺はいらん」 でも、でもですよ、子供とか喜びそうじゃないですか? と食い下がった結果。 確かにこどもは喜ぶ LINEのビデオ通話も 内臓AR機能で遊んだりしている 自分が作った3Dモデルがその中で出てきたら 喜ぶかも オリジナルの3Dが作れるのはすごいね。 と意見をもらいました。 もう1名はノリノリで「かつだっ!! か~っつっ!!!」 と叫んでくれたので、音声認識がちゃんと働きませんでした。 「反応しないw」で盛り上がれてよかったです。手作り万歳。 この先やってみたいこと 初心者がたった数日で、ARを作ることができて感動しました。 それだけに、夢はまだまだ広がります!! ①「あっぱれ」もARで出現させてみたい ②声を検知して自動でスクリーンショット撮影したい ③「喝」「あっぱれ」と叫んだ回数を自動でカウントしてみたい そして究極は・・・ ARで張本氏自身を出現させてみたい こんなことできたらめちゃくちゃ楽しいですよね♪ ほんのすこ~しでも「あっぱれ」と思ったらLGTMお願いします(^▽^)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonユーザーのためのTypeScript入門

はじめに この記事はそれなりにPythonを使ったことのある人間がとりあえずTypeScriptを書き始めることが出来るレベルになることを目標としています。 Python側の事情もそこそこに書いたのでもしかしたらTypeScriptユーザーのためのPython入門にもなる、かもしれません。 Why Python and TypeScript? ヨーロッパ圏の言語を勉強する際に似た系統の言語を一緒に覚えると学習効率がいいという話を聞いたことがありませんか? プログラミング言語も同じで、新しい言語を学習する際は似た言語に絡めて覚えると習得が早いような気がしています。 近年PythonはTypeScriptを参考に型システムを拡張しており近しい仕組みが数多く存在します。またそれ以外にも同じスクリプト言語であり、クラスベースであり、関数が第一級オブジェクトであり、総合的に見て似たような言語だと言えそうです。ユースケースも一部被ってますしね。 なので今回はこの2つの言語を例に説を立証してみようと思いました。 被検体は私(Python歴3年、TS歴3ヶ月)、そしてあなたです。よろしくお願いします。 開発環境 実行環境 Pythonみたいにとりあえずサクッとコンソールで動かせる環境が欲しい場合はnode + ts-node(後述)がいいかと思います。 ただじゃあ早速node.jsをインストールして…というのはちょっと待ってください。 あなたはそれをやってPythonで痛い目を見たことがありませんか? 実行環境のバージョン管理 これです。どうせ実行環境は複数用意することになるので最初から入れておきましょう。 現状のデファクトスタンダードはnvmですが最近はVoltaも人気っぽいですね。 パッケージ管理 npmとyarnという二大派閥があります。PipenvとPoetryみたいなもんです。 とりあえず作るぞ というわけでとりあえず最もシンプルな構成(nvm + node + npm + ts-node)で実行環境を作っていきます。 まずnvm。UNIX系の方は以下の指示に従ってスクリプトを実行してください。 Windowsはここから最新のnvm-setup.zipをダウンロードしてインストールしましょう。 以下は共通です。node + npm(+ npx)のインストール。 $ nvm install lts # 安定バージョンのインストール。最新バージョンを入れたい場合は`latest` $ nvm use xx.xx.x # ↑のコマンドを実行したときに指示がなければ必要なし プロジェクトのフォルダを作ったら移動してtypescriptとts-nodeをインストール。 $ npm init -y $ npm install typescript ts-node -D # dev-dependencies Hello, world! $ npx ts-node > console.log("Hello, world!") Hello, world! こんにちは、世界! 余談: ts-nodeってなに? お気付きかと思いますが実はnvmもnodeもnpmもTypeScriptではなくJavaScriptのお話でした。 試しに適当な場所でJSのハロワをしてみましょう。 $ node > console.log("Hello, world!") Hello, world! 出来ましたね。 本来TSを実行する際は.tsファイルから.jsファイルを生成してnodeに与える必要があるのですが、 それをショートカットするためプロジェクトにインストールするものがts-nodeです1。 よってts-nodeを入れたプロジェクト以外では直でTSを使うことは出来ないのでご注意ください。 エディター(IDE) / リンター / フォーマッター この3つはもう切っても切れない関係なのでセットで考えることをお勧めします。 つまりこのリンター/フォーマッターは自分が使っているエディターで動くのか?ということを考えましょうということですね。 PythonマンはVS Codeを使っていることが多いはずなのでここではVS Codeでリンター/フォーマッターを動かすことを考えます。 リンター リンターはESLintの一強です。 $ npm install eslint -D 入れたらまず設定ファイルを書かねばならないのですが、ひとまず置いておいて次へ進みましょう。 フォーマッター フォーマッターについては議論があります。ESLintのフォーマット機能を使うか、prettierを使うかです。 prettierは細かいカスタマイズを極力許さない思想が特徴のフォーマッターであり、分かりやすく言えばPythonにおけるblack的なポジションです(blackほど厳しくはないですが)。 私はblackの信徒なのでprettierを入れます。 $ npm install prettier -D blackとflake8がぶつかるように、prettierもESLintとぶつかることがあるのでこれを解消するためにeslint-config-prettierも入れます。 $ npm install eslint-config-prettier -D 設定ファイル 設定ファイルの作成を後回しにしたのはフォーマッターに何を使うかによって設定が変わるためです。 具体的に言うと $ npx eslint --init コマンドでESLintと対話を始めた際の最初の質問、 How would you like to use ESLint? に対する答えがESLintを使う場合は > To check syntax, find problems, and enforce code style に、prettierを使う場合は > To check syntax and find problems となります。 その他の問いに対しては今回はこんな感じになるかと↓ これでプロジェクトのルートに.eslintrc.ymlが生成されますが、prettierを使う場合はさらにもうひと手間。 ./eslintrc.yml extends: # この項目の最後に - eslint:recommended ... - prettier # これを追加します また同じくルートに.prettierrc.ymlを作ることでprettierrcの設定が可能ですが、前述の通りprettierは細かいカスタマイズを良しとしないため極力項目は少なくしましょう。 ./.prettierrc.yml printWidth: 100 semi: false # 行末のセミコロンだけは許せないので 参考: TypeScript プロジェクトに ESLint を導入する - まくろぐ TypeScript コードを Prettier で自動整形する - まくろぐ そして最後にもう1つ重要な設定ファイルを作ります。 $ npx tsc --init # 成功するとルートにtsconfig.jsonが生成されます これが何かとか、どんな設定があるのかとかは今は考えなくて大丈夫です。調べるとげんなりしちゃうので。 ただしこのコマンドだけは今やっておかないとnull安全でないTypeScriptを書くことになるので必ず実行しておいてください。 VS Codeの設定 ESLint、prettierそれぞれの拡張機能をインストールして設定を書きます。 ./vscode/settings.json { "typescript.format.enable": false, "[typescript]": { "editor.formatOnSave": true, "editor.formatOnType": true, "editor.formatOnPaste": true, }, } "typescript.format.enable": falseはVS Code標準のTSフォーマッターを切る設定なので必須、下3行はお好みです。 適当なファイルを作って正しく動作しているか確認してみましょう。 OK. prettierが正しく動いているかどうかは設定ファイルを弄ってみると分かりやすいです。 /.prettierrc.yml semi: true # 変更 この状態でsample.tsを保存すると… 大丈夫ですね。 参考: VS Code のフォーマッターで自動整形する (editor.formatOnSave) - まくろぐ デバッグ Pythonを書いていたときは何も考えずにF5を押せばデバッグが出来ていたと思いますが、TSの場合は少しだけ設定が必要です。 多くの人がデフォルトのまま使っているであろうPythonプロジェクトのlaunch.json { // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Python: Current File", "type": "python", "request": "launch", "program": "${file}", "console": "integratedTerminal" } ] } node+ts-nodeのlaunch.json(./.vscode/launch.json) { "version": "0.2.0", "configurations": [ { "name": "TypeScript: Current File", "type": "pwa-node", "request": "launch", "program": "${file}", "console": "integratedTerminal", "runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"], "skipFiles": ["<node_internals>/**", "${workspaceRoot}/node_modules/**"], } ] } Pythonの時に比べて増えたのは下2行ですね。これでts-nodeを経由してnodeでデバッグすることができます。 ブレークポイントも設定可能です。 テンプレート ここまでの内容をまとめたテンプレートリポジトリを用意しました。 各ファイルの置き場所確認などにご活用ください。 基本文法 ここからはいよいよ仕様や文法の比較に入ります。まずは構造化プログラミングにおける基本の基から。 なお以降JSの機能もまとめてTSと表記します(呼びわけが面倒なので)がその点ご容赦ください。 if Pythonではif文を1行で書くとblackに殴られますがprettierには怒られません。 py import random if random.random() > 0.5: print("big") # ng(blackが許さないだけで文法的に間違いではありません) if random.random() > 0.5: print("big") ts if (Math.random() > 0.5) { console.log("big") } // ok if (Math.random() > 0.5) console.log("big") 論理演算子 短絡評価であり最後に評価した値を返す仕様はTSも同様です。 py print(0 and 1) # falseyな「値」が返る > 0 print(0 and "") # 両辺がfalseyの場合は左辺が返る > 0 print(1 and "a") # 両辺がtruthyの場合は右辺が返る > a ts console.log(0 && 1) > 0 console.log(0 && "") > 0 console.log(1 && "a") > a py print(0 or 1) # truthyな「値」が返る > 1 print(0 or "") # 両辺がfalseyの場合は右辺が返る > print(1 or "a") # 両辺がtruthyの場合は左辺が返る > 1 ts console.log(0 || 1) > 1 console.log(0 || "") > console.log(1 || "a") > 1 py print(not (1 or "a")) # 否定をつけるとbool値になる > False ts console.log(!(1 || "a")) > false truthy / falsey Pythonでは自作オブジェクトの真偽を自分で決められましたが、TSにおいてオブジェクトは常に真です。 つまり何がtruthyで何がfalseyかは全て事前に決まっているということですね。 TypeScript Deep Dive 日本語版様より便利な表を引用します。 変数の型 falsyな値 truthyな値 boolean false true string '' (空文字列) その他の文字列 number 0 NaN その他の数値 null 常にfalsy なし undefined 常にfalsy なし その他のオブジェクト({}や[]といった空のものも含む) なし 常にtruthy せっかくなので残りのプリミティブ型も表に加えましょう。 (説明が前後しますがTSにおける値はプリミティブ型とオブジェクト型とに大別され、プリミティブ型はここに挙げた8種で全てです。) 変数の型 falsyな値 truthyな値 function なし 常にtruthy bigint 0n その他の数値 symbol なし 常にtruthy だいたいPythonと同じですね。[]や{}がtruthyなことだけ注意が必要そうです。 比較演算子 TSの比較演算子は事実上1つしかないので若干不便です。 py hoge = [] fuga = [] print(hoge == fuga) # ==はオブジェクトが等価であるかを見るためtrue > true print(hoge is fuga) # isはオブジェクトが同一であるか(参照値が同じか)を見るためfalse > false ts const hoge = [] const fuga = [] console.log(hoge === fuga) // ===でオブジェクト同士を比較する場合は同一であるかを見る(Pythonのisと同じ) > False console.log(1 === 1) // プリミティブ同士を比較する場合は等価であるかを見る(Pythonの==と同じ) > True じゃあオブジェクト同士が同じ値を持つかどうかはどうやって?という感じなんですが、 どうやらdeep-equalというサードパーティ製ライブラリを使うらしい…。 本当ですか?どなたか他の方法をご存じでしたら教えてください。 三項演算子 PythonとTSでは式の順番が異なりますがやることは同じです。 py import random "big" if random.random() > 0.5 else "small" ts Math.random() > 0.5 ? "big" : "small" for PythonのforはTSで言うとfor...ofになります。 py list_ = ["hoge", "fuga", "piyo"] for i in range(len(list_)): print(i) > 0 > 1 > 2 for elm in list_: print(elm) > hoge > fuga > piyo for i, elm in enumerate(list_): print(i, elm) > 0 hoge > 1 fuga > 2 piyo ts const list_ = ["hoge", "fuga", "piyo"] for (let i = 0; i < list_.length; i++) { console.log(i) } > 0 > 1 > 2 for (const elm of list_) { console.log(elm) } > hoge > fuga > piyo list_.forEach((elm, i) => { console.log(i, elm) }) > 0 hoge > 1 fuga > 2 piyo // {}は省略してもok for (let i = 0; i < list_.length; i++) console.log(i) for (const elm of list_) console.log(elm) list_.forEach((elm, i) => console.log(i, elm)) TSのループ文はたくさんあるので詳しくはこちらをご確認ください。 ちなみにPythonで言うfor...elseに該当する構文はありません2。 変数 定義 ここはさすがにベースが弱い動的型付けであるPythonと言語仕様として強い静的型付けであるTSとの差を感じます。 py hoge = "hoge" # 変数は突然代入してよい hoge = 1 # ok fuga: str = "fuga" # fugaをstr型として定義したので fuga = 2 # これはng from typing import Final piyo: Final = "piyo" # 再代入を禁止する書き方も一応あります piyo = "piyopiyo" # ng ts let hoge = "hoge" // この定義でhogeはstring型になる(型推論)ので hoge = 1 // 異なる型は代入できなくなる let fuga: string = "fuga" // 上記の理由から`: string`を書く必要がないのでこの書き方はng const piyo = "piyo" // 再代入禁止 piyo = "piyopiyo" // ng 基本的には全部constで定義してどうしても再代入したい場合だけletを使うといいと思います。 あとvarというのもあるのですが後方互換のために残してあるだけなので忘れてください。 スコープ Pythonのスコープを切るものはモジュール/クラス/関数の3つ、TSのスコープを切るものはブロック3というとなんか違うように感じますが実際にはPythonよりちょっと狭いくらいで基本的な考え方は一緒です。 py list_ = ["hoge", "fuga", "piyo"] for elm in list_: elm print(elm) # elmは生きてるのでエラーにならない(今確認してみたらPylanceには怒られるようになってました。いつから?) > piyo ts const list_ = ["hoge", "fuga", "piyo"] for (const elm of list_) { elm } console.log(elm) // elmはブロック内で死んでるのでエラー 双方ともスコープ外の変数は巻き上げられる。 py def print_hoge() -> None: print(hoge) hoge = "hoge" print_hoge() > hoge ts function logHoge(): void { console.log(hoge) } const hoge = "hoge" logHoge() > hoge トップレベルで定義したものの扱いだけが若干面倒で、 そのファイル(.ts)内にimportもしくはexportが1つでも書かれている場合はモジュールレベル(他のファイルから見えない) 1つもない場合はグローバル(他のファイルから見える) になります。 ↑ hogeとlogHogeがよそから見えている ↑ hogeだけが見えていてlogHogeが見えなくなった Pythonでバシバシトップレベルで定義することに慣れている方はご注意ください。 参考: ES2015のモジュール管理 | Think IT 関数 定義 py def log(msg: str) -> None: print(msg) ts function log(msg: string): void { console.log(msg) } 基本構文は同じですね。TSの方は戻り値がvoidと書いてありますが実際にはundefinedが返ります。 色々な引数 py # オプション引数 def log1(msg: str | None = None) -> None: print(msg) # デフォルト引数 def log2(msg: str = "hoge") -> None: print(msg) # キーワード引数 def log3(*, msg: str) -> None: print(msg) # 可変長引数 def log4(*msgs: str) -> None: print(msgs) # 可変長キーワード引数 def log5(**msgs: str) -> None: print(msgs) ts // オプション引数 function log1(msg?: string): void { console.log(msg) } // デフォルト引数 function log2(msg = "hoge"): void { console.log(msg) } // キーワード引数 function log3(option: { msg: string }): void { console.log(option.msg) } // 可変長引数 function log4(...msgs: string[]): void { console.log(msgs) } // 可変長キーワード引数 function log5(option: object): void { console.log(option) } 引数の渡され方 関数の仮引数には何が渡されるかと言うと、これもPythonと同じく参照値が渡されます。 参照の値渡しとか共有渡しとか呼ばれるアレです。 よってオブジェクトを渡した場合には副作用が生じる可能性があります。 py def log(msgs: list[str]) -> None: msgs.append("piyo") print(msgs) msgs = ["hoge", "fuga"] log(msgs) > ["hoge", "fuga", "piyo"] log(msgs) > ["hoge", "fuga", "piyo", "piyo"] ts function log(msgs: string[]): void { msgs.push("piyo") console.log(msgs) } const msgs = ["hoge", "fuga"] log(msgs) > [ "hoge", "fuga", "piyo" ] log(msgs) > [ "hoge", "fuga", "piyo", "piyo" ] 無名関数 Pythonのlambdaは引数に型を書くことが出来ないのですが、TSはアロー関数の引数にも型をつけることが出来ます(実際には型推論が優秀であまり書くことはないですが)。 py lambda x: print(x) ts (x: string) => console.log(x) 高階関数との組み合わせ↓ py seq = [1, 2, 3, 4, 5] print(list(filter(lambda x: x % 2 == 0, seq))) > [2, 4] ts const seq = [1, 2, 3, 4, 5] console.log(seq.filter((x) => x % 2 === 0)) > [ 2, 4 ] nullとundefined どっち使えばいい? MSのTypeScriptチームはnullを使わないそうです。 実際のところ 上記に倣ってnullは使わない、ただしnullチェックはif (tgt != null)で行うというプロジェクトが多いみたいです(==はnullとundefinedを区別しないため)。またこれがTSで==演算子を使う唯一の機会でもあります。 nullにまつわる構文 最近のTSはかなりnull周りが書きやすくなっている印象があります。 以下の構文は両方ともPythonには存在しません。 ts // オプショナルチェーン function len(text?: string): void { const len = text?.length // textがundefinedの場合undefinedになる console.log(len) } // Null合体代入演算子 function echo(text: string, options: { big?: boolean; trim?: boolean }): void { options.big ??= true // bigがundefinedの場合trueを代入 options.trim ??= true let processed = text if (options.big) processed = processed.toUpperCase() if (options.trim) processed = processed.trim() console.log(processed) } 列挙体(enum)とunion型 これもどちらを使うべきかという議論がありますが、私が調べた限り さようなら、TypeScript enum | Kabuku Developers Blog これが結論なように見えました。 ちなみにここで触れた通りPythonのLiteral型は反復処理ができないため全てのEnumを駆逐するまでには至りません(頑張ってこねこねすればいけそうですが…)。 クラス 定義 py from dataclasses import dataclass @dataclass class Player: name: str age: int def rename(self, name: str) -> None: self.name = name me = Player("nicco", 29) ts class Player { constructor(public name: string, public age: number) {} rename(name: string): void { this.name = name } } const me = new Player("mirai", 14) Pythonの@dataclassはフィールドの宣言を元に__init__やその他特殊メソッドを自動で生成してくれるデコレータですが、TSでは逆にコンストラクタの引数にアクセス修飾子をつけることによってフィールドの宣言を省略することができます。 またメソッド内でインスタンス変数にアクセスするためのselfはTSではthisになります。 公称型と構造的部分型 PythonとTSはどちらも公称型と構造的部分型の両方を扱うことのできる言語です。 ただしPythonは構造的部分型も扱うことが出来る公称型がベースの言語であり TSが公称型も扱うことが出来る構造的部分型がベースの言語であることは押さえておく必要があります。 それはなぜか。我々のような公称型に慣れた人間は公称型に準じた設計をしがちであり、そして構造的部分型はその影響範囲が広いからです。 参考: TypeScript: 異なる2つの型システム「公称型」と「構造的部分型」 公称型に準じた設計とは まさに上で書いたようなクラスがそれです。これを構造的部分型に準じた設計に変えてみましょう。 ts interface Player { name: string age: number } // `Player`型を含め`name`属性を持った型ならなんでも受け取れる function rename(named: { name: string }, newName: string) { named.name = newName } const me: Player = { name: "mirai", age: 14 } rename(me, "hikari") こうです。Goでよく見た感じになりましたね。 Protocolを継承しないと構造的部分型にならないPythonと異なり、TSで定義した型4は特定の条件を満たさない限り常に構造的部分型です。 今後{ name: string, age: number, rename: (name: string) => void }が全て公称型に準じた型であるPlayerと見なされてしまうのは設計者の意図に反する危ない状態ではないでしょうか? じゃあどうするのが正解? どちらかに寄せましょう。構造的部分型に準じた設計に変えるか、クラスを公称型で定義するかです。 後者のやり方は単純で、1つ以上パブリックではないフィールドを作ればOKです。 解決策その2.ts class Player { constructor(private name: string, public age: number) {} rename(name: string): void { this.name = name } } class Neko { constructor(private name: string, public age: number) {} rename(name: string): void { this.name = name } } let player: Player player = new Neko("mirai", 2) // 代入できない! 参考: 公称型クラス | サバイバルTypeScript おわりに タイムリープTypeScript 〜TypeScript始めたてのあの頃に知っておきたかったこと〜 カレンダー2日目の記事でした。 あの頃に知っておきたかったということでかなり基本に寄った内容でまとめてみましたがどうでしょうか? 特に環境周りは調べていて昔より随分楽になったんだな~と感じました。 また入門者の方はこれで一巡他の入門記事が読めるようになったのではないでしょうか。 おすすめは@uhyo氏のTypeScriptの型入門シリーズです。 本当にためになるシリーズなので未読の方はぜひ。 グローバルに入れることもできますがnvmやらnpmを使っている意味が分からなくなるのでやめましょう ↩ というかアレが珍しすぎますね ↩ 正確に言えばletとconstは、ですがvarは使わないので事実上 ↩ TSはクラスを定義すると同時に型も定義されます ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

huskyとlint-stagedを使ってlint errorが発生していたらcommitできないようにしよう

パーソンリンクアドベントカレンダー6日目の記事です! 前提 eslintは導入済み 以下のようなディレクトリ構成で行いました。 . ├── front └── server 手順 まずfront配下でライブラリのインストール yarn add -D husky lint-staged huskyの初期化コマンドの実行 npx husky-init && yarn package.jsonをカスタマイズ { "scripts": { ... "lint": "eslint . --ext ts --ext vue --ext js", "prepare": "cd .. && husky install front/.husky", "lint-staged": "lint-staged" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.@(ts|tsx|vue)": [ "yarn lint" ] } } 下記コマンドを実行 yarn prepare 自動生成された.huskyディレクトリ配下にpre-commitというファイルを作成する 下記の様に記述 pre-commit #!/bin/sh . "$(dirname "$0")/_/husky.sh" cd front yarn lint-staged 最後に下記を実行 cp -r ./front/.husky . ポイント package.jsonと.gitのディレクトリが同階層にいないと正常に実行されない為、度々cd frontが挟まれていました。 実際にpre-commitを実行しないと行けないため、最終的にroot階層に.huskyをcpしました。 動作確認 MacBook-Pro:front $ git commit -m "test" yarn run v1.22.17 $ lint-staged ✔ Preparing... ⚠ Running tasks... ❯ Running tasks for *.@(ts|tsx|vue) ✖ yarn lint [FAILED] ↓ Skipped because of errors from tasks. [SKIPPED] ✔ Reverting to original state because of errors... ✔ Cleaning up... ✖ yarn lint: error Command failed with exit code 1. $ eslint . --ext ts --ext vue --ext js /Users/user_name/project/example-project/front/components/common/Container.vue /Users/user_name/project/example-project/front/components/common/Container.vue 1:1 error Component name "Container" should always be multi-word vue/multi-word-component-names ✖ 1 problem (1 error, 0 warnings) info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. husky - pre-commit hook exited with code 1 (error) commit前にyarn lint-stagedが実行されており、lint errorが発生している為、commitできないのが確認取れました。 VSCodeでも実際にエラーが出ていることが確認取れました。 これでコードレビュー時にlint errorに怯えなくて済みます。。。 参考 https://typicode.github.io/husky/#/ https://zenn.dev/seya/articles/c908d88df0a587 https://kic-yuuki.hatenablog.com/entry/2019/05/27/090515
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

console.log(typeof null)の出力結果に自信のない人は読んでください

タイムリープTypeScript 〜TypeScript始めたてのあの頃に知っておきたかったこと〜 の7日目の記事になります。 よろしくお願いします。 問題 突然ですが問題です。 下記のコードの出力結果は何になるでしょうか? console.log(typeof null); 正解は、、、"object" です。 "null"じゃないの?って思った方がこの記事の読者です♪ なぜなのか 初期から存在するJavascriptのバグ1です。 嘘だと思うかもしれませんが、本当なんです。 どういうことなのか説明します。 初期のJavascriptでは、値を32bitで表現していました。 最初の3bitでデータのタイプを表現し、残りのbitでデータを表現していました。 イメージとしてはこんな感じでしょうか。 そして、データのタイプとしては下記の5つが定義されていました。 000: object 1: int 010: double 100: string 110: boolean お気付きでしょうか?nullがありません。 実は、nullはタイプとして定義されておらず、objectタイプのデータがすべて0という形で表現されていました。 そのため、タイプとしては存在しないのです。 イメージとしてはこんな感じです。 ではnullはobjectなのかというと、そういう意図でこの実装になっている訳ではないようです。 ではどういう訳かというと、Javascriptの最初のバージョンで納期に追われて生まれたバグだそうです。2 その後、修正する流れもあったようなのですが、互換性の問題などから修正されずに現在に至るそうです。3 そのため、typeof nullは"object"となる訳です。 以上になります。 最後までお読みいただきありがとうございました。 bugという表現はThe history of “typeof null”の表現を拝借しています。 ↩ The history of “typeof null”の下記の表現を拝借しています。 ↩ This may seem like a very obvious bug, but don’t forget that there was very little time to finish the first version of JavaScript. typeof_nullとリンク先の表現を拝借しています。 ↩ ECMAScript の修正案が (オプトインを使用して) 提案されましたが、却下されました。それは typeof null === 'null' という結果になるものでした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptと仲良くなる(初心者向け)

 TypeScriptと仲良くなる はじめまして、初投稿です。 私は約半年前から現場でReact、TypeScriptを扱うプロジェクトにアサインしています。 当時は、1行改修するにも丸一日かかるようなチュートリアルレベルでした、、 そんな半年前の自分に向けてTypeScriptで躓いた構文をまとめたいと思います。 若干ボリューミーに見えますが、簡単なコードが多いだけですw ① typeの書き方 まずは基本 engineer.ts type Engineer = { name: string age: number sex: string languages: string[] }; const engineer: Engineer = { name: 'ホゲ', age: 25, sex: 'man', languages: ['TypeScript', 'React', 'Java'], }; typeはオブジェクトの型を定義しています。 typeを定義しなくてもオブジェクトは問題なく作れるのですが、 各valueに好きな値を入れられてしまうため、いつの間にか変テコなオブジェクトになっていて なんかしらんけどバグってる。ってことになりかねません。 値の不正代入も防げて、IDEの入力補完も効くので型無しのJavaScriptよりも開発効率が良いと個人的に思います。 ② オブジェクトからtypeを作る engineer.ts const Sex = { man: '男', woman: '女', }; type SexType = keyof typeof Sex; type Engineer = { name: string age: number sex: SexType languages: string[] }; この構文も初めて見たときは何が起きているのかさっぱりでした。 結論としてはsexに入れられる値をmanまたはwomanの文字列だけに制限しています 1つずつ解説 const Sex = { man: '男', woman: '女', }; constで定義されているのでこれ単体だとただのオブジェクトです。 type SexType = keyof typeof Sex; 見慣れない構文keyof、typeofですが、一つずつ見ると typeof Sex; /*     ↓オブジェクトの構造がそのままtypeになる { man: '男', woman: '女', }   */ keyof typeof Sex; /*     ↓typeofでtypeになったSexのkey(文字列)のみを持つtypeになる 'man' | 'woman' */ 文字で説明すると逆にややこしい気がしますが、、 type Engineer = { name: string age: number sex: SexType /* 'man' | 'woman' */ languages: string[] }; const engineer: Engineer = { name: 'ホゲ', age: 25, sex: 'weman' /* 怒られる */, languages: ['TypeScript', 'React', 'Java'], }; もともとstringだったところをSexTypeに置き換えると、 man, woman以外の文字列を入れようとするとコンパイルエラーになります。 コンパイルエラーになる以外にも、 sexがmanだったときに男を画面に表示することもとても楽になります。 console.log(engineer.sex); /* man */ console.log(Sex[engineer.sex]); /* 男 */ 同じようにLunguageTypeを作るのもいいですね ③ 任意の型を追加する Engineerのプロフィールが寂しいので追加で hobbyとfavoriteFoodを任意で設定出来るようにしましょう type Engineer = { name: string age: number sex: SexType languages: LanguageType[] otherInfo: { hobby: string | undefined favoriteFood: string | undefined } }; type Engineer = { name: string age: number sex: SexType languages: LanguageType[] otherInfo: { hobby?: string favoriteFood?: string } }; type OtherInfo = { hobby: string favoriteFood: string }; type Engineer = { name: string age: number sex: SexType languages: LanguageType[] otherInfo: Partial<OtherInfo> }; 3通り書きましたが、typeとしてはすべて同じ内容になります。 3番めのPartial<>ですが、これはUtility Typesの一つで使いこなせると便利なやつです。 詳しくははこちらの方の記事がおすすめです。 【TypeScript】Utility Typesをまとめて理解する ④ typeを繋げる type Name = { name: string; } type Age = { age: number } type Engineer = Name & Age & { sex: SexType languages: LanguageType[] otherInfo: Partial<OtherInfo> }; こんな感じで細かくtypeを定義して&でつなげてひとまとめにできます。 もちろんName、Age以外も同じように定義しておいてすべて&でつなげることも可能です。 おまけ   BrandedType type Engineer = { firstName: string lastName: string age: number sex: SexType languages: LanguageType[] }; nameだったところをfirstNameとlastNameに分ける事になった場合、 どちらも型としてはstringで区別することはできません。 type FirstName = string & { _brand: 'FirstName' }; type LastName = string & { _brand: 'LastName' }; type Engineer = { firstName: FirstName lastName: LastName age: number sex: SexType languages: LanguageType[] }; stringに{ _brand: (型名等) }という他と被らないプロパティを付けることで、区別出来るようになります。 BrandedTypeは自身で把握出来る程度の規模であれば無くてもいいのかなと思いますが、 細部まで型が定められていると、参照が増えたり複雑な構造になった際にもミスを防げるし、自分以外の人が見たときにも分かり易いんじゃないかなと思います。 まとめ 今ではどれも当たり前のように使えるようになりましたが、 当時は本当に分からなくて頭に定着するまで何度も調べていました、、笑 TypeScriptで書くと、開発効率が良くなるのはもちろんですが、他の人が見たときに分かりやすいのもメリットだと思います。← とても大事 (頑張ります) 奥が深くまだまだ知らないこともあるので引き続き頑張ります。 最後まで見ていただきありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】【Vue】 など流行りのフレームワークを使うもののためのオブジェクト指向とは

流行りのフレームワークを使っていると忘れがち(意識しなくてよい)JSの基礎を学び直してみた。 オブジェクト指向とは 直訳するとオブジェクトはモノ。しかしこれではあまりに分かりにくいですよね。 要約すると「現実世界のものに見立ててプログラミングをする方法」になります。 これでも想像つきにくいですよね。つまりこのオブジェクトを使って、わかりやすくプログラミングするよというふわっとした意味らしいです。 ※ { name:"菅田将暉"; age:28; job:"actor" } そして、このオブジェクトを作る方法が2パターンあります。 ★オブジェクトリテラル リテラルというのは文字通りという意味です。これはいつも何気なく使っている書き方です。 const person = { name:"菅田将暉", age:28, job:"actor" } ★クラス クラスというのは ・オブジェクトの設計図 ・クラスから作られたオブジェクトはインスタンスと呼ばれる とりあえず見ていきましょう。 例 class Person { constructor(initName,initAge,initAge) { this.name = initName, this.age = initAge, this.job = initJob } } こういった形でクラスを定義します。 この設計図を使うにはnewというものを用いて、 const masaki = new Person("菅田将暉",28,"actor") と書くと、※で示したオブジェクト同じものが出来上がります。 この設計図からオブジェクトを作ることをインスタンス化と呼び、このmasakiがインスタンスと呼ばれます。(インスタンスという名前は付きましたがオブジェクトです。) 同じもの作るのに、なぜクラスなんか使わなくてはいけないの?と思った方もいるかもしてません。 理由は簡単、クラスは何回も使いまわしできるからです。設計図と呼ばれる所以かもしれません 例えば、 const nana = new Person("小松奈菜",25,"actress") const Mackenyu = new Person("新田 真剣佑",25,"actor") という新しいインスタンスも、簡単に作れてしまうわけです。いまは、オブジェクトのプロパティ(オブジェクトの中の情報)が3つしかないので、そのまま書いてもさほど大変ではありませんが、これが10個もあったりしたら、オブジェクトを3つ作るだけでも、大変ですよね。設計図を書いておけば、情報さえ渡してやれば、いくつも短い記述量でオブジェクトが作れるようになります。 この記事でクラス、インスタンス、コンストラクタあたりを詳しく書いたので、参考にしてみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

美男美女+ゆりやんレトリィバァを使って解説するオブジェクトとは

流行りのフレームワークを使っていると忘れがち(意識しなくてよい)JSの基礎を学び直してみた。 オブジェクト指向とは 直訳するとオブジェクトはモノ。しかしこれではあまりに分かりにくいですよね。 要約すると「現実世界のものに見立ててプログラミングをする方法」になります。 これでも想像つきにくいですよね。つまりこのオブジェクトを使って、わかりやすくプログラミングするよというふわっとした意味らしいです。 ※ { name:"菅田将暉"; age:28; job:"actor" } そして、このオブジェクトを作る方法が2パターンあります。 ★オブジェクトリテラル リテラルというのは文字通りという意味です。これはいつも何気なく使っている書き方です。 const person = { name:"菅田将暉", age:28, job:"actor" } ★クラス クラスというのは ・オブジェクトの設計図 ・クラスから作られたオブジェクトはインスタンスと呼ばれる とりあえず見ていきましょう。 例 class Person { constructor(initName,initAge,initJob) { this.name = initName, this.age = initAge, this.job = initJob } } こういった形でクラスを定義します。 この設計図を使うにはnewというものを用いて、 const masaki = new Person("菅田将暉",28,"actor") と書くと、※で示したオブジェクト同じものが出来上がります。 この設計図からオブジェクトを作ることをインスタンス化と呼び、このmasakiがインスタンスと呼ばれます。(インスタンスという名前は付きましたがオブジェクトです。) 同じもの作るのに、なぜクラスなんか使わなくてはいけないの?と思った方もいるかもしてません。 理由は簡単、クラスは何回も使いまわしできるからです。設計図と呼ばれる所以かもしれません 例えば、 const nana = new Person("小松奈菜",25,"actress") const Mackenyu = new Person("新田 真剣佑",25,"actor") という新しいインスタンスも、簡単に作れてしまうわけです。いまは、オブジェクトのプロパティ(オブジェクトの中の情報)が3つしかないので、そのまま書いてもさほど大変ではありませんが、これが10個もあったりしたら、オブジェクトを3つ作るだけでも、大変ですよね。設計図を書いておけば、情報さえ渡してやれば、いくつも短い記述量でオブジェクトが作れるようになります。 この記事でクラス、インスタンス、コンストラクタあたりを詳しく書いたので、参考にしてみてください。 ・クラスの継承 例えば、芸人さんの設計図 [class Comedian]を作るとします。 class Comedian { constructor(initName,initAge,initJob,initCompany) { this.name = initName, this.age = initAge, this.job = initJob, this.company = initCompany } } これでもクラスは作ることができますが、同じようなことを二回書くのはナンセンスですよね。 そこでクラスには、継承という機能があります。 class Comedian extends Person{ } これでPersonクラスを継承できました。 class Comedian extends Person{ } const suda = new Comedian("菅田将暉",28,"actor") こう書けば、※と同じオブジェクトができます。(クラスは芸人さんですが。) このPersonクラスを継承して所属事務所を付けたすと class Comedian extends Person{ constructor(initName,initAge,initJob,initCompany) { // 親クラスのコンストラクタを呼び出す super(initName,initAge,initAge,); this.company = initCompany } } こうかく そしてインスタンス化すると const yuriyann = new Comedian("ゆりやんレトリィバァ",31,"comedian","吉本興業") と書くことができます。 これは const yuriyan = { name: "ゆりやんレトリィバァ", age: 31, job: "comedian", company: "吉本興業", }; というオブジェクトと同じものが出来上がります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「MiNERVA」コロナ禍での学祭対面開催に向けた、情報系大学生の奮闘記

この記事は、法政大学アドベントカレンダー2021の2日目の記事です。 本記事は、大学を批判するものではありません。 今回の活動は全て大学の全面的な協力があってこそでした、ありがとうございました。 三行で表す コロナ禍で学祭を運営するにあたり、入室管理が必要になりました。 紙で記録するのは追跡が面倒なので、QRコードで管理できるシステムを作りました。 作ったら大学に褒められたので、図にのってQiitaにも投稿します。 自己紹介 @taikis Twitter( taikis_jp ), GitHub( taikis ) 法政大学でITを学んでいる大学4年生 研究内容は画像認識、機械学習 新入生歓迎会や学祭を運営する団体の代表をやってました 作ったもの QRコードを用い、どんな端末でも簡単にスキャンできる入室管理システム「MiNERVA」を作成しました。 … … …と思ってたらまさかの同じコンセプト、よりモダンなものを作られている方がいらっしゃいました。 こちらの記事もご紹介いたします。 動いている様子 基本的な動き 場所選択画面 スキャン画面 登録成功画面 初回登録未完了時 求められる機能 ベータ版を作り始めたのは、新入生歓迎会の時でした。 2020年度の新入生歓迎会は、対面で実施ができなかったため、2021年度がコロナ禍で初めての新入生歓迎会の対面開催となりました。 その際、大学から「来場者(新入生)が、どのブース・教室に立ち寄ったかを、分刻みで記録するように」との指示を受けました。 コロナ禍の中、その指示は的確なもので、もっともなことです。 ただし、新入生は1200人超、この量を紙で管理するのはアホのやることとても大変です。 来場者管理に必要なものとして、以下の三つが挙げられます。 ① 有事の際に簡単にデータを分析できること ② 個人固有のID ③ 簡単に打刻ができること ① 有事の際に簡単にデータを分析できること 先述したように、紙で管理するととてつもない量になります。 新入生の人数が1200人を超え、1人3団体のブース見て回るとしても3600行を手で探す、あるいはエクセル等に落とし込まないといけません。 無理です。 ここで、何らかの情報技術を使うことが確定しました。 ② 個人固有のID 名前を使うことが考えられますが、空白や、漢字の表記ぶれがあるため適しません。 では、学籍番号を使えるのでは?と思いますよね。 3月の私と大学のやりとりを見てみましょう 私 : 学籍番号で管理したいんですけどいいですか? 大学 : 学籍番号の発行は新歓期間中なので無理です 私 : じゃあ予約フォーム作って整理番号発行するので、メール流してください 大学 : 新入生のメアドの発行まだなのでできません 私 : 前日は流石に発行されてますよね…? 大学 : 3月末からシステム更新で事務のPC使えないので無理です 私 : じゃあどうしたらいいんですか? 大学 : 紙で管理してください!(満面の笑み) 私 : 当日にならないと誰が来るかわからないため、登録→整理番号発行を当日行う必要が起きました。 この時点で、メール配信が必要になりました。 ③ 簡単に打刻ができること 実際に打刻をしてくれるのは、各サークルなんですよね。 打刻が番号を入力するとかめんどくさいと、従ってくれない可能性があります。 手軽にささっと打刻できて、これならやってやってもいいかなと思えるもの。これがQRコードスキャンでした。 しかし、QRコードスキャナーを団体分配布するわけにもいかないため、スマホを用いることになります。ネイティブアプリの開発は、OS分作らなければならず、開発費用もかかります。そのため、ブラウザ上で動くWebアプリを作成することになりました。 ベータ版でうまくいかなかったこと 新入生歓迎会で運用したベータ版でうまくいかなかったことをもとに、学祭で大幅アップデートを行いました。その記録を残します。 ① GASのメール送信上限を知らなかった 整理番号発行→QRコードの生成→メール送信はGoogle フォームとGoogle App Scriptを使用していました。 メールの送信上限が100件なので、新歓期間中に上限超えました。 途中からメールが完全に送信されなくなる恐怖は今でもトラウマです。 外部のサービスを使う際は、制限や仕様をよく確認しなければですね。 ② 画面上のQRコード晴天下で反射して読めない メールでQRコードが送られるため、スキャナーでそれを読み込む仕様でした。 晴天下では、日光が画面に反射してスキャナーで読み込めないという声が多数上がりました。 実地で読み込んでいれば、こういった不具合は想定することが難しかったでしょう。実地でのテストが必要と思い知らされ、改良版ではQRコードのカードを配ることにしました。 工夫した点 ① Webブラウザで動くようにした 先述した通り、ネイティブアプリを作るにはコストがかかりすぎるため、Webブラウザで完結するシステムにしました。 QRコードを読み取るにあたり、下記の記事を参考にさせていただきました。 使用したライブラリ 全ての端末で確実に動かしたかったため、100人以上の委員を動員し、数十種の端末で徹底したテストを行いました。 結果的に動作不能端末が見つからなく、当日バグも一切なくなったため、成功と言えるでしょう。 ② 読み込む人によってQRの遷移先を変えた 新歓の反省から、来場者にQRコードが書かれた紙を渡すことにしました。 QRコードのURLを下記のようにすることで、来場者は整理番号と個人情報の紐付けページへ飛ぶことができます。 URL https://example.com/form?number=123456 実際読み込んだ場面 MiNERVAのQRコードリーダーでは、正規表現を使うことでURLではなく整理番号のみ抽出し、DBに登録します。 正規表現 url.match(/number=(\d{6})/); 一つのQRコードを標準カメラで読み込むと初回登録画面へ、MiNERVAのスキャナーで読むと場所の登録ができるようになりました。 ③ 初回登録をしていないユーザーは、場所登録ができないようにした 初回登録って時間かかるんですよね。ただ登録まで見張っていると、時間がかかって滞留してしまいます。 感染対策のシステムで密になるのは本末転倒なので、初回登録をしてなければどのブースにも立ち寄れないことにしました。 初回登録していないとこの画面になります。 この機能により、入場口での混雑を減らすことに成功しました。 最後に 今年の学祭は、何としてでも対面で開催しなければいけませんでした。 一般の学生団体は3年生で引退します。コロナ禍から2年がたち、今年対面で会うことができなければ多くの伝統が消えることになってしまいます。 コロナ対策と課外活動。この二つを相反させないために、我々は自分にできる全てのことを行いました。 私がやったことが、今後評価されたら嬉しいです。 関連リンク 参考文献・使用させていただいたライブラリ等 明日は@reud さんの「OpenAPI Generatorでハッカソンを楽しむ」です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】Proxyの使い方

はじめに Udemyの【JS】ガチで学びたい人のためのJavaScriptメカニズムの講座の振り返りです。 前回の記事 目的 Proxyについての理解を深める 本題 1.Proxy Proxyというのはプロパティーの操作に独自の処理を加えるオブジェクトのこと 例1 基本的な使い方(set) main.js // オブジェクトを変数に格納 const targetObj = { a: 0 }; const handler = { // setというメソッドを登録する(トラップとも呼ばれる) // 第一引数にはtargetObjが渡ってくる // 第二引数にはプロパティにアクセスされた際のプロパティの名前が渡ってくる // 第三引数にはsetの場合には新しい値が渡ってくる // 第四引数はnew Proxyのオブジェクト(proxy)が渡ってくる set: function (target, prop, value, receiver) { // Proxy経由で値が呼ばれた際に、setのトラップが呼ばれているのか確認する console.log(`[set]: ${prop}`); // 下記を定義することで新しい値を設定できるようにする(a = 1と表示される) target[prop] = value; } } // new演算子でProxyを定義し変数に格納 // 第一引数に定義したオブジェクトを入れる // 第二引数にはhandlerというtargetObjを操作した際に実行されるメソッドを格納したオブジェクトを入れる(上記) const proxy = new Proxy(targetObj, handler) // ここで出力結果が[set]:aとなるのでproxy経由で呼ばれていることがわかる proxy.a = 1; 例2 基本的な使い方(get) main.js const targetObj = { a: 0 }; const handler = { // getでは値の取得が行われた際の検知ができるようになる // 値の取得なので新しい値は渡ってこない = 第三引数は削除(value) get: function (target, prop, receiver) { console.log(`[get]: ${prop}`); // returnで値を返す return target[prop]; } } const proxy = new Proxy(targetObj, handler) // [get]:aと出力される proxy.a; 例3 基本的な使い方(delete) main.js const targetObj = { a: 0 }; const handler = { get: function (target, prop, receiver) { console.log(`[get]: ${prop}`); return target[prop]; }, // 下記のdeleteでは値が削除された際の検知を行う // deletePropertyの場合にはreceiverは渡ってこないので削除 deleteProperty: function (target, prop) { console.log(`[delete]: ${prop}`); // deleteとして値を削除 delete target[prop]; } } const proxy = new Proxy(targetObj, handler); proxy.a; // delete aと出力される delete proxy.a; // targetObjの中身をコンソールで確認してみるとオブジェクトの中身も削除されている 例4 実用的な例 const targetObj = { a: 0 };に対して値の変更を許可しない場合にはどのようにするべきか main.js const targetObj = { a: 0 }; const handler = { set: function (target, prop, value, receiver) { console.log(`[set]: ${prop}`); // setのトラップが呼ばれた際にErrorが発生するようにする throw new Error(`cannot add prop`) // deleteでも削除不可にできる } } const proxy = new Proxy(targetObj, handler); // 以下でaの値を1に変更使用としてもエラーが発生し変更できない proxy.a = 1; 例5 デフォルト値をgetで取得した際に渡したい場合 main.js const targetObj = { a: 0 }; const handler = { get: function (target, prop, receiver) { // if文を使用する // 最初の条件は自分自身のプロパティにpropの名前が見つかった場合 if(target.hasOwnProperty(prop)){ // returnで返す return target[prop] } else { // 見つからなかった場合にはデフォルト値を返す // 数値はなんでも可 return -1; } } } const proxy = new Proxy(targetObj, handler); // elseの方に条件が分岐するので -1と出力される console.log(proxy.b); 今日はここまで! 参考にさせて頂いた記事 【JS】ガチで学びたい人のためのJavaScriptメカニズム Let'sプログラミング JavaScript入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript入門(JavaScriptとは)

概要 JavaScriptを学習、理解を深めるため「JavaScript Primer 迷わないための入門書」を読み、 理解した内容等を記載していく。 JavaScript入門一覧に他の記事をまとめています。 この記事で理解できること JavaScriptはどのようなプログラミング言語なのか JavaScriptとECMAScriptの関係 JavaScriptのstrict mode JavaScriptとは 主にウェブブラウザ上(GoogleChromeやEdge、Safariなど)で動くプログラミング言語。 ウェブサイトの表示に変化(動き)を付けたり、サーバと通信しデータを取得するなどを行える。 サーバ側で動くNode.jsというプログラミング言語もJavaScriptが利用されている。 JavaScriptとECMAScript JavaScriptというプログラミング言語はECMAScriptという仕様によって動作が決められている。 ECMAScriptはどの実行環境でも共通な動作となる部分を定義している。 ただし、サーバ側で動作するNode.jsはブラウザ側を操作するための機能は不要となるなど、実行環境ごとに必要な機能については、環境ごとに定義(実装)されている。 ECMAScriptの仕様は毎年アップデートされ、新しい文法や機能が追加されている。 JavaScriptの特徴 JavaScriptの特徴には以下のようなものがある。 大文字と小文字を区別する 予約語を持つ 文はセミコロンで区切られる strict modeが存在する 大文字と小文字を区別する // nameという変数を大文字・小文字で宣言してみる // 全て小文字 const name = "Taro"; // 先頭のみ大文字 const Name = "Taro"; // 全て大文字 const NAME = "Taro"; // もう一度全て小文字 const name = "Taro"; // => 'name' を再宣言することはできない旨のエラーが発生する = 最初の変数以外は別物だと区別されている 予約語を持つ 前項の変数宣言時に使用した「const」のように、JavaScriptが用意している特別なキーワードが存在する。 また、その特別なキーワードは「予約語」とも呼ばれる。 この特別なキーワード(予約語)と同じ変数名は使用できない。 ※ECMAScript 2022の仕様書などで詳細を確認できる(英語) // constという予約語と同名の変数名を宣言する const const ... // => 予約語のため使用できない旨のエラーが発生する 文はセミコロンで区切られる JavaScriptは文ごとに処理が行われる、セミコロン(;)によって文が区切られる。 セミコロンが無い文も暗黙的に行末に自動でセミコロンが挿入される仕組みとなっているが、意図しない挙動を避けるため、常に書くことが推奨されている。 スペースやタブ文字は空白文字(ホワイトスペース)と呼ばれ、いくつ置いても挙動に違いはない。 // セミコロンで文が区切られる const name = "Taro"; const age = 24; // 空白文字の数は挙動に違いはない console.log(name + "は" + age + "です"); // => Taroは24歳です console.log(name + "は" + age + "歳です"); // => Taroは24歳です // ※別解:下記は「は 」という文字列と「で す」という文字列を指定しているのでそのまま反映されます console.log(name + "は " + age + "歳で す"); // => Taroは 24歳で す strict modeが存在する JavaScriptにはstrict modeという(厳格な)実行モードが存在している。 有効にすると古く安全でない構文や機能が一部禁止となる。 "use strict";(シングルコーテーション or ダブルコーテーションで囲う)をファイルまたは関数の先頭に書くことで、そのスコープにあるコードはstrict modeで実行される。 // strict mode無効状態 // nameを再代入したつもりが打ち間違え、グローバルな変数が生成される let name = "Taro"; naem = "Hanako"; // strict modeを有効にする "use strict"; let name = "Taro"; naem = "Hanako"; // => Uncaught ReferenceError: naem is not defined 実行コンテキスト(ScriptとModule) JavaScriptの実行コンテキストにはScriptとModuleが存在する。 実行コンテキスト(※一部抜粋 e-words) 同じコード記述やプログラム上の要素が、その置かれているプログラム内での位置や、 実行される際の内部状態などによって異なる振る舞いをしたり、異なる制約を受けたりすることを指してコンテキストということがある。 Scriptの実行コンテキスト Scriptの実行コンテキストは多くの実行環境でデフォルトの実行コンテキストとなっている。 前項のstrict modeはデフォルトで無効となっている。 Moduleの実行コンテキスト Moduleの実行コンテキストは、JavaScriptをモジュールとして実行するために、ECMAScript 2015で導入された。 前項のstrict modeはデフォルトで有効となっている。 モジュールの機能はModuleの実行コンテキストでしか利用できない。 ◆参考 JavaScript入門(ECMAScriptモジュール①) JavaScript入門(ECMAScriptモジュール②) JavaScript入門(ECMAScriptモジュール③)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む