20211031のJavaScriptに関する記事は23件です。

javascript演習 11日目/30日

覚えたこと splice()とslice()の違い ・spliceメソッドを使うと元になった配列要素が変更されてしまう  sliceメソッドは元の配列を変更しない ・slice()は文字列型のメソッド arr.slice([start[, end]]) splice(start, deleteCount) 配列の最後から一定数文字を取得して、 配列をつなげて、特定の文字が含まれてるかどうか調べる arr.splice(-secretCode.length -1 , pressed.length - secretCode.length); if(arr.join('').includes(secretCode)){ }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【学習記録⑮】Vue Routerを用いてURLごとに表示を切り替えてみる!

はじめに Vue.jsを最近勉強し始めたので学んだ文法などを備忘録としてメモしていこうと思います。 今回はVue Routerを用いてページごとに表示を切り替える方法について記載していきます。 Vue Router Vue RouterはVue.jsにおいてルーティングを制御するためのライブラリです。 ※現在僕の学習環境はVue.js 3のため、Vue Routerのバージョンは4で動作確認をしています。 セッティング まずはVue Routerをインストールします。 npm install vue-router@4 ルートファイルの設定 上記にてvue-routerをインストール後、router.jsなどのファイルを作成し、以下のようにrouterの設定を行います。 インストールしたvue-routerからcreateRouter, createWebHistoryをimportします。 createRouterメソッドがルーティング情報を生成し、createWebHistory()はhistoryモードを指しています。 historyモードに設定することでURLに'#'などが付かなくなります。 (createWebHashHistory()を設定するとURLに'#'が付きます。) 今回はMorningページとAfternoonページを用意しているのでそれぞれをroutesにセットします。 router.js import Morning from './views/Morning.vue'; import Afternoon from './views/Afternoon.vue'; import { createRouter, createWebHistory } from 'vue-router'; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Morning }, { path: '/afternoon', component: Afternoon } ] }); export default router; Main.jsでの呼び出し 上記で設定したルーター情報をMain.jsで呼び出します。 Main.js import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; createApp(App).use(router).mount('#app'); ルート先それぞれのコンポーネント 今回はMorningページとAfternoonページを使用するので、それらのVueファイルを作成します。 Morning.vue <template> <div>おはよう</div> </template> Afternoon.vue <template> <div>こんにちは</div> </template> 出力結果 npm run serveコマンドを実行し、結果を見てみます。 URLがlocalhost:8080のときはMorningページが出力されていることが分かります。 URLがlocalhost:8080/afternoonのときはAfternoonページが出力されていることが分かります。 おわりに 今回はVue Routerを利用してURLごとにページを切り替える方法を学びました。 次回はrouter-linkタグを用いて画面からURLを切り替えていく方法についてまとめていこうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのthisを理解する

thisとは 呼び出し元のオブジェクトへの参照を保持するキーワードのこと thisの参照先は、実行コンテキストによって変わります。 実行コンテキストには「グローバルコンテキスト」や「関数コンテキスト」があり、今回は関数コンテキストにおけるthisについて見ていきます。 関数コンテキストにおけるthisの場合は、呼ばれた関数ごとに参照先が変化します。 consoleで「hello Taro」と表示する場合 const person = { name: 'Taro', hello: function () { console.log('Hello ' + this.name); }, }; この場合、this.nameのthisは自身のオブジェクトのpersonを参照します。 helloメソッドを呼び出してみる const person = { name: 'Taro', hello: function () { console.log('Hello ' + this.name); }, }; person.hello(); person.hello()とすることで、personオブジェクトのhelloメソッドを呼び出すことができます。 実行結果 Hello Taro hello Taroと出力されました。 personというオブジェクトのメソッドとしてhelloが呼ばれた場合、helloメソッドの中で呼び出されるthisは呼び出し元のオブジェクトであるpersonを参照します。 thisをpersonに変えて呼び出してみる const person = { name: 'Taro', hello: function () { console.log('Hello ' + person.name); // thisをpersonに変更 }, }; person.hello(); 実行結果 Hello Taro 同じように、hello Taroと出力されました。 これは、変数personがhelloメソッドのレキシカルスコープになるためです。 ただ、メソッドの中でそのオブジェクトを取得したい場合は基本的にthisを使います。 まとめ personというオブジェクトのメソッドとしてhelloが呼ばれた時には、helloメソッドの中で呼び出されるthisは呼び出し元のオブジェクトであるpersonを参照します。 また、personというオブジェクトの中のhelloメソッドは、メモリ空間のどこかに登録されているfunctionのことを指しています。また、同じようにnameプロパティは、メモリ空間のどこかに登録されているTomという値への参照を保持しています。 この状態でpersonのhelloが呼ばれた場合、JavaScriptはpersonというオブジェクトを探しにいきます。そして、そのオブジェクトの中のhelloというメソッドを実行することで、Hello Tomとコンソールに表示されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのJavaScript⑯「関数リテラル」

目次 1.はじめに 2.関数リテラルとは? 3.実際どのようなものか? 4.構文 5.例題 6.おわりに 1. はじめに 本記事では、JavaScriptの「関数リテラル」について記載する。 2. 関数リテラルとは? ・function命令のように関数名を定義しない関数のことを指す。 ・匿名関数や無名関数と言われる。 3. 実際どのようなものか? 実際に構文を書いて、function命令のものと無名関数の2種類で説明する。 関数定義(function命令)の構文 index.js function Hamburg(){ console.log('ハンバーグ') } Hamburg(); // 出力結果:"ハンバーグ" //or Hamburg(); function Hamburg(){ console.log('ハンバーグ') } 関数定義(function命令)の特徴 定義してから実行できたり、コード順関係なく先に実行させることもできる。 関数リテラル(無名関数の代入) index.js let Hamburg = function () { console.log('ハンバーグ'); }; Hamburg();// 出力結果:"ハンバーグ" 関数リテラル(無名関数の代入)の特徴 変数"Hamburg”とfunction命令と紐付けて記述できる。 →全体的にコードがゴチャつかない。 また、先に実行するとエラーが発生してしまう。 index.js Hamburg();// Hamburg is not a function let Hamburg = function () { console.log('ハンバーグ'); }; 先に関数リテラルから先に書いてから実行すること!! 4. 構文 構文は以下のようになる。 index.js function(引数1、引数2、...) { //実行する処理 } 上記でも記載したが、関数名を持たないのが特徴である。 5. 例題 例題の内容は、過去に記載した記事より参照→はじめてのJavaScript⑭ 「関数」 5.例題より 例題の内容 例題は「四角形の面積を計算する関数」というテーマで進めていく。 四角形の縦の長さと横の長さを引数に渡すと、四角形の面積(縦×横)を計算して、値を返す関数を作る 作った関数を使って、高さ3m、横幅5mの四角形の面積を求め、コンソールへ出力する ※単位は㎡とする。(単位の出力は不要) 四角形の面積を計算する関数を関数リテラルで書く index.js let getRectangle = function (height, width) { return height * width; }; console.log(getRectangle(3, 5)); 前回function命令で書いた構文と違い、こちらのほうが私的に理解はしやすかったかなと思った。 解釈の仕方としては、 変数getRectangleに引数の3と5を渡し、渡したものをheightとwidthにそれぞれ代入し、heightとwidthをかけたものをreturnで返してあげる という解釈。 デベロッパーツールでの検証 前回同様、縦3と横幅5の面積は15㎡となるはず。 今回も正しく出力できた。 6. おわりに 次項:はじめてのJavaScript⑰ 「functionコンストラクタ」に続く。(現在作成中)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonが好きなjavascript使いが喜びそうなat method

最近、気づいたんですが.at()というメソッドが追加されたみたいです。 できることは、整数値をパラメーターとして受け取って、インデックスにある要素を返すというメソッドです。タイトルのpython好きなの部分に響きそうなのは、整数値なので、負の値が使えるということです。 何が嬉しいかというと、配列の最後の要素からカウントバックができるという事です。 多分、下記を見ていただくのが分かりやすいと思います。 例 arr = [1,2,3,4,5,6] print(arr[-1]) # 6 Javascriptの場合だと、最後の要素 // without .at() const arr = [1,2,3,4,5,6]; console.log(arr[arr.length-1]); // undefined console.log(arr.slice(-1)[0]); // 6 // with .at() const arr = [1,2,3,4,5,6]; console.log(arr.at(-1)); // 6 codepen mdn at() method ちなみに、どれくらい使えるのかcaniuse.comで確認すると、69%なので、ちょっとまだプロダクトで使うのは早いのかなぁという感じがします。ちなみに、map()は82.28
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonが好きなjavascript使いが喜びそうなat()メソッド

最近、気づいたんですが.at()というメソッドが追加されたみたいです。 できることは、整数値をパラメーターとして受け取って、インデックスにある要素を返すというメソッドです。タイトルのpython好きなの部分に響きそうなのは、整数値なので、負の値が使えるということです。 何が嬉しいかというと、配列の最後の要素からカウントバックができるという事です。 多分、下記を見ていただくのが分かりやすいと思います。 例 arr = [1,2,3,4,5,6] print(arr[-1]) # 6 Javascriptの場合だと、最後の要素 // without .at() const arr = [1,2,3,4,5,6]; console.log(arr[arr.length-1]); // undefined console.log(arr.slice(-1)[0]); // 6 // with .at() const arr = [1,2,3,4,5,6]; console.log(arr.at(-1)); // 6 ちなみに、小数点を渡してもエラーにはならず、小数点以下は切り捨てみたいです。 const arr = [1,2,3,4,5,6]; console.log(arr.at(-1.5)); // 6 console.log(arr.at(2.5)); // 3 console.log(arr.at(4.1)); // 5 codepen mdn at() method ちなみに、どれくらい使えるのかcaniuse.comで確認すると、69%なので、ちょっとまだプロダクトで使うのは早いのかなぁという感じがします。ちなみに、map()は82.28
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue / Nuxt】inputコンポーネントにenterでフォーカス移動する

input内でenterを押下した際に次のinputにフォーカスさせる処理、よくありますよね。 フォーカスしたいinputがコンポーネントの時に悩んだので備忘録です。 子コンポーネントの書き方 Input.vue <template> <div> <input ref="input" :value="value" :type="type" :placeholder="placeholder" @keydown="event=>$emit('focus', event)" /> </div> </template> <script> export default { name: 'Input', props: { type: { type: String, required: false, default: 'text' }, value: { type: String, required: false, default: '' }, placeholder: { type: String, required: false, default: '' } }, methods: { focus () { this.$refs.input.focus() } } } </script> 親コンポーネントの書き方 コンポーネントのinput -> 通常input -> コンポーネントのinputの順でフォーカス移動するサンプルにしました。 index.vue <template> <div> <Input :value="value" type="text" placeholder="name" @focus="event=>nextFocus(event,'emailRef')"/> <input ref="emailRef" :value="value" type="text" placeholder="email" @keydown="event=>nextFocus(event,'passwordRef')"/> <Input ref="passwordRef" :value="value" type="text" placeholder="password"/> </div> </template> <script> import Input from '@/components/Input.vue' export default { components: { Input }, data() { return { value: '' } }, methods: { nextFocus(event, refName) { if (event.keyCode != 13) {return} this.$refs[refName].focus() } } } </script> 解説 Input.vue <input ref="input" :value="value" :type="type" :placeholder="placeholder" @keydown="event=>$emit('focus', event)" /> 子コンポーネント側の@keydown="event=>$emit('focus', event)"で、何かしらキーが押下されるたびにeventを渡すfocusメソッドを読んでいます。 このeventを元にフォーカス処理をするために@focus="event=>nextFocus(event,'ref名')"でメソッドを呼びます。 通常のinputの場合は直接@keydownで問題ありません。       index.vue nextFocus(event, refName) { if (event.keyCode != 13) {return} this.$refs[refName].focus() } nextFocusメソッドでイベントと対象のref名をもらいます。今回はenterの場合に移動したいので、keyCodeが13(enter)の時以外はなにも処理しません。 this.$refs[refName].focus()で受け取ったref名に対して focus処理をしています。      Input.vue focus () { this.$refs.input.focus() } フォーカス先がコンポーネントの場合、コンポーネント(Input)のrefではなく、コンポーネント内に実際に書かれているinput要素にフォーカスする必要があります。 そのため実際にフォーカスしたいコンポーネント内のinputに対するフォーカスのメソッドが必要になります(説明が下手ですみません) 私はこれを書かなかったことによりfocus() is undefinedになり原因解明に時間がかかってしまいました。。。 最後に 他にもっといい書き方がありそうな気がしますが、フォーカス移動付きコンポーネントに関する記事が全く見つからなかったので私にはこれが限界でした。。。 今の現場では$listenersを使用する方法で記述していましたが、vue3では廃止されているようなので自分で考えて書いてみました。ものすごく時間がかかった。。。 もっといい書き方があるよ〜とか、俺ならこう書くぜ!などコメントいただけると大変嬉しいです。 最後まで読んでいただきありがとうございます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【学習記録⑭】transition-groupを用いてリストのトランジションを実装する!

はじめに Vue.jsを最近勉強し始めたので学んだ文法などを備忘録としてメモしていこうと思います。 今回はtransition-groupを用いてv-for等の複数のタグが作成される場面でトランジションを実装していきたいと思います。 transition-group transition-groupはv-for属性等を用いて複数のタグが作られる際にそれらに対してまとめてトランジションを適用したい場合に付けます。 例えば以下の場合、transitionタグで囲っても正常にトランジションは動作しません。 example.vue <li v-for="number in numbers" :key="number"> {{ number }} </li> また、transaction-groupタグを用いる際は必ずkey属性を付ける必要があります。 以上を用いてサンプルを作ってみる 以上を用いてサンプルを作ってみます。 内容としては、初期状態で画面上に数字の「1,2,3」があり、数字追加ボタンをクリックすると4以降の数字が順番に追加され、対象の数字にカーソルをあててクリックするとその数字が削除されるというものとなっています。 App.vue <template> <numbers></numbers> </template> <script> import Numbers from "./components/Numbers.vue"; export default { components: { Numbers }, }; </script> Numbers.vue <template> <button @click="addNumber">数字追加</button> <transition-group name="fade"> <li v-for="(number, index) in numbers" :key="number" @click="remove(index)"> {{ number }} </li> </transition-group> </template> <script> export default { data() { return { numbers: [1, 2, 3], nextNumber: 4, }; }, methods: { addNumber() { this.numbers.splice(this.numbers.length, 0, this.nextNumber++); }, remove(index) { this.numbers.splice(index, 1); }, }, }; </script> <style scoped> .fade-enter-from { opacity: 0; } .fade-enter-active { transition: opacity 0.5s; } .fade-enter-to { opacity: 1; } .fade-leave-from { opacity: 1; } .fade-leave-active { transition: opacity 0.5s; } .fade-leave-to { opacity: 0; } </style> 結果 以下のようにボタンをクリックしたら数字が追加され、数字をクリックしたらその数字が消えることが分かります。 おわりに transition-groupを用いてv-for属性がセットされた要素に対してまとめてレンダリングをすることが出来ました。 transitionは結構難しいですがしっかり理解していきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next JS での TypeError: Failed to construct 'URL': Invalid URL

エラーメッセージ TypeError: Failed to construct 'URL': Invalid URL nextjs import Image from "next/Image"; ... ... <Image className={stack.landingImage} src="/images/hoge.jpg" alt="hoge" layout="fill" objectFit="cover" objectPosition="center" /> next/Imageでのエラー。 原因 不明。 いくら検索してもパッケージにまつわる話しか出てこなく、また、特に該当ソースやコンフィグファイルをいじっていないに関わらず例外を投げられるようになったため、パッケージマネージャー関連の問題と予想。 解決 結局。プロジェクト全体を作り直して解決した。 やはりパッケージのバージョンの依存関連だろう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

最近webpackで環境構築したので利用したプラグインまとめてみた

はじめに こんにちは〜 最近フロントエンドの環境構築の勉強でwebpackを利用しました。 その際、プラグイン多いな!!! と感じたので、自分なりにプラグインをまとめてみました! そもそもwebpackって? コンポーネント化が進む時代・・・build時に各種ファイルを1つにバンドルしてくれるプリプロセッサがwebpackです。 公式の画像がわかりやすいので添付します。 このように、webpackが依存関係も込でいい感じにファイルを1つにまとめてくれます。 あとは、バンドルしてくれたファイルを読み込めば解決になります。 素晴らしいですね! このバンドル時に数々あるプラグインを利用することでより便利になりますので、今回はそちらを紹介いたします。 プラグイン一覧 プラグイン名 プラグインの特徴 webpack webpackを使えるようにするためにとりあえずInstallする webpack-cli 開発者がカスタムwebpackプロジェクトをセットアップする際の速度を上げるための柔軟なコマンドセットを提供 html-loader webpack用のhtmlローダーモジュール css-loader webpack用のcssローダーモジュール file-loader webpack用のfileローダーモジュール html-webpack-plugin バンドルを読み込んだ HTML を出力するプラグイン mini-css-extract-plugin バンドルされる CSS を別の CSS ファイルに抽出する optimize-css-assets-webpack-plugin CSS を最適化するプラグイン webpack-merge 設定をマージする関数 terser-webpack-plugin JavaScritpt を圧縮するプラグイン @babel/core Babelを利用できるようにする @babel/preset-env 対応ブウラウザなどの大雑把な情報を元に、必要なプラグインを自動で選択して、最新の ES6+ を動く状態にしてくれる autoprefixer バンドルする際にベンダープレフィックスを設定する babel-loader babelをwebpackで利用できるようにする clean-webpack-plugin build時に利用していないファイルを削除してくれる ざっとこんな感じです。 本当に便利なプラグインが多いですね。。。 他にも使えそうなプラグインがあったり、便利な書き方があったりしたので、次回の機会にアウトプットしようと思います。 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【学習記録⑬】カスタムトランジションクラスとAnimate.cssを用いてアニメーションを実装する!

はじめに Vue.jsを最近勉強し始めたので学んだ文法などを備忘録としてメモしていこうと思います。 今回はカスタムトランジションクラスとAnimate.cssを用いてアニメーションを実装する方法について記載していきます。 カスタムトランジションクラス 前回の記事ではtransitionコンポーネントのname属性に任意の名前を付け、その属性を用いてstyleタグ内にて組み込みのトランジションクラスへCSSを適用しました。 今回利用するカスタムトランジションクラスは任意のクラス名を組み込みのトランジションクラスのように使いたいとき(説明が難しくてこんな表現ですみません...)に利用します。 例えば、今回利用するAnimate.cssではライブラリが用意したクラス名をトランジションクラスに利用したいのですが、前回の記事の内容ではクラス名は組み込みのものを利用しているため、ライブラリが用意したクラスを利用することができません。 そこで任意のクラスを用いたいが、だがしかし組み込みのトランジションクラスのように特定の発火時を指定したい(v-enter-activeやv-leave-activeなど)ときに、このカスタムトランジションクラスを使います。 利用方法 利用方法は割と単純でtransitionタグに以下のように特定のカスタムトランジションクラスを用意し、その中に任意のクラス名を入れます。各トランジションクラスについてはそれぞれ組み込みのトランジションクラスの意味と同様です。 exampleTemplate.vue <template> <transition enter-from-class="任意のクラス名" enter-active-class="任意のクラス名" enter-to-class="任意のクラス名" leave-from-class="任意のクラス名" leave-active-class="任意のクラス名" leave-to-class="任意のクラス名" > </transition> </template> 以上を用いてサンプルを作ってみる それでは以上を用いてサンプルコードを作ってみます。 今回はおはようボタンをクリックし、あいさつ文が現れたり消えたりする内容にします。文字が現れる際はAnimate.cssのbounceモーションが、文字が消える際はAnimate.cssのflashモーションが適用されるようにしていきます。 Animate.cssはanimate__animated animate__動作のようにクラス名をセットすることで利用が出来ます。 (今回のサンプルでは省略していますが、ライブラリ参照についてはCDNを利用しており、index.htmlファイルにその設定をしています。) Morning.vue <template> <button @click="show = !show">おはようボタン</button> <transition enter-active-class="animate__animated animate__bounce" leave-active-class="animate__animated animate__flash" > <div v-if="show">{{ greetMessage }}</div> </transition> </template> <script> export default { data() { return { greetMessage: "Good Morning!", show: true, }; }, }; </script> App.vue <template> <morning></morning> </template> <script> import Morning from "./components/Morning.vue"; export default { components: { Morning }, }; </script> 結果 以下のように文字が現れる際はbouceが、消える際はflashが適用されていることが分かります。 おわりに 今回Vue.jsの勉強をしていて偶然Animate.cssを見つけたのですが、これとても便利ですね! 今後アニメーションを実装したいと思った際はこちらをぜひ使ってみようと思います!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript メモ :定数、変数

定数 プログラムの中で値に付けられる名前のこと ・定数を作るには、constとというキーワードの後に定数名を書いて、定数を使いますよと宣言する必要がある。 例)const price = 200; ・上記の=(イコール)は右の値に左の値を当てはめるという意味。これを代入という。 ・再代入できない。 変数 ・変数を作るには、letもしくはvarとというキーワードの後に変数名を書いて、変数を使いますよと宣言する必要がある。 ・再代入できる。 例) let prece =100; console.log(price * 10); console.log(price * 20); price = 150;(再代入) console.log(price * 10); console.log(price * 20);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのJavaScript⑮ 「function命令」

目次 1.はじめに 2.function命令とは? 3.構文 4.例題 5.おわりに 1. はじめに 本記事では、JavaScriptの「function命令」について記載する。 2. function命令とは? ・様々な処理を1つにまとめて、名前をつけることができるもの。 ・単調な処理を1つにまとめて、どこからでも使えるように効率化するという目的でよく使用される。 ・1つにまとめることで同じ処理を何度も書く必要がなくなりミスが減るうえ、別のプログラムに使いまわすことも可能になる。 3. 構文 構文は以下のようになる。 index.js function 関数名 (引数1、引数2、...) { //任意の処理 return 戻り値 } 構文の補足 ・引数とは、関数の呼び出し元から、関数へ値を渡すときに使う特別な変数のこと。 ・戻り値とは、関数から関数呼び出し元へ返す値のこと。 4. 例題 例題の内容は、過去に記載した記事より参照→はじめてのJavaScript⑭ 「関数」 5.例題より 例題の内容 例題は「四角形の面積を計算する関数」というテーマで進めていく。 四角形の縦の長さと横の長さを引数に渡すと、四角形の面積(縦×横)を計算して、値を返す関数を作る 作った関数を使って、高さ3m、横幅5mの四角形の面積を求め、コンソールへ出力する ※単位は㎡とする。(単位の出力は不要) 四角形の面積を計算する関数をfunction命令で書く index.js function getRectangle(height, width) { return height * width; } console.log(getRectangle(3, 5)); <解説> まず関数名のgetRectangleだが、JavaScriptの関数名でググっても検索にヒットせず。 下記リンクにそれっぽい表記があり、 キャプションをその中に描画する矩形を取得する とのこと。おそらく面積を求めるということでこの関数を使用していると思われる。 GetRectangle メソッド 引数には高さを表すheightと横幅を表すwidthを指定する。 returnの内容は、四角形の面積を求める式と同様に縦×横で記述する。 コンソールへ出力し、getRectangle内の引数へそれぞれ値を与える。 function命令の読み解き方 fanction命令の構文は、以下のように読み解く。 コンソールへ出力した値をfunction内の引数にそれぞれ代入し、returnの部分で四角形の面積を求める計算式で返してあげる。 デベロッパーツールでの検証 縦3と横幅5の面積は15㎡となるはずなので、 正しく15と出力することができた。 5. おわりに 次項:はじめてのJavaScript⑯ 「関数リテラル」に続く。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptのイテレータとジェネレーターを簡単に触ってみた

イテレーター イテレーターとは反復操作を行う際に使用するオブジェクトになります。イテレーターでは2つのプロパティを持つオブジェクトを返すnext()メソッドをもつことによってイテレータープロトコルを実装するオブジェクトになります。 言葉では理解しにくいと思うのでまずはコードで確認してみましょう。 JS function genIterator() { // genIteratorのreturnで返すものがイテレーター return { next: function() { return { done: true/false, //ループ継続の可否 value: 値 //返却されるもの } } } } コードを確認できたところでまずは簡単に0~3までを出力するイテレーターを作成してみましょう。 JS function genIterator(max) { let i = 0; //iの初期化 return { next: function() { if(i >= max) { return { done: true // valueは記述不要 } } else { return { done: false, value: i++ } } } } } const it = genIterator(4); console.log(it.next()); //=>{done: false, value: 0} console.log(it.next()); //=>{done: false, value: 1} console.log(it.next()); //=>{done: false, value: 2} console.log(it.next()); //=>{done: false, value: 3} console.log(it.next()); //=>{done: true} まずgenIteratorという関数を作成します。引数にはmaxを設定してその日数の値によって継続のfalse/trueをif文で分けていきます。genIteratorを実行すると期待されるdone,valueプロパティを保持したオブジェクトが返却されていることが確認できます。 これらを馬鹿正直に一つずつconsole.logで出力するのはスマートではありません。while文で記述しなおして数字だけを取り出してみましょう。 JS function genIterator(max) { let i = 0; //iの初期化 return { next: function() { if(i >= max) { return { done: true // valueは記述不要 } } else { return { done: false, value: i++ } } } } } const it = genIterator(4); let a = it.next(); while(!a.done) { console.log(a.value); a = it.next(); } オブジェクトを反復可能にする オブジェクトを反復可能にするにはSymbol.iterator (このための特別な組み込みのシンボルです)という名前のメソッドをオブジェクトに追加する必要があります。 JS function genIterator(max = 4) { let i = 0; //iの初期化 return { next: function() { if(i >= max) { return { done: true // valueは記述不要 } } else { return { done: false, value: i++ } } } } } const obj = {} //空のオブジェクトを用意する。 obj[Symbol.iterator] = genIterator //objのSymbol.iteratorに先程生成したgenIteratorをセットする。このとき引数のデフォルト上限値を設定しないと無限ループになるので注意 for(let i of obj) { console.log(i); } ジェネレーター ジェネレーターはイテレーターを生成するための特殊な関数でより簡略して記述することができます。 functionに*をつけることで明示的にジェネレーター関数であることを宣言できます。ジェネレーター関数にはyeildというキーワードがありこれによりイテレーター部分を表現することができます。 またyeildではnextメソッドに続くdoneがfalseでその時のvalueの値がyeildのvalueと一致します。 コードで確認してみましょう。 JS function* foo(index) { while (index < 2) { yield index++; } return 2 } const iterator = foo(0); console.log(iterator.next()); //{value: 0, done: false} console.log(iterator.next()); //{value: 1, done: false} console.log(iterator.next()); //{value: 2, done: true} const iterator = foo(0)とすることでジェネレーターからイテレーターを作成します。そして、イテレーターのnextメソッドで期待されるオブジェクトをconsoleで出力しています。ここで、最後にreturnで呼ばれているのでdoneがtureで返されていることが確認できます。 おまけ おまけとしてスプレット演算子について軽く取り扱います。スプレット演算子とは反復可能や列挙可能オブジェクトの展開を行います。主に配列を展開し、新しい配列に代入するとき時などに利用します。 コードで確認してみましょう。 JS const arry1 = [1,2,3,4,5]; const arry2 = [...arry1]; console.log(arry1); //(5) [1, 2, 3, 4, 5] console.log(arry2); //(5) [1, 2, 3, 4, 5] console.log(arry1 === arry2); //false 今arry1という配列を宣言しスプレット演算子(...)によって新しい配列としてarry2に代入しています。ここで注意が必要なのが、スプレット演算子で代入されたarry2はあくまで新しい配列なので等価性を見るとfalseの結果が返ってきます。 スプレット演算子を関数の仮引数に設定すると、渡ってくる引数が...に続く変数名の配列となります。こちらもコードで確認してみましょう。 JS function sum(...args) { let ret = 0; console.log(args); //(4) [1, 2, 3, 4] for(let v of args) { console.log(v); ret += v; } return ret; } const result = sum(1,2,3,4) console.log(result); //10 関数sum内でのargsは渡ってきた引数を配列にした形で処理されていることが確認できます。 参考 Udemy: 【JS】ガチで学びたい人のためのJavaScriptメカニズム 現代の JavaScript チュートリアル:https://ja.javascript.info/logical-operators
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React-Hook-Formの使い方(Part2:RHFとyupでバリデーション)?

はじめに Part1ではRHFの使い方。Part2でははRHFとyupを合わせて利用する方法を見ていきます。次回のPart3ではさらにMUIとの併用を見ていきます。 ※これはpart2になります。part1は以下リンク https://qiita.com/shunnami/items/98c0c03e3eb59b5c9c57 パッケージインストール yarn add yup hookform/resolvers yupの使いかた RHFの使い方はpart1で勉強しました。あとはyupの使い方を理解すればすぐに合体させて利用することができます。またyup自体も使い方は簡単です。 すでに詳細な使い方を説明している記事があるので以下を参照してください。 ※2ページ目まで読めば次の項目をスムーズに読むことが出来ると思います。 https://codezine.jp/article/detail/13518 組み合わせる yupの定義 まずは、バリデーションをyupで定義します。 yup.object().shapeに渡すオブジェクトのプロパティをpasswordにしています。これはregisterで登録したname属性にしてください。 import * as yup from "yup"; // スキーマ const schema = yup.object().shape({ password: yup .string() .min(4, "4桁以上必須") .max(20, "最大20桁") .required("必須です") }); useFormのオプションを設定する useFormの引数に値を渡すことでオプションを設定できます。yupを利用する際にはuseFormのオプションを設定する必要があります。その設定に必要なのが冒頭でインストールしたyupResolverです。yupResolverの引数には先ほど定義したスキーマを渡します。 import { yupResolver } from "@hookform/resolvers/yup"; useForm<IFormInputs>({ // useFormの引数にyupResolverを設定 resolver: yupResolver(schema) }); 全体のコードを確認 上記の2つの工程でyupバリデーションが実現できました。最後にコード全体を見てみます。RHF単体のときよりyupを組み合わせたほうがエラーメッセージがスマートに出し分けできます。 import { useForm, SubmitHandler } from "react-hook-form"; // yupを利用するなら以下をimportする import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; // submitされる値の型定義 type IFormInputs = { password: string; }; // submitをハンドリング const formSubmitHandler: SubmitHandler<IFormInputs> = (data) => { console.log(data); }; // スキーマ const schema = yup.object().shape({ password: yup .string() .min(4, "4桁以上必須") .max(20, "最大20桁") .required("必須です") }); export default function App() { const { register, handleSubmit, watch, formState: { errors } } = useForm<IFormInputs>({ // useFormの引数にyupResolverを設定 resolver: yupResolver(schema) }); return ( <form onSubmit={handleSubmit(formSubmitHandler)}> <input {...register("password")} /> {errors.password && ( // errors.password.messageにはyupのスキーマで設定した値が入る <span style={{ color: "red" }}>{errors.password.message}</span> )} <br /> <input type="submit" /> </form> ); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【学習記録⑫】transitionコンポーネントを用いてアニメーションを作ってみる!

はじめに Vue.jsを最近勉強し始めたので学んだ文法などを備忘録としてメモしていこうと思います。 今回はtransitionコンポーネントを用いてアニメーション(簡単なフェードイン、フェードアウト)を作ってみようと思います。 transitionコンポーネント Vue.jsでアニメーションを実装する際は組み込みコンポーネントであるtransitionコンポーネント、もしくはtransition-groupコンポーネントを用いて実装します。 基本的な使い方 基本的な使い方としては以下のようにアニメーションさせたい部分をtranasitionコンポーネントで囲み、name属性へ任意の値を入れます。 exampleTemplate.vue <template> <transition name="任意の値"> <div></div> </transition> </template> その後、上記で付けた「任意の値」を用いて以下のクラスを作成します。 [任意の値]-enter-from [任意の値]-enter-active [任意の値]-enter-to [任意の値]-leave-from [任意の値]-enter-active [任意の値]-enter-to 以下は「任意の値」をfadeとしたときの例です。 exampleStyle.vue <style scoped> .fade-enter-from { /* 処理 */ } .fade-enter-active { /* 処理 */ } .fade-enter-to { /* 処理 */ } .fade-leave-from { /* 処理 */ } .fade-leave-active { /* 処理 */ } .fade-leave-to { /* 処理 */ } </style> これらのクラスをトランジションクラスと言い、それぞれの意味や発火時については以下のドキュメントが参考になります。 以上を用いてサンプルを作ってみる 以上を用いて、サンプルコードを書いてみます。 内容としてはおはようボタンというボタンをクリックするとGood Morning!という文字がフェードインしてくるものとします。 サンプルコード Morning.vue <template> <button @click="show = !show">おはようボタン</button> <transition name="fade"> <div v-if="show">{{ greetMessage }}</div> </transition> </template> <script> export default { data() { return { greetMessage: "Good Morning!", show: true, }; }, }; </script> <style scoped> .fade-enter-from { opacity: 0; } .fade-enter-active { transition: opacity 0.5s; } .fade-enter-to { opacity: 1; } .fade-leave-from { opacity: 1; } .fade-leave-active { transition: opacity 0.5s; } .fade-leave-to { opacity: 0; } </style> App.vue <template> <morning></morning> </template> <script> import Morning from "./components/Morning.vue"; export default { components: { Morning }, }; </script> 結果 以下のように挨拶文がフェードイン・フェードアウトしていることが分かります。 おわりに 今回はtransitionコンポーネントを用いたアニメーション作成の学習を行いました。 次回はAnimate.cssを用いて色々なアニメーションを実装してみたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

InDesign JavaScript ストーリーの段落を要素に

ストーリーの段落を要素にするスクリプトは、これで良いのかな・・・? /* このスクリプトを利用して起こった不具合の責任は取れません。 ご了承下さい。 更新 2021/10/31 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "ストーリーの段落を要素に"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function(){ // ダイアログ var dialogueFlg = confirm("ストーリーの段落を段落の「終わりの記号」を除いて要素にします。" + "\r\r" + "段落の先頭に「索引マーカー」がある場合は追加された要素にその「索引マーカー」は含まれないので注意して下さい(手抜き仕様)。" + "\r\r" + "ストーリーの要素を選択してスクリプトを実行して下さい。" ,"", scriptName); // Noの場合 if(dialogueFlg == false){ exit(); } // 検索の初期化 app.findGrepPreferences = NothingEnum.NOTHING; app.changeGrepPreferences = NothingEnum.NOTHING; // 検索文字列 app.findGrepPreferences.findWhat = ".+"; // 段落の最大数 var paragraphCountMaxNumber = 0; if(app.activeDocument.selection.length > 0){ // 最大の段落数を調べる for(var i = 0; i < app.activeDocument.selection.length; i++){ if(app.activeDocument.selection[i].constructor.name == "XMLElement"){ if(app.activeDocument.selection[i].xmlContent.constructor.name == "Story"){ if(paragraphCountMaxNumber < app.activeDocument.selection[i].xmlContent.paragraphs.count()){ paragraphCountMaxNumber = app.activeDocument.selection[i].xmlContent.paragraphs.count(); } } } } // タグを追加する for(var i = 1; i <= paragraphCountMaxNumber; i++){ if(app.activeDocument.xmlTags.itemByName("Paragraph" + i).isValid == false){ app.activeDocument.xmlTags.add("Paragraph" + i); } } // 段落に要素を関連付け for(var i = 0; i < app.activeDocument.selection.length; i++){ if(app.activeDocument.selection[i].constructor.name == "XMLElement"){ if(app.activeDocument.selection[i].xmlContent.constructor.name == "Story"){ for(var ii = 0; ii < app.activeDocument.selection[i].xmlContent.paragraphs.count(); ii++){ // 置換でタグを付ける app.changeGrepPreferences.markupTag = app.activeDocument.xmlTags.itemByName("Paragraph" + (ii + 1)); app.activeDocument.selection[i].xmlContent.paragraphs.item(ii).changeGrep(); } } } } } // 検索の初期化 app.findGrepPreferences = NothingEnum.NOTHING; app.changeGrepPreferences = NothingEnum.NOTHING; //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

InDesign JavaScript XML ストーリーの段落を要素に

ストーリーの段落を要素にするスクリプトは、これで良いのかな・・・? /* このスクリプトを利用して起こった不具合の責任は取れません。 ご了承下さい。 更新 2021/10/31 */ // アプリ指定 #target "indesign"; // スクリプト名 var scriptName = "ストーリーの段落を要素に"; //スクリプトの動作指定(一つのアンドゥ履歴にする、及び、アンドゥ名) app.doScript(function(){ // ダイアログ var dialogueFlg = confirm("ストーリーの段落を段落の「終わりの記号」を除いて要素にします。" + "\r\r" + "段落の先頭に「索引マーカー」がある場合は追加された要素にその「索引マーカー」は含まれないので注意して下さい(手抜き仕様)。" + "\r\r" + "ストーリーの要素を選択してスクリプトを実行して下さい。" ,"", scriptName); // Noの場合 if(dialogueFlg == false){ exit(); } // 検索の初期化 app.findGrepPreferences = NothingEnum.NOTHING; app.changeGrepPreferences = NothingEnum.NOTHING; // 検索文字列 app.findGrepPreferences.findWhat = ".+"; // 段落の最大数 var paragraphCountMaxNumber = 0; if(app.activeDocument.selection.length > 0){ // 最大の段落数を調べる for(var i = 0; i < app.activeDocument.selection.length; i++){ if(app.activeDocument.selection[i].constructor.name == "XMLElement"){ if(app.activeDocument.selection[i].xmlContent.constructor.name == "Story"){ if(paragraphCountMaxNumber < app.activeDocument.selection[i].xmlContent.paragraphs.count()){ paragraphCountMaxNumber = app.activeDocument.selection[i].xmlContent.paragraphs.count(); } } } } // タグを追加する for(var i = 1; i <= paragraphCountMaxNumber; i++){ if(app.activeDocument.xmlTags.itemByName("Paragraph" + i).isValid == false){ app.activeDocument.xmlTags.add("Paragraph" + i); } } // 段落に要素を関連付け for(var i = 0; i < app.activeDocument.selection.length; i++){ if(app.activeDocument.selection[i].constructor.name == "XMLElement"){ if(app.activeDocument.selection[i].xmlContent.constructor.name == "Story"){ for(var ii = 0; ii < app.activeDocument.selection[i].xmlContent.paragraphs.count(); ii++){ // 置換でタグを付ける app.changeGrepPreferences.markupTag = app.activeDocument.xmlTags.itemByName("Paragraph" + (ii + 1)); app.activeDocument.selection[i].xmlContent.paragraphs.item(ii).changeGrep(); } } } } } // 検索の初期化 app.findGrepPreferences = NothingEnum.NOTHING; app.changeGrepPreferences = NothingEnum.NOTHING; //スクリプトの動作指定の続き }, ScriptLanguage.JAVASCRIPT, [scriptName], UndoModes.ENTIRE_SCRIPT, scriptName);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】関数とオブジェクト⑯ クラス

はじめに Udemyの【JS】ガチで学びたい人のためのJavaScriptメカニズムの講座の振り返りです。 前回の記事 目的 関数とオブジェクトについての理解を深める 本題 1.クラス コンストラクター関数をクラス表記したもの → シンタックスシュガー 例1 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.hello = function() { console.log('hello ' + this.name); } 上記のPersonコンストラクターをクラスに書き換えていく // クラスを用意 class Person { // constracterで関数を定義 constructor(name, age) { this.name = name; this.age = age; } // プロトタイプのメソッドも関数と同じ並びに配置 // function削除 hello() { console.log('hello ' + this.name); } } // インスタンス化 const Lupin = new Person("ルパン", 26); // 出力するとPerson {name: 'ルパン', age: 26}で返ってくる console.log(Lupin); JavaScriptではインスタンス化したら、オブジェクトで返ってくる 2.クラス継承 クラス継承とは他のクラスのメソッドやプロパティを継承すること 例1. function Person(name, age) { this.name = name; this.age = age; } Person.prototype.hello = function() { console.log('hello ' + this.name); } function Japanese(name, age, gender) { Person.call(this, name, age); this.gender = gender; } Japanese.prototype = Object.create(Person.prototype); Japanese.prototype.hello = function() { console.log('Konnichiwa ' + this.name); } Japanese.prototype.bye = function() { console.log('Sayonara ' + this.name); } const taro = new Japanese('Taro', 23, 'Male'); 上記のプロトタイプ継承されたコードをクラス継承に変えていく // Personをクラス表記に変更 class Person{ constructor(name, age) { this.name = name; this.age = age; } // helloメソッド追加 hello() { console.log('hello ' + this.name); } } // 同様にjapaneseクラスを作成 // Javaでも使用したモノ! class Japanese extends Person{ constructor(name, age, gender) { // クラスの継承では下記を使用していたが、クラスではextendsを使用 // Person.call(this, name, age); // superを使用することで継承元のプロパティを使用できる super(name, age); this.gender = gender; } // helloメソッドを追加 hello(){ console.log('Konnichiwa ' + this.name); } // byeメソッドを追加 bye() { console.log('Sayonara ' + this.name); } } // 下記のコードもextendsによって実現されるので不要 // Japanese.prototype = Object.create(Person.prototype); // インスタンス化 const taro = new Japanese('Taro', 23, 'Male'); // 出力するとオブジェクトが生成される console.log(taro); // taroメソッドが使用される taro.hello(); 今日はここまで! 参考にさせて頂いた記事 【JS】ガチで学びたい人のためのJavaScriptメカニズム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ランダムなダミーの温湿度の値で急激な変動がない値を生成する(Node.js で simplex-noise を利用)

「デバイス+センサー」が MQTTクライアントになった構成の開発をしている中で、MQTT の値を受信する MQTTブローカーの部分や別クライアント側に手を加える開発をするのに、以下のような物理部分をセットしなくても開発できるようにしたくて、今回の内容を進めました。 GR-ROSE と「Grove - CO2 & Temperature & Humidity Sensor (SCD30) 」の接続はスルーホール用テストワイヤを使い、差し込み式で接続してる。 https://t.co/hA2wu3KYw3 pic.twitter.com/WbV3yNgNB6— you (@youtoy) October 30, 2021 概要 ひとまず温湿度の 2つの値を対象に、それらをランダムに生成するプログラムを作っていきます。 その値について、JavaScript だと Math.random() を使う方法もありますが、あまり急激な変化はしてほしくなかったため、乱数生成の部分には「Simplex Noise」を使いました(パッケージは以下を利用)。 ●simplex-noise - npm  https://www.npmjs.com/package/simplex-noise ちなみに、Simplex Noise をざっくり説明すると、以下のパーリンノイズと同じようなものになります。 ●パーリンノイズ - Wikipedia  https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%83%AA%E3%83%B3%E3%83%8E%E3%82%A4%E3%82%BA 必要になる環境など 開発には Node.js を使っていきます。 MQTTブローカーを何らか用意し、今回のプログラムを動作させる PC の中で動かしてください。 また、以下のコマンドで 2つのパッケージを利用できるようにしておいてください。 $ npm i mqtt simplex-noise プログラム プログラムの内容は、以下のとおりです。 const mqtt = require("mqtt"); const client = mqtt.connect("mqtt://localhost"); const { SimplexNoise } = require("simplex-noise"); const simplex = new SimplexNoise(); let count = 0; client.on("connect", function () { const timerId = setInterval(() => { const randomT = convertedValueRandom(simplex.noise2D(count, 10), 20, 25); client.publish("outTopic/T", randomT + ""); const randomH = convertedValueRandom(simplex.noise2D(count, 20), 60, 80); client.publish("outTopic/H", randomH + ""); console.log(` 温度: ${randomT}, 湿度: ${randomH}`); count += 0.007; }, 2000); }); function convertedValueRandom(inputSimplexNoise, min, max) { const randomZeroToOne = (inputSimplexNoise + 1) / 2; return randomZeroToOne * (max - min) + min; } simplex-noise は -1 から 1 の間の値を生成するため、Math.random() のような 0 から 1 の値を生成させたい場合は、少し手を加える必要があります。 上記の中では、 convertedValueRandom() でその変換を行っています。 また、 count += 0.007 の値を変更すると、生成される乱数の変化の度合いを変えることができたりもします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング修羅道。新しい技術や知識を学習するのに疲れ果てたプログラマー達へ。

次々に生まれては消える技術と足りない学習時間。消えゆくモチベーション 異国でのコロナロックダウンと、長年やっていたビジネスのシャットダウンを受け、昔やっていたプログラミングを再度勉強し直し始めて早10か月あまり。 最初は2020年のモダンな環境で、フルスタックというネーミングに憧れ、とりあえずウェブサービスを作れるくらいになりたいなぁと始めたのですが、やればやるほど深みに嵌っていく底なし沼。次々に現れる新しい敵。毎月開発される新しいライブラリやフレームワーク。どんどんdeprecatedになり、使えなくなっていくモジュール類。アップデートされる言語仕様。半年も経てば移り変わるトレンド。 何かを一つ手に入れたと思えば、過去に手にしたものを忘れ去り、何かを実装しようと思えば、さらに新しい概念の習得が必要となり、まさに砂漠に水を撒いてるような感覚になり、モチベーションが下がる。多分、ほとんどのプログラマー諸先輩も経験されているのではないかと思います。 自分もこういう穴に孤独に嵌りつつあり、他の神の様なトッププログラマー諸先輩は、どのようにモチベーションを保ち、学習時間を確保し、どういう勉強方法で次々に新しい技術にキャッチアップしていってるのかに興味が湧き、色々と体験談を見てみると、驚くほど皆同じような事を言ってるんだよね。 で、これはシェアする価値があるんじゃないかと思ったので、備忘録的に書いてみるよ。 プログラミングで一番重要なスキルはgoogling どのプロフェッショナルなプログラマーも言ってるのは、検索スキル。これは自分の知りたい事やエラーの解決方法を的確に調べて解決するスキルの事。プログラミングでは非常に重要なワークフローで、時間の大半はリサーチや検索作業に費やしているといってるプログラマーが本当に多いね。 ここで重要なのは、分からなければググれ では無く、ググるという作業自体がすでにプログラミングの超重要な部分を占めているという事。 で、具体的なサイトとしていつも上がるのが、https://stackoverflow.com/ と MDN(https://developer.mozilla.org/en-US/)。 あと、重要だと挙げられているのは、公式ドキュメントをちゃんと読め。という事。てめえの知るべき事は、全てはそこに書いてあると。 プログラムは暗記するな。時間の無駄 ほぼ、どのデヴェロッパーも言ってるのが、個別の関数やモジュールなんかは覚える必要なし。で、それを一生懸命やっているとするなら時間の無駄である。という事。一番重要なのはコンセプトを理解すること。個別の関数やシンタックスは、その都度ググればよい。プログラミングに関する事なら、100%オンライン上にドキュメント化されている。 脳の記憶は一度覚えると変わらないけど、プログラミングの世界は絶えず変わっていってるので、それはすぐに役に立たないものになる。 コンセプトを理解する 色々なプログラミング言語が世の中には存在するが、コンセプトは共通しているものが多い。 プログラムをコントロールする仕組み。ループや条件分岐、データ型の考え方はほぼ全ての言語で共通しているので、違いは書き方の違いだけである。なので、これを理解しておけば、どんな言語でも理解が容易になる。あとは、データ構造とアルゴリズム。 また、個別の関数レベルでも、覚えるべきはコンセプト。例えば、DOMはどういうコンセプトで操作するのかを理解すれば、個別の関数は覚えなくても良い。そうすれば、覚えるべきことはグッと少なくなる。 個別の関数や、シンタックスは覚えてもきりが無いので、時間の無駄。でも、コンセプトはきっちり理解しよう。 あと、プログラミングの入門として、pythonかjavascriptをリコメンドしている人が多い印象。 ラビットホールに嵌るな。自分の手で自分のプロジェクトを作れ 最初に新しいことを学ぶ際は、YoutubeやUdemyのチュートリアルはとても役に立つし、コードを真似して書いていくことで学べる事は沢山ある。 ただ、人のコードをなぞったりコピーしてみて何かを作っても、いざ自分が一から何かを作ろうとしたときに、全く何からやっていいのか分からないというパターンに陥ることがほとんどである。 何かに行きつくには、プログラマーがいればそのプログラマーの数だけ方法があり、道筋がある。一つの正解しかないという事はあり得ない。 なので、自分の手を汚す。小さなスニペットでも良いので、自分のプロジェクトを作って、自分で自分のコードをググりながら書いてみることが何よりも重要で、そこから学べる事はけた違いである。 練習用のプロジェクトとして、出来るだけ人のコードをコピペせず、自分の興味のあるもの、機能が1-2個だけの小さなプロジェクトから始めると挫折せずにやりやすい。 本当に重要な技術はそんなに沢山無い 日夜新しいライブラリやフレームワークが開発され、リリースされ、ネット上には膨大な情報やそれにまつわるチュートリアルや解説をしているコンテンツがあるし、そういうのを見ていると、ストレスが溜まるし、燃え尽きる様な気分になると思う。ただ、本当にあなたにとって重要なものはそれほどあるわけでは無い。 自分が何がやりたいか、それには何が必要なのかを冷静に考えると、本当に必要な技術はそれほど多くないことに気付くはずである。 コードを書く時の3原則 KISS(Keep It Simple Stupid) : コードはstupidな位シンプルにしろ DRY(Don’t Repeat Yourself) : 繰り返しは極力避けろ(1つのファンクションにまとめる) YAGNI(You Aren’t Gonna Need It) : 今実際に必要なコード以外は書くな どんなデヴェロッパーも最初からエラーの無いコードが書けるわけではない 最初に書くコードは、エラーが出ることが普通。 YoutTubeとかで、どんなにスムースにコードを書いてる様に見えるプログラマーでも、相当に事前にリサーチをしてテストをしている。 一見凄いプログラマーでも、やっぱり新しい技術を覚えるときは、初心者みたいにYoutube見たりブログ見たり、オンラインのクラスをとったりして勉強し、自分で小さなコードを書いて、トライ&エラーを繰り返しながら習得している。 ひょっとすると、「世界のトッププログラマーがやってる凄い学習法!Google、Amazonのプログラマーが絶賛!」みたいな釣り本的内容を期待された方もいるやも知れませんが、初心者も、経験のあるプログラマーも、結局学習のプロセスにはそんなに差がなく、とにかくググれ、書いてみろっていうのが共通して言ってることで、凄いプログラマーだから、何か特別な方法があるという事ではなく、ある意味対等なんだなあという印象を受けました。 というわけで、栄光に近道無し! やはり、基本は独学。自分なりの独学法を見つけたものが栄光への近道なのであって、無料で使える海外独学サイト一覧を作ったよ↓ ビギナー向け、無料で最強の海外独学サイトリスト 参考にした動画や記事 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript演習 11日目/30日

覚えたこと videoタグ paused と pause() if(video.paused){ video.play(); }else{ video.pause(); } //こうも書ける const method = video.paused ? 'play' : 'pause'; video[method](); video.currentTime と this.dataset <button data-skip="-10" class="player__button">« 10s</button> <button data-skip="25" class="player__button">25s »</button> <script> function skip(){ video.currentTime += parseFloat(this.dataset.skip); } </script> name属性を使って連動させる <input type="range" name="volume" min="0" max="1" step="0.05" value="1"> <input type="range" name="playbackRate" min="0.5" max="2" step="0.1" value="1"> <script> function handleRangeUpdate(){ video[this.name] = this.value; } </script> video.duration と cssのflex-basis function handleProgress(){ const percent = (video.currentTime / video.duration) * 100; progressBar.style.flexBasis = `${percent}%`; } video.addEventListener('timeupdate', handleProgress); その他 function scrub(e){ const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration; video.currentTime = scrubTime; } あとaddEventListener の mousedown とか mouseupとか ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ーーー 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[いよいよ年明けリリース!?]WebTransportでもechoがしたい!

WebTransportがChromeM97でいよいよリリースです! Webを愛する皆さんこんにちは。 去年、一昨年、WebsoketやWebRTCの新しい技術として注目を集めたWebTransportがいよいよ年明けにもリリースされるようです。 いやぁ、WebRTCが2014ごろにリリースされてから早7年、ようやくという感じです。 今回はCanaryがM97となり、Origin Trials不要になったので公式サンプルを修正して動かしてみます。 そもそもWebTransportとはなんぞやみたいな話はNTTコミュニケーションズさんの記事が詳しいのでそちらをご覧ください。 大きな変更点まとめ Chrome Canary M97ではOrigin Trialsの登録が不要になった プロトコルは quic-transport:// ではなく https:// に決定 H3_DATAGRAM は 0x276 ではなく 0xffd277 に QUICがRDC9000になった 公式サンプルはjsが古くて動かない 作ったもの githubにローカルで動作するクライアントとサーバーのソースコードをアップしました。 手順 とりあえず動かしてみたい方は以下の手順で動作確認ができます。 今回はIntelMac(Python3.9)で動かしました。 Windowsなどでも動くかとは思います。 必要なソフトをインストールする Chrome Canary をダウンロードします Python3.xがない場合はインストールしてください。 今回のソースコードはgithubに置いてあります。 Macの場合はOpenSSLをインストールする必要があります。(デフォルトでインストールされているLiberaSSLではオプションが異なるため) $ brew install openssl pythonのライブラリをインストールして一部修正する pythonでの通信にaioquicというライブラリを使用しますが、H3_DATAGRAMの値が古いので修正します。 $ pip3 install aioquic # pipenvなどを使っていない場合は多分↓にあると思います。 $ vim /usr/local/lib/python3.9/site-packages/aioquic/h3/connection.p # fix from H3_DATAGRAM = 0x276 # to H3_DATAGRAM = 0xffd277 自己証明書を準備する 通信は全てTLSになるので証明書が必要になります。 fingerprintをCanary起動時に指定します。 # 証明書を作成(certificate.pemと.keyができます) $ openssl req -newkey rsa:2048 -nodes -keyout certificate.key \ -x509 -out certificate.pem -subj '/CN=Test Certificate' \ -addext "subjectAltName = DNS:localhost" # fingerprintを作成(canary起動時に指定) $ openssl x509 -pubkey -noout -in certificate.pem | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | base64 サーバーを起動する # server $ python3 py_server/server.py certificate.pem certificate.key # server for client.html $ python3 -m http.server canaryを起動する 起動時に自己証明書のfingerprintを指定します。(しないとTLSエラーで接続できません) /Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \ --origin-to-force-quic-on=localhost:4433 \ --ignore-certificate-errors-spki-list=[fingerprint] アクセスしてechoすることを確認する http://localhost:8000/client.htmlにアクセスします。 するとこんなページが出てきます。 まず、HTTP/3はコネクションを張るので、URLのところはそのままで「Connect」ボタンを押して接続します。 すると、1RTTのハンドシェイクが行われ、接続できたことがEvent Logに表示さります。 さて、HTTP/3には三種類の通信方式があります。 1. datagram : UDPっぽく使える 2. Unidirectional Stream : 方方向通信のTCPのようなもの 3. Bidirectional Stream : 双方向通信のTCPのようなもの フォームのラジオボタンから方式を選び、救うえのテキストエリアに文字を入力して「Send Data」を押してみましょう。 するとデータが送信され、エコーされることが確認できます。 解説 さて、ソースコードが何をしているのか確認してみましょう。 HTTP/3の詳細な話はまた別でやりたいと思いますが、 WebTransportを利用する上での要点としては三つくらいです。 コネクションの接続 データグラムの送信・受信待ち受け ストリームの送信・受信待ち受け 今回はechoなので、それぞれの通信方式でデータを送り返しています。 - datagramが送信されたらdatagramを送り返す - unidirectional streamを受け取ったらクライアント向きのunidirectional streamで送りかえす - bidirectional streamを受け取ったら、そのままデータを送りかえす クライアントサイド ファイル構造はシンプルで、普通のHTML/JS/CSSです。 client.html client.js client.css 接続する まず、クライアントでは「Connect」ボタンが押されたら接続処理を行い、データグラムとストリームの待ち受け処理を作ります。 接続はとてもシンプルで、WebTransportのコンストラクタにURLを渡してreadyで同期させるだけです。 client.js async function connect() { try { const url = document.getElementById("url").value; wt = new WebTransport(url); addToEventLog('Initiating connection...'); await wt.ready; addToEventLog('Connection ready.'); wt.closed .then(() => {setUIStart(); addToEventLog('Connection closed normally.'); }) .catch(() => {setUIStart(); addToEventLog('Connection closed abruptly.', 'error')}); streamNumber = 1; datagramWriter = wt.datagrams.writable.getWriter(); // データグラムの待ち受け処理 readDatagrams(); // Unidirectional Stream の待ち受け処理 acceptUnidirectionalStreams(); setUIConnected(); } catch (e) { addToEventLog(`Connection failed. ${e}`, 'error'); } } 待ち受け処理 データグラムとストリームの待ち受け処理を作ります。 データグラムはセッションが一つなので待ち受け処理も一つです。 ストリームは複数セッション張れるので、返信用のUnidirectionalを待ち受けます。(Bidirectionalはこちらから張るのでその時に受信処理を書きます) 以下はデータグラムの受信処理です。 受信したデータをログに出力しているだけです。 W3Cのサンプルを見る限りではコメントアウトしている for await ブロックのみで完結するようなのですが、残念ながら実装中のようなのです。 (というか、for await とか初めて知りました。。) client.js async function readDatagrams() { try { /* const decoder = new TextDecoderStream("utf-8"); for await (const data of wt.datagrams.readable.pipeThrough(decoder)) { addToEventLog(`Datagram received: ${data}`); } */ let decoder = new TextDecoder("utf-8"); let reader = wt.datagrams.readable.getReader(); while(true) { const {value, done} = await reader.read(); if (done) { addToEventLog('Done reading datagrams!'); return; } let data = decoder.decode(value); addToEventLog(`Datagram received: ${data}`); } } catch (e) { addToEventLog(`Error while reading datagrams: ${e}`, 'error'); } } 次にUnidirectional Stream の待ち受け処理です。 こちらからUnidirectionalを張ってデータを送信すると、サーバー側でechoとしてUnidirectionalをクライアントに向けて張るので受信処理を登録します。 このデータ受信処理(readFromIncomingStream)はBirectionalでも同じです。 client.js async function acceptUnidirectionalStreams() { try { /* for await (const readable of wt.incomingUnidirectionalStreams) { const number = streamNumber++; addToEventLog(`New incoming unidirectional stream #${number}`); // 受信用の処理を登録する readFromIncomingStream(readable, number); } */ let reader = wt.incomingUnidirectionalStreams.getReader(); while (true) { const {value, done} = await reader.read(); if (done) { addToEventLog('Done accepting unidirectional streams!'); return; } let readable = value; let number = streamNumber++; addToEventLog(`New incoming unidirectional stream #${number}`); // 受信用の処理を登録する readFromIncomingStream(readable, number); } } catch (e) { addToEventLog(`Error while accepting streams ${e}`, 'error'); } } async function readFromIncomingStream(readable, number) { try { /* const decoder = new TextDecoderStream("utf-8"); for await (const chunk of readable.pipeThrough(decoder)) { addToEventLog(`Received data on stream #${number}: ${chunk}`); } */ let decoder = new TextDecoderStream("utf-8"); let reader = readable.pipeThrough(decoder).getReader(); while (true) { const {value, done} = await reader.read(); if (done) { addToEventLog('Stream #' + number + ' closed'); return; } let data = value; addToEventLog(`Received data on stream #${number}: ${data}`); } } catch (e) { addToEventLog(`Error while reading from stream #${number}: ${e}`, 'error'); addToEventLog(` ${e.message}`); } } 送信処理 「Send Data」ボタンが押されたら種類に合わせてデータを送信します。 基本的にはストリームを作成してwriterを取得してwriteするだけです。 client.js // 接続時に設定される let wt, streamNumber, datagramWriter; async function sendData() { const form = document.forms.sending.elements; const rawData = sending.data.value; const data = new TextEncoder("utf-8").encode(rawData); try { switch (form.sendtype.value) { case "datagram": { // データグラムを送信しているだけ await datagramWriter.write(data); addToEventLog(`Sent datagram: ${rawData}`); break; } case "unidi" : { const writable = await wt.createUnidirectionalStream(); const writer = writable.getWriter(); await writer.write(data); await writer.close(); addToEventLog(`Sent a unidirectional stream with data: ${rawData}`); break; } case "bidi": { const duplexStream = await wt.createBidirectionalStream(); const n = streamNumber++; // サーバーからの返信を受信する設定 readFromIncomingStream(duplexStream.readable, n); const writer = duplexStream.writable.getWriter(); await writer.write(data); await writer.close(); addToEventLog(`Sent bidirectional stream #${n} with data: ${rawData}`); break; } } } catch (e) { addToEventLog(`Error while sending data: ${e}`, 'error'); } } クライアント処理は以上です。 for await が使えるようになるとかなりスッキリしそうですね。 サーバーサイド サーバーでは大きく分けて三つの処理が必要になります。 サーバーを初期化する コネクションを処理する イベントを処理する サーバーを初期化する まずはサーバーの初期化です。 TLS証明書やポート番号を指定して起動します。 py_server/server.py if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('certificate') parser.add_argument('key') args = parser.parse_args() configuration = QuicConfiguration( alpn_protocols=H3_ALPN, is_client=False, max_datagram_frame_size=65536, ) configuration.load_cert_chain(args.certificate, args.key) loop = asyncio.get_event_loop() loop.run_until_complete( serve( BIND_ADDRESS, BIND_PORT, configuration=configuration, create_protocol=WebTransportProtocol, )) try: logging.info( "Listening on https://{}:{}".format(BIND_ADDRESS, BIND_PORT)) loop.run_forever() except KeyboardInterrupt: pass コネクションを処理する 次にコネクション処理を書きます。 この辺りはほぼ公式のサンプル通りです。 ハンドシェイクやヘッダーやらOriginなどの処理があります。 py_server/server.py class WebTransportProtocol(QuicConnectionProtocol): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._http: Optional[H3Connection] = None self._handler: Optional[CounterHandler] = None def quic_event_received(self, event: QuicEvent) -> None: if isinstance(event, ProtocolNegotiated): self._http = H3Connection(self._quic, enable_webtransport=True) elif isinstance(event, StreamReset) and self._handler is not None: # Streams in QUIC can be closed in two ways: normal (FIN) and # abnormal (resets). FIN is handled by the handler; the code # below handles the resets. self._handler.stream_closed(event.stream_id) if self._http is not None: for h3_event in self._http.handle_event(event): self._h3_event_received(h3_event) def _h3_event_received(self, event: H3Event) -> None: if isinstance(event, HeadersReceived): headers = {} for header, value in event.headers: headers[header] = value if (headers.get(b":method") == b"CONNECT" and headers.get(b":protocol") == b"webtransport"): self._handshake_webtransport(event.stream_id, headers) else: self._send_response(event.stream_id, 400, end_stream=True) if self._handler: self._handler.h3_event_received(event) def _handshake_webtransport(self, stream_id: int, request_headers: Dict[bytes, bytes]) -> None: authority = request_headers.get(b":authority") path = request_headers.get(b":path") if authority is None or path is None: # `:authority` and `:path` must be provided. self._send_response(stream_id, 400, end_stream=True) return if path == b"/counter": assert(self._handler is None) self._handler = CounterHandler(stream_id, self._http) self._send_response(stream_id, 200) else: self._send_response(stream_id, 404, end_stream=True) def _send_response(self, stream_id: int, status_code: int, end_stream=False) -> None: headers = [(b":status", str(status_code).encode())] self._http.send_headers( stream_id=stream_id, headers=headers, end_stream=end_stream) イベントを処理する 最後にイベントを処理します。 今回はechoサーバーなので、データグラムにはデータグラムで、ストリームにはストリームでデータを送信します。 なお、データグラムについては受信したデータをそのまま返しますが、 ストリームでは受信したデータを self._texts[stream_id]に溜め込み、受信が完了した時点でデータを返しています。 py_server/server.py class CounterHandler: def __init__(self, session_id, http: H3Connection) -> None: self._session_id = session_id self._http = http self._texts = defaultdict(bytes) // ここにストリームの受信データを貯めておきます // これが今回のメインechoな処理 def h3_event_received(self, event: H3Event) -> None: // データグラムを受信した場合、そのままデータグラムで返す if isinstance(event, DatagramReceived): logging.info("receive and send datagram {}".format(event.data.decode(encoding='utf-8'))) payload = event.data # str(len(event.data)).encode('ascii') self._http.send_datagram(self._session_id, payload) // ストリームの場合 if isinstance(event, WebTransportStreamDataReceived): self._texts[event.stream_id] += event.data // 受信が完了するまでデータをバッファする。closedされた時のevent.dataは空になっているのでバッファが必要 if event.stream_ended: payload = self._texts[event.stream_id] // Unidirectionalの場合、クライアントに向けてUnidirectional Streamを作る if stream_is_unidirectional(event.stream_id): logging.info("receive and send unidirectional stream {}".format(payload.decode('utf-8'))) response_id = self._http.create_webtransport_stream( self._session_id, is_unidirectional=True) // Bidirectionalの場合は双方向通信なのでそれに対してデータを返す else: logging.info("receive and send bidirectional stream {}".format(payload.decode('utf-8'))) response_id = event.stream_id self._http._quic.send_stream_data( response_id, payload, end_stream=True) self.stream_closed(event.stream_id) def stream_closed(self, stream_id: int) -> None: try: del self._texts[stream_id] except KeyError: pass まとめ 通信方式が三種類あるため、一見ややこしそうに見えますが、 クライアントもサーバーも処理自体はとてもシンプルです。 今後活用していくにあたってはサーバー側の作り込みやプロトコルの理解が必要になってくるのでその辺りを引き続き調べようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む