- 投稿日:2019-08-11T23:43:36+09:00
JavaScriptでクォートをエスケープしないですむ方法を紹介
こんにちは、プログラミングスクールのレビューサイト「スクールレポート」を運営しているアカネヤ(@ToshioAkaneya)です。
JavaScriptでクォートをエスケープしないですむ方法を紹介
ES6までは、シングルクォートやダブルクォートをエスケープする必要が生じる場合もありました。
しかし。ES6からはバッククォートを使うことでこの問題を解決できます。
例:
`'Hello' "World!"`
この記事が参考になれば幸いです。
終わりに
Ruby on RailsとVueで作成したプログラミングスクールのレビューサイトを運営しています。良ければご覧ください。https://school-report.com/
- 投稿日:2019-08-11T22:51:29+09:00
RPGアツマールのグローバルシグナルでレイドバトルを実装する
概要
RPGアツマールには非同期ネットゲームを想定したAPIが存在します
「グローバルシグナル」を使ってレイドバトルを作ってみたのでその解説になりますグローバルシグナルについて
- 公式
- 以前書いた記事
- RPGアツマールのグローバルシグナルについて
- 基本的な動き方のイメージはこちらに書いてあります
作ったもの
- レイドバトル(エンディング後の要素にしています)
仕様
高HPなボスが出現して、他のプレイヤーも戦闘に参加し、協力してそのボスを倒す
というのを実現します具体的な仕様は下記のとおりです
- ボスについて
- ユーザー1人につきボスが1体出現する
- 出現から24時間経過で消滅する
- ボスを倒した、もしくは、消滅したら、翌日に次のボスが出現する
- 曜日によって出現するボスが変わる
- 他のプレイヤー
- 出現中のボスを選んで戦闘に参加してダメージを与えることができる(これによりあるプレイヤーが他のプレイヤーのボスに対して攻撃することができる)
実装について
送信するシグナルの情報
戦闘終了時に、以下の情報を入れてグローバルシグナルを送信します
これにより、シグナルを受け取ったプレイヤーは別のプレイヤーのボスの存在を知れる状態になります
- 誰のどのボスかを識別する情報
- ユーザーID
- セーブ番号
- ボスのレベル(倒せた場合に次回出現時に1上がる)
- 戦闘結果
- ボスに今回与えたダメージ
- ボスの残りHP
- ボスの情報
- 敵グループID
- 出現した時間(UnixTimeから「時」を算出する)
{"id":101,"sig":[00000,1,14,999865792,134208,196,434706]}戦闘結果に応じて配列に詰めてこのようなjsonを送信しています
シグナルは100byteの制限があるので収まるように注意してデータの内容を決めます誰のどのボスかを識別する
誰の(ユーザーID)どのセーブの(セーブ番号)どのボス(ボスのレベル)か、
によって、誰のどのボスかを一意に識別しています「戦闘結果」のところに書いたHPの計算をするために、シグナルが来たときにどのボスに対してダメージを与える計算をするかに使います
1つのゲームで複数のセーブデータを許す場合、ユーザーIDだけだと一意に識別することができなくなります。ゲームの仕様次第ですが、ゲーム開始時に現在時刻などからハッシュを作ってセーブデータを一意に判別できるようにするなどをやっておくとよいです。今回実装したゲームでは、セーブは複数できるけど途中でセーブ番号が変えられないような仕様にしています。
ただ、この辺ややこしくなりそうならセーブデータは1個しか保存できないようにした方がシンプルになるかと思います。戦闘結果
シグナルの受け取り側がボスの状況を再現するのに必要な情報を入れます
そのために必要な情報が
「誰が何ダメージ与えたか」「合計何ダメージ与えられて残りHPがいくつか」です
必要な理由はそれぞれ以下の通りです
「誰が何だダメージ与えたか」
- シグナルを受ける側は与えたダメージの情報を単純に引くことで、残りHPを計算します。同じボスに対して2人3人4人など複数が同時に攻撃をしかけた場合にはこれの合計値が使われることになります
「残りHPがいくつか」
- すべてのグローバルシグナルは受け取れない可能性があることを考慮しなければならないため、1個のシグナルで1体分のボスを表現できるようにします。そのため「残りHP」の情報も含めています
後は、
シグナルを受け取る度に、以下の2つの計算結果から残りHPが小さくなる方を採用して、自分の手元にあるボスの情報を更新します
- 手元のボスの残りHPから与えたダメージを引く
- シグナルに含まれた残りHP
これにより、ダメージがより進んでいる方が採用されます
ボスの情報
敵グループID
- 曜日ごとにボスを変えるため、敵グループIDを入れておきます
出現した時間
- ボスが出現した時点でUnixTimeの年月日時分秒...の内、「時」を記録しておきます。出現から24時間経った時点で倒せていなかったら消滅した判定をします
まとめ
他プレイヤー間のボスを表現するために必要な情報をこのようにしてグローバルシグナルで実装できました
これによりレイドバトルをグローバルシグナルで実現しています
- 投稿日:2019-08-11T22:47:10+09:00
FireStoreでページングを試してみたメモ
概要
FireBaseのバージョンは6.3.5。
FireStoreを使ったページングについて試したメモ。
最初に件数を取得する以外に何か方法がないか探している。ソース
保存の頻度は高くなく、createdAtにnanosecondsまで同じデータはなく、ユニークな値となっている想定。
nextPageTokenが空白のときは次のページはないという仕様にしている。
以下のソースだと、limitと、保存されているデータの総数が同じときに、
次のデータがないのに「次へ」というボタンが表示されるバグがある。import * as firebase from 'firebase'; const { Timestamp } = firebase.firestore; export async function readEnemies(storeUserId, limit, pageToken) { const db = firebase.firestore(); let query = db.collection("users").doc(storeUserId).collection('enemies').orderBy('createdAt', 'desc').limit(limit); const splitter = ':'; if (pageToken !== "") { const [seconds, nanoseconds] = pageToken.split(splitter); const timestamp = new Timestamp(seconds, nanoseconds); query = query.startAfter(timestamp); } const querySnapshot = await query.get(); const enemies: any[] = []; await querySnapshot.forEach((doc) => { const enemy = doc.data(); enemies.push(enemy); }); if (querySnapshot.docs.length < limit) { // limitより少なければ、次のデータはないとする ... limitと同値の時は次へが表示されてしまう return { enemies, nextPageToken: "" }; } const last = querySnapshot.docs[querySnapshot.docs.length - 1]; const lastData = last.data(); const time = lastData.createdAt; let nextToken = `${time.seconds}${splitter}${time.nanoseconds}`; return { enemies, "nextPageToken": nextToken }; }参考ソース
参考
Firestore で いいね順(Score順)Sort + Paging するポイント
クエリカーソルを使用したデータのページ設定
firebase. firestore. Timestamp
Firestoreを試してみた
Firebase Cloud Firestoreの使い方
TypeScriptからFirestoreを使いやすくするfirestore-simple v4をリリースしました
Firestoreお役立ちリンク
- 投稿日:2019-08-11T22:21:21+09:00
Firebase Functions + TypeScriptをparcelで事前ビルドしてやりすごす
Firebase functionsは現在標準でTypeScriptに対応している。
しかしHostingなど他の機能も使いたい場合や、それらとモジュールを共有したい場合、tsconfigやpackage.jsonが複数のディレクトリに割れたりしてちょっとしんどくなる。
この場合lernaなどでmonorepo構成を取るのが良いのだろうが、TypeScriptが絡んだりするとなかなかまだハマりどころが少なく無く面倒になりがちだ。そこで以前AWS Lambda向けのファイルをparcelでビルドしたのと似たようなことをやってみたら思ったより悪くなかったのでまとめておく。
1. 準備
まずは適当にディレクトリを作るとこから
$ mkdir some-firebase-project $ cd some-firebase-project $ yarn init -y $ touch firebase.jsonparcelとtypescriptも入れてしまおう
$ yarn add parcel typescript $ yarn tsc --initあと今回は
firebase init
は使わないのでそこは自前で行う$ mkdir functions $ touch functions/package.json2. package install
次にプロジェクト直下でパッケージインストール。トップディレクトリでfunctionsに必要なパッケージを入れる。最終的にbundleしたものをアップするのでpeerDependencyも入れる
$ yarn add firebase-admin @firebase/database @firebase/app @firebase/app-types @firebase/database-types firebase-tools3. functionファイルの用意
function本体となるファイルを配置する。
$ mkdir src $ mkdir src/functions// src/functions/index.ts import * as functions from "firebase-functions" // 全インポートにしないとひっかかるっぽい exports.helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!") })4. スクリプト設定(本題)
package.jsonをこんな感じで設定
{ "scripts": { "build:functions": "parcel build src/functions/index.ts --target=node -d functions -o index.js --bundle-node-modules --no-source-maps --no-minify",だいたいAWS Lamdaの場合と一緒で、
--target=node
、--bundle-node-modules
にしている。
またfirebase-admin関連がsourcemap関連のwarningを吐き出してくるので--no-source-maps
を付けている。
また吐き出し先を-d functions
でfunctions下にしている。
--no-minify
をしてるのはビルド速度を上げたい&サーバーサイドのスクリプトなのでminifyする意味あんまり感じない&デバッグ楽そう ぐらいな気持ちなので好みで決めていいこれで
yarn build:functions
が出来た。4.5. Cannot resolve dependency 'http2' の対策をする
本当はここまでで通るはずなのだが、そのまま実行すると
Cannot resolve dependency 'http2'
のエラーが出る事がある。
どうもparcelの問題として、http2モジュールをうまく読み込めずビルドが失敗する状態にあるようだhttps://github.com/parcel-bundler/parcel/issues/2921
ということでその場合は下記のようにhttp2パッケージをインストールしてしまえば解決する
$ yarn add http25. firebase.jsonの準備
次にfirebase.jsonを記述する。だいたいこんな感じ。predeployで指定を与える部分が重要
{ "functions": { "source": "functions", "predeploy": "yarn build:functions" }, "hosting": { ... } }6. functions/package.jsonをいじる
AWS Lambdaと違ってpackage.jsonが無いとdeploy時に失敗してしまうので、これを回避するためにpackage.jsonを置いとく。
バージョンは10にしておく。今回はparcel側で--bundle-node-modules
するやり方をとっているのでdependencies
は不要だ。functions/package.json{ "name": "functions", "engines": { "node": "10" }, "main": "index.js", "private": true }7. gitignoreする(オプション)
細かいがgitignoreとしてfunctions/index.jsを追加しておくと良い。
functions/index.js dist好みでなければ吐き出し先を
functions/lib/
などディレクトリ先にしてそこごとignoreするのも良いだろう。8. deploy
レッツデプロイ。
$ firebase deploy --only functionsおまけ: hostingがここに入った場合
hositngが入るとこんな具合のディレクトリ構成になる。
. ├── dist # hostingの出力先 │ ├── hosting.09bd9689.js │ └── index.html ├── functions # functionsの出力先 │ ├── index.js │ └── package.json ├── src │ ├── functions # functionsのソース │ │ └── index.ts │ ├── hosting # hostingのソース │ │ ├── index.html │ │ └── index.tsx │ └── lib # 共通部分 │ └── database-client.ts ├── firebase.json ├── package.json ├── renovate.json ├── tsconfig.json └── yarn.lockfirebase.jsonとpackage.jsonはこんな感じになる
package.json"script": { "build": "parcel build src/index.html", ...firebase.json"hosting": { "public": "dist", "predeploy": "yarn build", "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], "rewrites": [ { "source": "**", "destination": "/index.html" } ] }
- 投稿日:2019-08-11T21:26:56+09:00
【Vue.js】Vue CLIでVue.jsを動かす〜プロジェクト作成まで
Vue.jsとは
- JavaScriptのフレームワーク。
- どのような規模・段階のアプリケーションにも対応することができる(プログレッシブフレームワーク)
- ページの一部で部分的に使用することもできるし、大規模なSPA(シングルページアプリケーション)を構築することもできる。
プログレッシブフレームワークとは、Vue.jsの生みの親であるEvan You氏の提唱する概念です。
参考記事
Vue.jsについて学習してみた <基礎編>
Vue.js概要?Vue CLIとは
- 迅速なVue.js開発を支援するためのコマンドラインインターフェイス。
- Vue.jsを利用した開発には、様々なパッケージやツールについての知識・複雑な設定が必要になる。それを自動化し、開発環境の構築を補助してくれる。
- 個別に公開されているパッケージ(ビルドツール、コンパイラなど) の集合体でもある。Vue CLIを利用することにより、そこから必要な機能だけを使うことができる。
参考記事
インストール
あらかじめディレクトリを作成し、初期化しておきます。(今回初期化の必要はないかもしれませんが、念の為。)
$ mkdir vue_cli_test $ cd vue_cli_test $ npm initnpmによるインストール
グローバルインストールで行っています。
$ npm install -g @vue/cli加えて、
@vue/cli-init
もインストールします。$ npm install -g @vue/cli-init@vue/cli-initのインストールがされていない場合、プロジェクト作成のコマンド
npm init
実行時に以下のエラーが発生します。Command vue init requires a global addon to be installed. Please run npm install -g @vue/cli-init and try again.グローバルインストールの場合、パッケージは
npm list -g
で確認できるディレクトリ配下のnode_modulesディレクトリ
にインストールされます。$ npm list -g /Users/yuki/.nodebrew/node/v12.7.0/libパッケージ名を指定して確認することもできます。
$ npm list -g @vue/cli /Users/yuki/.nodebrew/node/v12.7.0/lib └── @vue/cli@3.10.0環境変数の設定
このままだとvueコマンドが使えないため、
.bash_profile
にパスを設定します。
まず、コマンドがどこに存在するのか確認します。$ npm bin -g /Users/yuki/.nodebrew/node/v12.7.0/bin.bash_profileに追記します。
$ vi ~/.bash_profileexport PATH="$HOME/.nodebrew/node/v12.7.0/bin:$PATH"反映させます。
$ source ~/.bash_profileVue.jsのバージョン確認
vueコマンドが通るようになったので、Vue.jsのバージョンを表示してみます。
確認ができれば、Vue CLIのインストールは成功です。$ vue -V 3.10.0プロジェクト作成
npm init テンプレート名 プロジェクト名
でプロジェクトを作成します。今回、テンプレートはwebpackを指定します。
テンプレートの種類については、こちらの記事が参考になります。
Vue-cli(webpack)解剖 ーディレクトリ構成ー$ vue init webpack my-appいくつか質問をされます。
今は余分な機能を入れたくなかったので、以下の質問はNoにしました。// SPA構築に必要なVue.js公式ルータ ? Install vue-router? No // 構文チェックツール ? Use ESLint to lint your code? No // テストツール ? Set up unit tests No // テストツール ? Setup e2e tests with Nightwatch? Noプロジェクト名である
my-appディレクトリ
が作成され、必要な機能がインストールされます。
最後の方にあるメッセージの通り、my-appディレクトリに移動します。To get started: cd my-app npm run dev$ cd my-app次に
npm run dev
でサーバを起動します。
dev
というエイリアスコマンドは、プロジェクト名のディレクトリ/package.json
のscript
で設定されています。デフォルトでは以下の設定になっています。
npm start
やnpm run start
でもサーバを起動することができるようです。myapp/package.json"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "build": "node build/build.js" },$ npm run dev表示されるメッセージの通り
http://localhost:8080
にアクセスして、Vue.jsのデモ画面が確認できれば成功です。Your application is running here: http://localhost:8080
- 投稿日:2019-08-11T19:59:21+09:00
【JS】動的なカテゴリーボックスの実装方法
はじめに
動的なセレクトボックスを実装するにあたり、非同期通信を用いたので備忘録としてここ記す。
テーブル
categoriesテーブル
Column Type Options category-name string null false ancestry string null false ancestryのGemを使い多階層としている。
親、子、孫が一つのカラムの中に存在している。実装
category.js$(function(){ function appendOption(category){ // optionの作成 var html = `<option value="${category.id}">${category.name}</option>`; return html; } function appendChidrenBox(insertHTML){ // 子セレクトボックスのhtml作成 var childSelectHtml = ''; childSelectHtml = `<div class='product-select-wrapper' id= 'children_wrapper'> <div class='product_category-select'> <select class="category_select-box" id="child_category" name="category_id"> <option value="---">---</option> ${insertHTML} </select> <i class='fa fa-chevron-down'></i> </div> <div class= 'product_select-grandchildren'> </div> </div>`; $('.product_select-children').append(childSelectHtml); } function appendgrandChidrenBox(insertHTML){ // 孫セレクトボックスのhtml作成 var grandchildrenSelectHtml = ''; grandchildrenSelectHtml = `<div class='product-select-wrapper' id= 'grandchildren_wrapper'> <div class='product_category-select'> <select class="category_select-box" id="grandchild_category" name="category_id"> <option value="---">---</option> ${insertHTML} </select> <i class='fa fa-chevron-down'></i> </div> <div class= 'product_select-grandchildren'> </div> </div>`; $('.product_select-grandchildren').append(grandchildrenSelectHtml); } $(document).on('change', '#category_select', function(){ // 親セレクトボックスの選択肢を変えたらイベント発火 var productcategory = document.getElementById('category_select').value; // ↑ productcategoryに選択した親のvalueを代入 if (productcategory != ''){ // ↑ productcategoryが空ではない場合のみAjax通信を行う。選択肢を初期選択肢'---'に変えると、通信失敗となってしまうため。 $.ajax({ url: 'category_children', type: 'GET', data: { productcategory: productcategory }, dataType: 'json' }) .done(function(children){ // 送られてきたデータをchildrenに代入 var insertHTML = ''; children.forEach(function(child){ // forEachでchildに一つずつデータを代入。子のoptionが一つずつ作成される。 insertHTML += appendOption(child); }); appendChidrenBox(insertHTML); $(document).on('change', '#category_select', function(){ // 通信成功時に親の選択肢を変えたらイベント発火。子と孫を削除。selectのidにかけるのではなく、親要素にかけないと残ってしまう $('#children_wrapper').remove(); $('#grandchildren_wrapper').remove(); }) }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) } }); // documentにしないとリロードしなければイベントが発火しない $(document).on('change', '#child_category', function(){ var productcategory = document.getElementById('child_category').value; if (productcategory != ''){ $.ajax ({ url: 'category_grandchildren', type: 'GET', data: { productcategory: productcategory }, dataType: 'json' }) .done(function(grandchildren){ var insertHTML = ''; grandchildren.forEach(function(grandchild){ insertHTML += appendOption(grandchild); }); appendgrandChidrenBox(insertHTML); $(document).on('change', '#child_category',function(){ $('#grandchildren_wrapper').remove(); }) }) .fail(function(){ alert('カテゴリー取得に失敗しました'); }) } }); });routes.rbRails.application.routes.draw do devise_for :users root to: "items#index" # ここから itemsコントローラのAjax通信でのアクション先指定 resources :items do collection do get 'category_children' get 'category_grandchildren' end end # ここまで enditems_controller.rbdef new @category = Category.all.order("id ASC").limit(13) # categoryの親を取得 end def category_children @category_children = Category.find(params[:productcategory]).children end # Ajax通信で送られてきたデータをparamsで受け取り、childrenで子を取得 def category_grandchildren @category_grandchildren = Category.find(params[:productcategory]).children end # Ajax通信で送られてきたデータをparamsで受け取り、childrenで孫を取得。(実際には子カテゴリーの子になる。childrenは子を取得するメソッド)category_children.json.jbuilderjson.array! @category_children do |child| json.id child.id json.name child.category_name endategory_grandchildren.json.jbuilderjson.array! @category_grandchildren do |grandchild| json.id grandchild.id json.name grandchild.category_name endnew.html.haml.product_select-details .product_select-group .product_header カテゴリー .product_require 必須 .product_category-select = f.collection_select :category, @category, :id, :category_name, { prompt: "---" }, { class: "category_select-box", id: "category_select" } %i.fa.fa-chevron-down .product_select-childrenさいごに
間違っているところがあればご指摘下さい
- 投稿日:2019-08-11T18:10:10+09:00
【読切】非同期処理の沼~Vue.js Django編~
あらすじ
2019年8月10日。私はVue.jsとDjango REST Frameworkを使い、ある業務システムの画面開発を担当していた。
フレームワークなんて代物はまだ触り始めて2,3か月。とは言えどこを触れば何が変わるか多少は理解してきている自負もあった。
そんな良くも悪くも弾みのついていた私を戒めるかのように、単体テスト中、そいつは突然現れた。出会い
『更新中の…ステータスが…… エラーコード:409』
「(動作確認では何度も正常に作動していた機能開発...そんなはずは...)」
NG判定とエラー内容をテスト記録として記述する。"""
HTTP 409 Conflict はリクエストが現在のサーバーの状態と競合したことを示すステータスコード。競合は PUT メソッドを使用したリクエストのレスポンスで最も発生しやすい。例えば、サーバーにすでに存在しているファイルよりも古いバージョンのファイルをアップロードした際に409の応答が返され、バージョン管理システムの競合が発生する可能性がある。
"""
409 Conflict困った困った。なけなしの知識と経験であれやこれやと思考した。
そして画面の再描画後、再度そいつは現れた。『更新中の…ステータスが…… エラーコード:409』
非同期処理の沼
「ここか。」
答えに辿り着くのに長くはかからなかった。heavyHoge.ts// post(url, data, loading) // url : 呼び出し先のURL // data : REST側に送りたいデータ // loading : 処理終了までローディング画面を表示 // 重ための処理 private async heavyProcess(): Promise<void> { const payload = '画面から送る何かのデータ群'; this.$http.post(url='hoge-url1', data={'payload': payload}, loading=false); this.$http.post(url='hoge-url2', data={'payload': payload}, loading=false); // 画面の更新処理 this.onShown(); }ボタンを押下して、この処理が走っている間に他の作業も進められるようにしたい。数十秒~数分なんて流石に待っていられない。
そのような声は当然にあり、この処理は上記の通り非同期処理で実装していた。
ここでの2つのhttp.post(以下、処理①と処理②)は類似処理を行っているが、具体的には下記の処理を行っている。1.画面内のデータを取得しREST側へPOST
2.取得したデータの条件が合えば、外部APIへ有料の関連データを取得
3.種々のCRUD処理を行う更にエラーの発生パターンについて調べたところ、大きく下記の2つであった。
1.処理①が上記1~3を終えて、戻り値を返して処理②を行おうとした直後
2.処理①のみor処理②のみが走るように画面側で予め条件設定し、1度目の処理を終えた直後に同じ処理のトリガーを引いた直後つまり、処理量の多さにCRUD処理が実際には完了しきいっていない状態の時に再び処理①②を行うことでエラーが起きていた。
それを示すように awaitを付記するとloading画面は処理終了まで続くがエラーは消える。(当然っちゃ当然だが)heavyHoge.ts// post(url, data, loading) // url : // data : 何かのデータ群 // loading : 処理終了までローディング画面を表示 // 重ための処理 private async heavyProcess(): Promise<void> { const payload = '画面から送る何かのデータ群'; await this.$http.post(url='hoge-url1', data={'payload': payload}, loading=false); await this.$http.post(url='hoge-url2', data={'payload': payload}, loading=false); // 画面の更新処理 this.onShown(); }とにかく、今回のエラーの発生要因やその背景なんかを整理すると
・外部APIが有料ということもあり、連続or多数の本チャンデータ取得パターンの確認を怠った。
・1度のボタン押下で行う非同期処理を2つに分けてしまった。
(※片方は他画面で使用しているViewに飛んでいた為、このような実装になった)
・REST側で各処理を行うViewを呼び出す為の、共通クラスを用意しなかった。
(※やってることは2個目と同じ)しかし、上記の課題を全てクリアして当初の設計通り実装しようとしても、まだエラーの発生パターン2が解決しない。
そう、ユーザーが短時間に連続で同じ処理を行おうとした時だ。完全に非同期処理の沼に嵌まってしまったのである。2019年8月11日。27歳の誕生日を迎えた今日、未だ解決策は見つかっていない。
To Be Continued
- 投稿日:2019-08-11T17:48:44+09:00
javascriptを一行使用するだけで、プルダウンメニューで選択した瞬間に画面遷移
プルダウンメニューから選択した瞬間に画面を移動する機能の実装に躓いたのでメモします。
以下のソースをhtmlファイルに記述します。<select onChange="location.href=value;"> <option class="selected">並び替え</option> <option value="URL1">新しい順</option> <option value="URL2">参考になった順</option> </select>これで、新しい順を選択するとURL1、参考になった順を押すとURL2に遷移するようになります。
onChange="location.href=value;"
の一文をselectタグに追加するだけで機能するので便利だと感じました。
- 投稿日:2019-08-11T16:18:32+09:00
JavaScript ES6 【forEach/map/filter/find/every/some/reduce】
ES6を勉強したので自分のためにメモする
UdemyでES6を結構しっかり学んだけど、関数系がごっちゃになりがちなので自分用にメモします。
全部便利そうなので今後しっかり使っていきたい!forEach
オブジェクトに繰り返し処理を行いたいときに使用する。
let images = [ { height: 10, width: 20 }, { height: 20, width: 30 }, { height: 30, width: 40 }, ]; let areas = []; images.forEach(function(image) { areas.push(image.height*image.width); }); areas; ----- result ------ [200,600,1200] // アロー関数使用バージョン images.forEach(image => areas.push(image.height*image.width));map
オブジェクトから特定のキーオブジェクトを抜き出したいときに使用する。
let colors = [ { jp: '赤', eng: 'red' }, { jp: '青', eng: 'blue' }, { jp: '黄', eng: 'yellow' }, ]; let engColor = colors.map(function(color) { return color.eng; }); engColor; ----- result ------ ["red","blue","yellow"] // アロー関数使用バージョン let engColor = colors.map(color => color.eng);filter
条件に合致するものだけ抜き出したいときに使用する。
let includeCaffeine = [ { name: 'コーヒー', caffeine: true }, { name: '麦茶', caffeine: false }, { name: '緑茶', caffeine: true }, ]; includeCaffeine.filter(function(drink) { return drink.caffeine; }); ----- result ------ [{"name":"コーヒー","caffeine":true},{"name":"緑茶","caffeine":true}] // アロー関数使用バージョン includeCaffeine.filter(drink => drink.caffeine);find
条件に合致する一番最初の要素を見つけたいときに使う。
let dogsOkCafe = [ { name: 'CafeA', dogsOK: false }, { name: 'CafeB', dogsOK: true }, { name: 'CafeC', dogsOK: true }, ]; dogsOkCafe.find(function(cafe) { return cafe.dogsOK; }); ----- result ------ {"name":"CafeB","dogsOK":true} // ↑最初のひとつだけなので、CafeCははいらない // アロー関数使用バージョン dogsOkCafe.find(cafe => cafe.dogsOK);every
すべてのオブジェクトが条件に合致しているかどうか判定するときに使用する
let places = [ { name: '北海道', visited: true }, { name: '沖縄', visited: true }, { name: '群馬', visited: true }, ]; places.every(function(place) { return place.visited; }); ----- result ------ True // アロー関数使用バージョン places.every(place => place.visited);some
ひとつでも条件に合致しているものがあるか判定するときに使用する
let places = [ { name: '北海道', visited: true }, { name: '沖縄', visited: false }, { name: '群馬', visited: false }, ]; places.some(function(place) { return place.visited; }); ----- result ------ True // アロー関数使用バージョン places.some(place => place.visited);reduce
オブジェクトを違う型にしたりするときに使用する
var fruits = [ { type: 'Apple' }, { type: 'Strawberry' }, { type: 'Strawberry' }, { type: 'Strawberry' }, { type: 'Apple' } ]; fruits.reduce(function(prev, fruit) { if (fruit.type === 'Apple') { prev.apple += 1; return prev; } if (fruit.type === 'Strawberry') { prev.strawberry += 1; return prev; } }, { apple: 0, strawberry: 0 }); ----- result ------ {"apple":2,"strawberry":3} // アロー関数使用バージョン fruits.reduce((prev, fruit) => { if (fruit.type === 'Apple') { prev.apple += 1; return prev; } if (fruit.type === 'Strawberry') { prev.strawberry += 1; return prev; } }, { apple: 0, strawberry: 0 });
- 投稿日:2019-08-11T15:08:23+09:00
Vue.js超基本の使い方まとめ
最近やっているプロジェクトでjQueryを使ってたのですが、jQueryの面倒さに発狂したくなってしまい、なんとかならないものかと思っていたところ、Vue.jsに出会いましたので学習をはじめました。
参考
今回udemyを一通り受講し、振り返りをしているため非常に参考とさせていただきました。非常に分かりやすい構成になっているため、初めて学習を始める人はまず受講してみることをオススメ致します。
Vue.jsの基本構文
データバインディング
Mustache構文
Vue.jsではMustache構文を使うことで、簡単にVueインスタンスデータとHTMLを結びつけることができます。
このデータの結びつけのことをデータバインディング
といいます。下記の例を表示すると
{{ message }}
にはHello Vue.js!が表示されます。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Vue Sample</title> </head> <body> <div id="example"> {{ message }} </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> </body> </html>main.jsvar app = new Vue({ el: "#example", data: { message: "Hello Vue.js!" } });v-bind
簡単なテキストのデータバインディングであればMustache構文を使えましたが、DOM要素の属性をデータバインディングする場合にはMustache構文はエラーとなり使えません。
※以降Vueに関わる部分のみHMTLを記載
index.html<div id="example-error"> <a href="{{ url }}">Google</a> </div>main.jsvar app = new Vue({ el: "#example-error", data: { url: "https://google.com" } });そこで使用するのが
v-bindディレクティブ
です。
v-bindディレクティブ
を使えばDOM要素の属性を動的に切り替えることができます。例1:href属性のデータバインディング
下記例だとhref属性のurlにGoogleのURLがデータバインディングされます。index.html<div id="example1"> <a v-bind:href="url">Google</a> </div>main.jsvar app = new Vue({ el: "#example1", data: { url: "https://google.com" } });例2:value属性のデータバインディング
下記例ではinputタグのvalue属性がデフォルトでおはよう!
と表示されます。index.html<div id="example2"> <input v-bind:value="message"> <p>{{ message }}</p> </div>main.jsvar app = new Vue({ el: "#example2", data: { message: "おはよう!" } });しかし初期値の
おはよう!
は正しく表示されていましたが、文字を書き換えた時にpタグが書き換わっていなかったことに気づくと思います。これはv-bindによるデータバインディングは
・jsで更新したdataはテンプレートに更新されるが、 ・テンプレート側で更新してもdataが更新されないという性質があるためです。そこで登場するのが、
v-on
メソッドです。v-on
v-on
メソッドはイベント処理をするためのディレクティブです。
input要素のテキストエリアに入力する度にinputイベントが発生するので、v-bindに追加でv-onを追加してdataを更新する処理を追加します。index.html<div id="example"> <input v-bind:value="message" v-on:input="message = $event.target.value" > <p>{{ message }}</p> </div>main.jsvar app = new Vue({ el: "#example", data: { message: "おはよう!" } });
- 初期値で
message
におはよう!が入力されています- そのためMustasche構文でかいているpタグにも
おはよう!
が表示されます- 入力欄に
おはよう!
の!を削除するとinputイベントが発生し、messageを書き換えます- するとMustasche構文でかいているpタグにも
おはよう
に更新されますこのようにjsからテンプレートのデータを更新するだけでなくく、テンプレートからもデータを更新することを 双方向データバインディングといいます。下記の2行は全く同じ表現で、どちらもmessageに対して双方向データバインディングすることができます。
v-model
v-bindとv-onを組み合わせた双方向データバインディングの例を示しましたが、これは
v-model
というディレクティブで表現することができます。// どちらも同じ <input v-model="message"> <input v-bind:value="message" v-on:input="message = $event.target.value">v-bindとv-onの省略記法
v-bindとv-onは頻繁に使用するため省略記法があります。
ディレクティブ 完全な構文 省略記法 :v-bind <a v-bind:href="url"> ... </a>
<a :href="url"> ... </a>
:v-on <a v-on:click="func"> ... </a>
<a @click="func"> ... </a>
繰り返し処理
v-for
v-for
を使えば繰り返し文を使うことができます。
またv-for="(user, index) in users"
とすることで配列のキーを取り出すことも可能です。リストの例<ul id="example"> <li v-for="(user, index) in users" > {{ member }} </li> </ul>var app = new Vue({ el: "#example", data: { users: ["佐藤", "鈴木", "山田"] } });表示・非表示の切り替え
v-if, v-if-else, v-else
v-if
ディレクティブではブロックを条件に応じて描画することができます。<div id="app"> <div v-if="toggle"> <p>Hello</p> <div v-else> <p>World</p> </div> </div>var app = new Vue({ el: '#app', data: { toggle: true // 切り替える } })v-show
v-show
ディレクティブは属性値がtrueとなる場合に要素を表示します。<div id="app"> <div v-show="toggle"> <p>Hello</p> </div> </div>var app = new Vue({ el: '#app', data: { toggle: true // 切り替える } })v-ifとv-showの比較と使い分け
比較
v-ifとv-showは表示・非表示の方法に明らかな違いがあります。
v-if v-show 切り替え方法 ブロックごと作成・削除 ブロックを残したまま表示・非表示を切り替え 初期描画コスト 低い 高い 描画切り替えコスト 高い 低い 一方でv-showは非表示にする時にブロックに対してCSSの
display:none
を使って切り替えます。
使い分け
v-if
・ 初期描画コストが低く、描画切り替えのコストが高い
・ そのため切り替えの頻度が低いなら、初期描画コストの低いv-if
を使用する
v-show
・ 初期描画コストが高く、描画切り替えのコストが低い
・ そのため切り替えの頻度が多いなら、描画切り替えコストの低いv-if
を使用する算出プロパティ
computed
算出プロパティは関数で算出したデータを返すことができるプロパティのことです。要はgetterやsetterの役割だと思ってもらえれば良いかと思います。
<div id="app"> {{ reverseMessage }} </div>var app = new Vue({ el: '#app', data: { message: "Hello world!" }, computed: { // data:messageの文字列を反転させる reverseMessage: function() { return this.message.split('').reverse().join('') } } })算出プロパティとメソッドの比較
先ほどの例だと、メソッドを使っても同じことが実現できます。
<div id="app"> {{ reverseMessage }} {{ reverseMessageMethod() }} </div>var app = new Vue({ el: '#app', data: { message: "Hello world!" }, computed: { reverseMessage: function() { return this.message.split('').reverse().join('') } }, methods: { reverseMessageMethod: function() { return this.message.split('').reverse().join('') } } })そこで算出プロパティとメソッドの違いをまとめると3点の違いがあります。
算出プロパティ メソッド 記法 ()不要 ()必要 使い方 getter, setter getter キャッシュ される されない 算出プロパティのキャッシュ
算出プロパティとメソッドの使い分けとしてキャッシュの有無があります。
Mathのランダム関数を使ったときの例を用いて説明します。<div id="example"> メソッド <ol> <li> {{ getDate() }}</li> <li> {{ getDate() }}</li> <li> {{ getDate() }}</li> </ol> 算出プロパティ <ol> <li>{{ date }}</li> <li>{{ date }}</li> <li>{{ date }}</li> </ol> </div>var app = new Vue({ el: "#example", computed: { date: function () { console.log("computed"); return Math.random(); } }, methods: { getDate: function () { console.log("methods"); return Math.random(); } } });出力結果メソッド 0.523744236259859 0.29754866940841374 0.3024997149055251 算出プロパティ 0.19494595412700444 0.19494595412700444 0.1949459541270044このように同じMath.random()を使っても、算出プロパティは全て同じ値に対してメソッドは全てバラバラの値になりました。
また算出プロパティは描画は3度していますが、コンソールログには1度しかログが出ていません。算出プロパティとメソッドの使い分け
算出プロパティ
・ getter以外にもsetterを定義したい場合
・ キャッシュを効かせて、処理を高速化させたい場合メソッド
・ あえて処理を高速化させたくない場合さいごに
今回初めてVue.jsを使ってみましたが、jQueryのように直接DOM操作をするわけでなく、少ない量でかつわかりやすくDOMの操作をすることができ非常に期待が高まりました。
使い分けに関する情報など、初学者に少しでも役に立てば幸いです。
- 投稿日:2019-08-11T13:21:48+09:00
Vue.jsでサクサク!SVG内に線を引いてみる
はじめに
前回の記事続きです。
SVGとVue.jsを組み合わせて今回は、Pathを使って線を引いてみるという
処理を実装して見たいと思います。前回の記事と組み合わせることで、
マインドマップのようなWebアプリも作れるかもしれないです。
(その場合はpath要素と図形要素を保持して、線を設定する形になると思います。)早速コードを見ていきましょう!
Path要素を入れる
まず、線を表現するのに必要なPath要素を、
SVGタグ内に記載いたします。
v-model内にリストで表現し、線でつないだ分だけ可変的に生成できるようにします。線を引いている時(確定前要素)と、線を引いた後(確定後要素)で、
Pathを分けております。
今回オプション要素で、線の太さや、色を全体に可変的に適用する設定もおまけで入れてます。SVGDemo.vue<template> <div class="container"> <div class="setting-box"> <!-- 色を変えるとこ --> <p>色</p> <input type="color" v-model="strokeFill" /> <!-- 線の太さとか --> <p>線の太さ</p> <input type="range" v-model="strokeWidth" /> </div> <!-- SVG定義 --> <svg :width="width" :height="height" :viewBox="viewport" @wheel="zoompan" @mousedown="lineNode($event, idx)"> <!-- 線を引いた後 --> <path :d="path" fill="none" :stroke="strokeFill" :opacity="strokeOpacity" :stroke-width="strokeWidth" stroke-linecap="round" v-for="(path, idx) in pathList" :key="idx" v-if="pathList.length > 0"/> <!-- 線を引いている最中 --> <path :d="pathTemp" fill="none" :stroke="strokeFill" :opacity="strokeOpacity" :stroke-width="strokeWidth" stroke-linecap="round" v-if="isLineNode"/> </svg> </div> </template>イベントハンドラを実装
今回は図形からドラッグしていくと
線が生成され、図形同士で繋げた場合に
生成されるようにしていきます。SVGDemo.vue<script> export default { name: 'SVGDemo', data () { return { width: 500, height: 500, ratio: 1, dx: 0, dy: 0, viewport: '0 0 500 500', isLineNode: false, beforeMouseX: null, beforeMouseY: null, selectIdx: 0, pathList: [], pathTemp: '', strokeFill: '#525', strokeOpacity: 1, strokeWidth: '5', moveLineX: 0, moveLineY: 0, startLineX: 0, startLineY: 0, } }, // マウス操作関連 mounted () { console.log('MOUNT LISTENER ON') document.addEventListener('mouseup', this.mouseUp) document.addEventListener('mousemove', this.mouseMakeLine) }, beforeDestroy () { console.log('MOUNT LISTENER OFF') document.removeEventListener('mouseup', this.mouseUp) document.removeEventListener('mousemove', this.mouseMakeLine) }, methods: { zoompan (e) { var [x, y, w, h] = this.viewport.split(' ').map(v => parseFloat(v)) if (e.ctrlKey) { // 拡大(Y軸が上がる場合) 縮小(Y軸が下がる場合) if (e.deltaY > 0) { w = w * 1.01 h = h * 1.01 } else { w = w * 0.99 h = h * 0.99 } this.makeViewBox(x, y, w, h) this.ratio = w / this.width e.preventDefault() } else { // 移動 if ((this.dx + e.deltaX > -this.width && this.dy + e.deltaY > -this.width) && (this.dx + e.deltaX < this.width * 2 && this.dy + e.deltaY < this.width * 2)) { this.makeViewBox(x + e.deltaX, y + e.deltaY, w, h) this.dx += e.deltaX this.dy += e.deltaY } } }, // viewboxを作成 makeViewBox (x, y, w, h) { this.viewport = [x, y, w, h].join(' ') }, // マウスのクリックが終わった段階で初期化 mouseUp (e) { this.isLineNode = false this.beforeMouseX = null this.beforeMouseY = null this.pathList.push(this.pathTemp) this.pathTemp = null e.preventDefault() }, // move中は前回まで動かした差分を取りながら座標を変化させていく mouseMove (e) { if (!this.isMove) return var mouseX = e.offsetX * this.ratio + this.dx var mouseY = e.offsetY * this.ratio + this.dy var dx = 0 var dy = 0 if (this.beforeMouseX && this.beforeMouseY) { dx = mouseX - this.beforeMouseX dy = mouseY - this.beforeMouseY } this.beforeMouseX = mouseX this.beforeMouseY = mouseY var tempX = dx + Number(this.rects[this.selectIdx].x) var tempY = dy + Number(this.rects[this.selectIdx].y) if (tempX > 0) this.rects[this.selectIdx].x = tempX if (tempY > 0) this.rects[this.selectIdx].y = tempY e.preventDefault() }, lineNode (e, i) { this.startLineX = e.offsetX * this.ratio + this.dx this.startLineY = e.offsetY * this.ratio + this.dy this.moveLineX = e.offsetX * this.ratio + this.dx this.moveLineY = e.offsetY * this.ratio + this.dy this.isLineNode = true e.preventDefault() }, mouseMakeLine (e) { if (!this.isLineNode) return var mouseX = e.offsetX * this.ratio + this.dx var mouseY = e.offsetY * this.ratio + this.dy var dx = 0 var dy = 0 if (this.beforeMouseX && this.beforeMouseY) { dx = mouseX - this.beforeMouseX dy = mouseY - this.beforeMouseY } this.beforeMouseX = mouseX this.beforeMouseY = mouseY var tempX = this.moveLineX var tempY = this.moveLineY tempX += dx tempY += dy if (tempX > 0) this.moveLineX = tempX if (tempY > 0) this.moveLineY = tempY this.pathTemp = 'M' + this.startLineX + ',' + this.startLineY + ' L' + this.moveLineX + ',' + this.moveLineY } } } </script>完成
ここまでロジックを記載していくと
このような形になるかと思います。
ベジェ曲線など組み込んでカーブさせても
面白そうですね!最後に
いかがでしたでしょうか?
Vue.jsとSVGを使うと色々なことができるので、
また色々記事を書いていけたら、と思います。現在弊社では、HRモンスターと呼ばれる
採用の新しいスタイルを提供するサービスをローンチいたしました。ローンチ後のさらなる機能追加、改善などのPDCAサイクルを回すべく、
エンジニアを募集しております。
https://www.wantedly.com/projects/341182Kubernetes、Vue.js(Javascript)、Django(Python)といったモダンな技術を使って、
開発しておりますので、もしご興味がある方はぜひ、ご応募お待ちしております。
- 投稿日:2019-08-11T13:04:27+09:00
【Programming News】Qiitaまとめ記事 Weekly August 2nd week, 2019 Vol.4
筆者が2019/8/4(日)~8/10(土)に気になったQiitaの記事のまとめのまとめを作成しました。日々のまとめ記事のWeekly版です。
皆様が興味のある言語や思いもよらぬハック方法をこの中から見つけられたら幸いです。
Java
- Tips
- Spring Framework
- Spring Boot
Python
- Django
- Tips
- PyAutoGUIを使ったPythonアプリをexe化するまで
- Kali LinuxにWingIDE(Python統合開発環境)インストール
- Watsonで数独を解く! Decision Optimizerを使ってみた
- pythonのパッケージ階層の話
- Gray-Scottモデルで模様のアニメーションを描く
- Expatを使ってPythonでXMLを解析する話
- PipenvでDjango開発環境をつくる
- 【Python】 Pythonで自己位置推定
- WGANgpで無限にラーメン画像生成する話
- 30行で顔認識(OpenCV)してファイル出力!
- PythonでJSONの入出力(エンコーディングもしっかりと)
- Python + Jinja2 でデータを HTML に埋め込んで JavaScript 側から利用する
- Python スタイルガイドとリンター
- Kaggle
- Tools
Ruby
Rails
- Beginner
- Tips
- RubyのRailsをMySQLでHerokuにあげてみた
- RubyOnRails/自動更新機能の実装
- RubyOnRails/インクリメンタルサーチの実装
- Rails6 のちょい足しな新機能を試す63(db:system:change 編)
- Rails 3-5 複数のデータベースに接続する方法
- Railsのerbをslimに変更する方法
- Rails6新機能 ActionText使用方法
- Rails6 のちょい足しな新機能を試す64(db:seed 編)
- RailsのStrong Parametersを一括で処理するメソッド
- Railsで開発されているOSS
- 本番環境でRailsを起動しようとした時のMysql5.7系に関するエラー
- Rails6 のちょい足しな新機能を試す65(timestamps precision編)
- Railsにdeviseを導入する方法
- deviseの日本語対応
- RailsでS3から画像やPDFファイルをダウンロードする方法
- Railsのサービスクラスを作る前に考えるべきこと
- rails font-awesome-sass導入方法
- AWS利用上のセキュリティを確保する方法
- railsでYoutube data APIを使ってみた
- gem'devise'に頼りすぎていて、sessionとcookieの概念が疎かだったので、まとめる
C#
Android
- Tips
- Androidでグラフを作ってみる
- FragmentでLiveData#observeを使うときの第一引数(Owner)には
viewLifecycleOwner
を使う- Cordova 9.0にバージョンアップする際にハマったこと
- Androidスマホの位置情報をKibanaで可視化してみる
- Android Jetpackのリリース前のバージョンを使う方法
- Android JetpackのApp Crawlerを動かしてみる
- Retrofitの返り値は
Observable
とSingle
などとはどっちがいいか- Activityを再起動させたいけど余分な画面を一枚挟まないといけない…という場合に有効なライブラリ
- AndroidX 導入手順
Swift
- Tips
- Apps
Kotlin
- Tips
Flutter
- Beginner
Fulx
JavaScript
- Beginner
- Tips
React
- Beginner
- Tips
- Apps
Node.js
- Tips
- Express
- Tools
- Apps
Vue.js
- Beginner
- Tips
Vuex
Nuxt.js
- Tips
Nest.js
Angular
jQuery
TypeScript
C#
PowerApps
ReactNative
Laravel
- Tips
C
PHP
CakePHP
Rust
Go言語
- Tips
R言語
- Tips
Scala
Haskell
Unity
Spark
Line
- Tips
- Apps
A-Frame
- Beginner
- Tips
PowerApp
Line
HTML
CSS
Sass
SQL
MySQL
- Tips
PostgreSQL
Oracle
MongoDB
SQL Server
Apache
ビッグデータ
Visual Studio Code
IntelliJ IDEA
AI
IoC
Git
- Beginner
- Tips
AWS
- Beginner
- Tips
- EC2
- AWS Lambda
- LambdaでのCloudWatch LogsからS3へのエクスポート
- Lambda×node.jsでGB単位の動画をStreamAPIでアップロードする方法
- AWS Lambdaでのbundle install
- AWS LambdaとPythonでスクレイピング処理をマイクロサービス化する
- API Gateway + LambdaでAPI Keyの値を取得する
- AWS Lambda:API GatewayとApplication Load Balancerの違い
- CloudWatchの空になったロググループ削除
- LambdaでIAMユーザーのアクセスキーを監視
- AWS Lambdaを使ってAndroid JetpackのリリースをRSSで受け取れるようにした
- Amazon S3
- Amazon MQ
- AWS CDK
- AWS CLI
- AWS SAM
- AWS Clinet VPN
- AWS CloudHSM
- AWS Certificate Manager
- AWS CloudFormation
- AWS CodeCommit
- AWS Textract
- AWS Fargate
- AWS WAF
- AWS Inspector
- DynamoDB
Azure
- Tips
Oracle Cloud
IBM Cloud
Active Directory
Cloud SQL
Cloud9
インフラ
ブロックチェーン
Ethereum
Hyperledger
セキュリティ
機械学習
自然言語処理
Network
RPA
CI
Docker
- Tips
- Dockerイメージをレジストリに登録する(ECR含む)
- Dockerを使ってKali Linuxの環境を構築した時のメモ
- Dockerでrails開発環境構築(リバースプロキシで複数アプリを構築)
- 簡単な静的ページと簡単なAPIサーバーをGoogle Cloud Runで動かす
- Docker上でLaravelを動かしてみよう(laradockなしで)
- AWS (ECS + RDS)+ CircleciによるCI/CDの理解(初学者がインプットすべき情報)~用語と概念理解編~
- Autoware(1.9.1) Docker セットアップ
- Docker for WindowsでDockerイメージを一括削除するワンライナースクリプト
- Laradockでローカルサーバを立ち上げてブラウザ表示する方法
- Docker上にDjangoとReactの環境を構築する
- macにdocker-composeでwordpressをたてる
Heroku
VirtualBox
kubernetes
OpenStack
Swagger
- Tips
AMP
OpenID
OAuth2.0
Elasticsearch
Linux
Cent OS
Windows
Mac
Redis
- Beginner
Google API
Google Apps Script
Google Cloud Platform
Google Colaboratory
Google Cloud Data Catalog
Google Drive
VB.Net
Firebase
Server Side
CSS
BootStrap
WordPress
Develop
- Beginner
- Tips
- Tools
Keras
PowerShell
Vim
Atom
awk
LaTex
Redmine
UML
- Beginner
Raspberry
- Tips
- Apps
RPA
IoT
Alexa
Line
SharePoint
VBA
ShellScript
PowerShell
Slack
Nim
Emacs
WPF
UI
Ansible
Arduino
Julia
Coral
ionic
- Tips
QRCode
OCR
EC-CUBE
- Beginner
資格
転職
更新情報
Kotlin
- Kotlin入門
Android
Java
IDE
- 投稿日:2019-08-11T12:26:44+09:00
盆休みだし改めてvar,let,constと思い出を振り返る
はじめに
せっかくのお盆休み(とは言っても3連休だけですが)なので、知識としては知っているもののあやふやな部分があるところに関して少し振り返ってみようかと思いました。
まずは変数宣言について振り返ります。var,let,constについて
現在JavaScriptにおいて変数、定数を宣言する場合はlet,constが使用されます。
let,constがでる前はvarを使用していましたが、これからJavaScriptを書く場合にあえてvarを選択する理由はありません。またletに関しても再宣言は不可能なものの再代入は可能であるため、一部の場面を除いてconstを利用する方が良いでしょう。
MDNで確認
まずはそれぞれの定義に関してMDNで確認してみます。
var 文は変数を宣言し、任意でそれをある値に初期化します。
let 文はブロックスコープの局所変数を宣言します。任意で値を代入して初期化できます。
定数 (const) は、 let 文を使って定義する変数と同じ、ブロックスコープです。定数の値は、再代入による変更はできず、再宣言もできません。
各宣言方法について
var > let > constの順でより制限が強くなっていきます。
for文などイテレータが必要な場合はletを使用することもありますが、書き方次第ではそれも回避できるので使用頻度は低めです。
名称 再宣言 再代入 関数スコープ ブロックスコープ var ◯ ◯ ◯ × let × ◯ × ◯ const × × × ◯ 再宣言、再代入
再宣言
一度宣言した変数を再度同じ変数名で宣言することです。
それぞれ実際のコード例を元にして説明していきます。var
varの場合は、再宣言も再代入も可能です。同一名の変数に関して何度でも上書きが可能です。
// 再宣言 try { var cat = 'tama'; var cat = 'saba'; console.log(cat) // saba } catch(err) { console.log(err); } // 再代入 try { let cat = 'tama'; cat = 'mike'; console.log(cat) // mike } catch(err) { console.log(err); }let
letの場合は、再宣言は不可ですが、再代入は可能です。上書き自体はできてしまうので、宣言した後に書き換えられてしまう危険性は残ります。
// 再宣言 try { let shiba = 'maru'; let shiba = 'jiro'; console.log(shiba) } catch(err) { console.log(err); // Uncaught SyntaxError: Identifier 'shiba' has already been declared } // 再代入 try { let shiba = 'maru'; shiba = 'taro'; console.log(shiba) // taro } catch(err) { console.log(err); }const
再宣言も再代入も不可能です。一度宣言したら値への参照自体は変更することができません。
しかしobjectや配列を宣言した場合、その中身自体の変更可能です。(後述します)// 再宣言 try { const Hamu = 'kuru' const Hamu = 'momo' console.log(Hamu) // Uncaught SyntaxError: Identifier 'Hamu' has already been declared } catch(err) { console.log(err) } // 再代入 try { const Hamu = 'kuru'; Hamu = 'tete'; console.log(Hamu) } catch(err) { console.log(err) // TypeError: Assignment to constant variable. }スコープ
グローバルスコープ
- プログラム全体で参照可能
- どのような場合にグローバルスコープになるか
- 何もつけずに変数を宣言
- 関数のトップレベルで変数を宣言
scope = 'global' // グローバルスコープの変数scope function checkScope() { scope = 'local1' // 関数スコープにならずにグローバススコープ変数を上書き localScope = 'local2' // 関数内ではあるが、グローバルスコープとして宣言された変数localScope console.log('functionLocal', scope, localScope) } checkScope() // functionLocal, local1, local2 console.log('globalScoep', scope, localScope, window.scope, window.localScope) // globalScoep, local1, local2, local1, local2関数スコープ
- 変数が宣言された関数の中だけで参照可能。
- どのような場合に関数スコープになるか
- 関数内でvarで変数を宣言
var scope = 'global' function checkScope() { var scope = 'local1' // checkScope関数の内部だけで有効な変数scope if (true) { var localScope = 'local2' // checkScope関数の内部からアクセス可能な変数localScope } console.log('functionLocal', scope, localScope) // functionLocal, local1, local2 } checkScope() // functionLocal, local1, local2 console.log('globalScoep', scope, window.scope, window.localScope) // globalScoep, global, global, undefinedブロックスコープ
- ブロック({}に囲まれた領域)内でのみ参照可能
- 同じ関数内であっても異なるブロックであった場合アクセス不可
- どのような場合にブロックスコープになるか
- ブロック内でlet, constで変数,定数を宣言
function checkScope() { var scope = 'local1' // checkScope関数の内部だけで有効な変数scope if (true) { let localScope = 'local2' // checkScope関数の内部からアクセス可能な変数localScope } console.log('functionLocal', scope, localScope) // localScopeはスコープが異なるため参照不可 } checkScope() // Uncaught ReferenceError: localScope is not definedconstにおけるobject,arrayの扱い
object,arrayについては変数の参照自体の再宣言、再代入はできません。
ですがそれらの中身を変えることは可能です。自分ははじめここでかなりconstに関しては混乱をしました。
まずは一番わかりやすいただのstringとしての定数の宣言をしたいと思います、// 再宣言 const Idol = 'shimamu-' const Idol = 'mio' // Uncaught SyntaxError: Identifier 'Idol' has already been declared // 再代入 const Idol = 'shimamu-' Idol = 'mio' // Uncaught TypeError: Assignment to constant variable.constの場合は定数(constant variable)として宣言されるので、再宣言も再代入もできません。
次にオブジェクトを宣言してみます。// 再宣言 const Idol = {nickname: 'shimamu-'} const Idol = {nickname: 'mio'} // Uncaught SyntaxError: Identifier 'Idol' has already been declared // 再代入 const Idol = {nickname: 'shimamu-'} Idol = {nickname: 'mio'} // Uncaught TypeError: Assignment to constant variable.当然同じように再代入も再宣言もできません。
ではオブジェクトそのものではなく、オブジェクトの中身を変更した場合はどうでしょうか// オブジェクトの要素を変更 const Idol = {nickname: 'shimamu-'} Idol.nickname = 'mio' console.log(Idol) // {nickname: 'mio'}オブジェクトはそのものの再宣言、再代入は許容されませんがオブジェクトの要素に関しては変更、追加、削除が可能です。
// オブジェクトの要素の追加 const Idol = {nickname: 'shimamu-'} Idol.type = 'cute' Idol.talent = 'smile' console.log(Idol) // {nickname: "shimamu-", type: "cute", }// オブジェクトの要素の削除 const Idol = { nickname: 'shimamu-', type: 'cute', talent:'smile' } delete Idol.talent console.log(Idol) //{nickname: "shimamu-", type: "cute"}また配列においても同様に変更、削除、追加ができます。
// 変更 const Newgene = ['shimamu-'] const idolAdd = ['mio', 'shiburin'] Newgene.push(...idolAdd) console.log(Newgene) // ["shimamu-", "mio", "shiburin"] // 削除 const Newgene = ['shimamu-', 'mio', 'shiburin'] Newgene.shift() console.log(Newgene) // ["mio", "shiburin"] // 追加 const Newgene = ['mio', 'shiburin'] Newgene.unshift('shimamu-') console.log(Newgene) // ["shimamu-", "mio", "shiburin"]ここでこれまで説明してきた内容を振り返りつつ、とあるアニメのとあるグループの状態遷移を再現してみたいと思います。
// objectの宣言 const Uduki = { nickname: 'shimamu-', type: 'cute', status:'doMyBest'}, Mio = { nickname: 'chanmio', type: 'passion', status: 'fun'}, Rin = { nickname: 'shiburin', type: 'cool', status: 'seek'} // 配列の宣言 const UdukiMioRin = [Uduki, Mio, Rin] //初期値の宣言 const Newgene = [] // 配列への追加、削除 const changeGroup = (groupName, method, member) => { if (method === 'add') { Array.isArray(member) ? groupName.push(...member) : groupName.push(member) // 配列への追加 } else if (method === 'left') { const NickName = member.nickname const Order = groupName.findIndex(n => n.nickname === NickName) groupName.splice(Order, 1) // 配列からの削除 } } // objectの変更 const changeGroupValue = (groupName, type, nickname, key, value) => { if (type === 'change') { groupName.filter(n => n.nickname === nickname)[0][key] = value } else if (type === 'delete') { delete groupName.filter(n => n.nickname === nickname)[0][key] } } // 1話 changeGroup(Newgene, 'add', UdukiMioRin) console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "doMyBest"} 1: {nickname: "chanmio", type: "passion", status: "fun"} 2: {nickname: "shiburin", type: "cool", status: "seek"} */ // 6話 changeGroup(Newgene, 'left', Mio) console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "doMyBest"} 1: {nickname: "shiburin", type: "cool", status: "seek"} */ // 7話 changeGroup(Newgene, 'add', Mio) changeGroupValue(Newgene, 'change', 'chanmio', 'status', 'motivated') console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "doMyBest"} 1: {nickname: "shiburin", type: "cool", status: "seek"} 2: {nickname: "chanmio", type: "passion", status: "motivated"} */ // 21話 changeGroupValue(Newgene, 'change', 'chanmio', 'talent', 'acting') console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "doMyBest"} 1: {nickname: "shiburin", type: "cool", status: "seek"} 2: {nickname: "chanmio", type: "passion", status: "motivated", talent: "acting"} */ // 22話 changeGroupValue(Newgene, 'change', 'shiburin', 'talent', 'song') changeGroupValue(Newgene, 'change', 'shiburin', 'status', 'fun') changeGroupValue(Newgene, 'delete', 'shimamu-', 'talent') changeGroupValue(Newgene, 'change', 'shimamu-', 'status', 'dark') console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "dark"} 1: {nickname: "shiburin", type: "cool", status: "fun", talent: "song"} 2: {nickname: "chanmio", type: "passion", status: "motivated", talent: "acting"} */ // 25話 changeGroupValue(Newgene, 'change', 'shimamu-', 'talent', 'smile') changeGroupValue(Newgene, 'change', 'shimamu-', 'status', 'joy') console.log(Newgene) /** 0: {nickname: "shimamu-", type: "cute", status: "joy", talent: "smile"} 1: {nickname: "shiburin", type: "cool", status: "fun", talent: "song"} 2: {nickname: "chanmio", type: "passion", status: "motivated", talent: "acting"} */なぜconstを使うのか
スコープを狭める
let
も同じですが、スコープを狭くしておくことで変数の影響範囲がわかりやすくなり、開発がしやすくなります。状態の把握が容易
let
の場合は一度宣言しても値の再代入は可能です。そのため宣言後にも状態が変わります。
const
の場合は上書きされることがないことが保証されているので現在の状態の推測が容易です。まとめ
最後まで読んでくださってありがとうございます。
- これからJavaScriptを書く場合varは使用しない
- 再代入が必要な場面以外はconstを使用する
ちなみに自分の好きなものを変数にすると記事を書くのが捗りますね。皆さんも良いお盆休みを
- 投稿日:2019-08-11T12:18:40+09:00
JavaScript 正規表現(RegExp:Regular Expression)メモ
JavaScriptで利用できる主な正規表現パターン
パターン マッチする文字列 ABC 「ABC」という文字列 [ABC] A,B,Cのいずれか1文字 [^ABC] A,B,C以外のいずれか1文字 [A-Z] A〜Zの間の1文字 A|B|C A,B,Cのいずれか X* 0文字以上のX X? 0、または1文字のX X+ 1文字以上のX X{n} Xとn回一致 X{n,} Xとn回以上一致 X{m,n} Xとm〜n回一致 ^ 行の先頭に一致 $ 行の末尾に一致 . 任意の1文字に一致 \w 大文字/小文字の英字、数字、アンダースコアに一致(=[A-Za-z0-9_]) \W 文字以外に一致(=[^\w]) \d 数字に一致(=[0-9]) \D 数字以外に一致(=[^0-9]) \n 改行(ラインフィード)に一致 \r 復帰(キャリッジリターン)に一致 \t タブ文字に一致 \s 空白文字に一致(=[\n\r\t\v\f]) \S 空白以外に一致(=[^\s]) \〜 「〜」で表される文字 オブジェクトの生成
var 変数名 = new RegExp('正規表現', 'オプション'); //コンストラクター var 変数名 = /正規表現/オプション; // リテラル正規表現のオプション
オプション 概要 g 文字列全体にマッチするか(無指定の場合、1度マッチした時点で処理を終了) i 大文字/小文字を区別するか m 複数行に対応するか(改行コードを行頭/行末と認識) u Unicode対応(ES2015) エスケープ
コンストラクター構文では「 \ 」、リテラル構文では「 / 」をエスケープする必要がある。
正規表現による検索・検証・置換・分割
String.match
String.matchメソッドはマッチした文字列を配列として取得する。
gオプションを無効にした場合、「最初にマッチした文字列全体」と「サブマッチ文字列」を取得する。
(サブマッチ文字列:正規表現パターンの中で丸カッコで示された部分(サブマッチパターン)に合致した部分文字列。マッチしている部分がない場合はundefined、複数マッチしている場合には最後にマッチしたものを保持する。)str.match(pattern) // str:検索対象の文字列 // pattern:正規表現RegExp.exec
RegExp.execメソッドはgオプションの有無にかかわらず、1度の実行で1つの実行結果しか返さず、マッチした文字列全体とサブマッチ文字列を配列として返す。
execメソッド(RegExpオブジェクト)は「最後にマッチした文字位置を記憶する」ため、次にexecメソッドを実行する場合、RegExpオブジェクトは「前回のマッチ位置から検索を再開」する。検索結果がない場合はnullを返す。regexp.exec(str) // regexp:正規表現 // str:検索対象の文字列 // 全てのマッチング結果を得る場合 while ((result = regexp.exec(str)) !== null){ // 繰り返し処理 }RegExp.test
RegExp.testメソッドを使うことで、単に正規表現にマッチしているかどうかを真偽値で返すことができる。
regexp.test(str) // regexp:正規表現 // str:検索対象の文字列String.search
String.searchメソッドは、指定した正規表現で最初にマッチした文字位置を返す。マッチした文字列が存在しない場合は戻り値として-1を返す。
str.search(pattern) // str:検索対象の文字列 // pattern:正規表現String.replace
String.replaceメソッドは、正規表現でマッチした文字列を置換することができる。
引数のrepには「\$1...$9」の特殊変数を埋め込むことができる。
特殊変数はサブマッチ文字列を保存するための変数。str.replace(pattern, rep) // str:置き換え対象の文字列 // pattern:正規表現 // rep:置き換え後の文字列String.split
String.splitメソッドは、正規表現で文字列を分割することができる。
str.split(sep[, limit]) // str:分割対象の文字列 // sep:区切り文字(正規表現) // limit:分割回数の上限出典
[改訂新版]JavaScript本格入門 山田祥寛著
- 投稿日:2019-08-11T10:37:18+09:00
【5分でデプロイまで】Gatsbyで爆速ブログを作成
概要
Gatsbyのスターターと、Netlifyを使用して誰でも5分で爆速ブログを作成する方法を紹介します。
記事内ではこちらのブログをデプロイまで行います。
Gatsbyとは?
Gatsbyに関してはこの方の記事でとても良く説明されています。
Gatsbyのインストール
Gatsbyがインストールされていない場合は以下のコマンドでインストールしましょう。
$ npm install -g gatsby-cliブログの作成
以下のコマンドでブログを作成します。
my-blog
の部分でブログの名前を指定します。https://github.com/gatsbyjs/gatsby-starter-blog
の箇所は使用するスターターを指定します。こちらから好きなデザインを選ぶ事が可能です。
$ gatsby new my-blog https://github.com/gatsbyjs/gatsby-starter-blogローカルで確認
作成したブログのディレクトリに移動し、ローカルでブログを確認しましょう。
以下のコマンドを実行すると、
http://localhost:8000/
でブログが起動している事が確認できます。$ cd my-new-blog $ gatsby develop新しい記事の追加
content/blog
に新しいマークダウンファイル(.md
)を追加してみましょう。
すると、自動でブログに記事が更新されている事が確認できます。--- title: テスト記事 --- 今日はいい天気だな。ブログをGitHubにプッシュする。
Netlifyを使用したデプロイ方法ではデプロイしたいサイトがGitHub/GitLab/Bitbucketで管理されている必要があります。
本記事ではGitHubを使用します。git init git add . git commit -m "Initial commit"git remote add origin https://github.com/yourname/your-blog-name.git git push -u origin masterブログをデプロイする
Netlifyのアカウントがない場合は作成しましょう。
アカウント作成後、以下のNew site from Git
をクリックします。今回はGitHubからのデプロイなので、GitHubを選択します。
デプロイするレポジトリで作成したブログのレポジトリを選択します。
次の画面で
Deploy site
をクリックする事でデプロイが始まります。デプロイができたら、サイトを確認してみましょう。
ここではhttps://elastic-montalcini-5361d7.netlify.com
がサイトのURLになります。URLの変更
URLが気にくわない場合は
Site settings
をクリックし、Change Site name
でnetlify.com
以前のURLを変更する事が出来ます。
また、カスタムドメインを使用することも出来ます。デプロイ後のブログの更新
Neflifyではmasterブランンチにコミットやマージがあると自動的にデプロイが走り、ブログが更新されます。
ですので、記事を追加したり、デザインを変更した後に、GitHubでmasterブランチにその変更をプッシュするだけでサイトの更新が可能です。参考
- 投稿日:2019-08-11T10:28:34+09:00
javascriptにおける関数
関数とは一連の処理をまとめたもの。
引数は関数に対して渡すパラメーターでそのパラメーターを元に関数内で処理を行い、戻り値を返す。この戻り値はreturnを使って戻す。returnのあとは処理が行われず、returnがないとundefined(未定義値)を返す。
- 投稿日:2019-08-11T10:18:57+09:00
【JavaScript】ビルドとは何か〜webpackを使ってビルドする
この辺が曖昧だったので、書き残しておきます。
- ビルドとは何か
- なぜビルドが必要か
- ビルドでは何を行なっているのか
- ビルドしたらどうなるのか
Node.jsの特徴
- JavaScriptをサーバーサイドで動かすための実行環境。
- モジュール機能(他ファイルの読み込み)
- ノンブロッキングI/Oで処理を効率化
参考記事
ノンブロッキングI/O (non-blocking I/O)
ノンブロッキングI/Oと非同期I/Oの違いを理解する
Node.js を5分で大雑把に理解する
Node.jsについて調べてみた
JavaScriptが辿った変遷
npm とか bower とか一体何なんだよ!Javascript 界隈の文脈を理解しようビルドツール
Node.jsスタイルのコードは、そのままの状態ではブラウザで動きません。ソースコードを元にして、実際に動くプログラム(実行可能ファイル)を作ってあげる必要があります。
その作成処理のことをビルド
と言います。ビルドを実行するためには、
ビルドツール
を使用します。概要
以下、10年遅れたJavaScriptの知識をざっくり10分でアップデートしようより引用。
最近のWebアプリケーションは、開発時のコードと実際の製品に使われるコードが異なります。開発者はブラウザーにサポートされていない可能性がある最新バージョンのJavaScriptでコードを書いたり、node_modulesフォルダー内のサードパーティ製パッケージとその依存オブジェクトを多用したり、静的解析ツールや圧縮ツール(Minifier)などを使ったりして開発します。これらをすべて変換、効果的にデプロイして、大半のWebブラウザーが理解できる形にするのがビルドツールです。
何を行うのか
以下、キャッチアップJavaScriptビルドより引用。
- モジュール管理(依存性解決)
- テンプレート管理
- UT/操作テスト
- 構文チェック
- 圧縮/難読化
- CSSプリプロセッサー
- ES2015コンパイル
- altJSコンパイル
簡単に補足します。
- CSSプリプロセッサーとは、独自の構文でCSSを生成するプログラムのこと。コンパイルにより、純粋なCSSより可読性や保守性が高くなります。
- ES2015(ES6)はJavaScriptの新基準ですが、古いブラウザでは対応していない場合があります。そのため、コンパイルで互換性のあるES5に変換します。(トランスパイル)
- altJSとは、コンパイルによりJavaScriptに変換されるプログラミング言語のこと(代替JavaScript)。従来のJavaScriptより可読性と保守性が高く、コーディングの負担を減らします。
テンプレート管理が何を意味しているのか、よくわからなかったです。
文字列の組み込みのことでしょうか・・・?タスクランナーとは
ビルドツールで行われている処理(タスク)を自動化してくれるツールのこと。
grupやGruntが挙げられます。ネットで調べた限り、ビルドツールとタスクランナーの区分は曖昧でした。
webpackの立ち位置が微妙なところです。npmとは
Node.jsのパッケージ管理ツール。
Node.jsと同時にインストールされます。各種ビルドツールは、npmを使ってインストールすることができます。
今回は、webpack
を使ってみます。参考記事
CSS プリプロセッサー
CSSプリプロセッサ(CSSメタ言語)のお勉強
最近のビルドツールって何なの?
タスクランナー(Gulp)の入門の入門
【保存版】gulp, webpack, parcel...増え続けるタスクランナーの特徴を理解して最適なものを選択しよう
JavaScriptをやっているとnpm/yarn/gulp/grunt/webpackなど、たくさんのツールがあって混乱したので、それぞれの役割と違いをざっくりとまとめた
プログラミングが便利に!2018年に取り入れたいオススメのタスクランナー5選
Webpackってどんなもの?実演
初期化
テスト用ディレクトリを作成し、移動します。
$ mkdir js_test $ cd js_test初期化します。
いくつか質問がありますが、全てEnterで大丈夫です。$ npm init初期化後、ディレクトリ内に
package.json
が生成されます。webpackのインストール
webpackをローカルインストールします。
$ npm install webpack --save-devディレクトリ直下に
package-lock.json
とnode_modulesディレクトリ
が生成されます。webpackのバージョンを確認します。
$ npx webpack -v 4.39.1このバージョンでは、
webpack-cli
というライブラリも必要になります。
webpackと同じく、ローカルインストールします。$ npm install --save-dev webpack-cliインストール後、package.jsonの
devDependencies
に追記されました。package.json{ "name": "js_test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^4.39.1", "webpack-cli": "^3.3.6" } }webpackの設定
ビルドするためには、元のソースコードの場所とビルド後に生成される実行ファイル(bundleファイル)についての情報が必要です。
元のソースコードで一番最初に読み込まれるファイルは、エントリ-ポイント
と呼ばれます。ディレクトリ直下に設定ファイル
webpack.config.js
を作成します。$ touch webpack.config.js次のように設定します。
webpack.config.jsconst path = require('path'); module.exports = { // エントリーポイントの設定 entry: './src/my-main.js', // ビルド後、'./dist/my-bundle.js'というbundleファイルを生成する output: { path: path.resolve(__dirname, 'dist'), filename: 'my-bundle.js' } };テストファイルの作成
テストファイルを作成します。
文字列を返すメソッドを記述し、それをモジュールとして別ファイルで読み込んで出力します。$ touch my-index.html $ mkdir src $ mkdir distmy-index.html<html> <head> <meta charset="utf-8"> </head> <body> <script type="text/javascript" src="dist/my-bundle.js" charset="utf-8"></script> </body> </html>$ cd src $ touch my-main.js $ touch my-func.jsmy-main.jsvar func = require('./my-func'); var res = document.createTextNode(func()); document.body.appendChild(res);my-func.jsmodule.exports = function () { return 'my-test'; };エイリアスコマンドの設定
webpackコマンドを簡単に実行するために、エイリアスコマンドを設定します。
package.jsonのscriptに以下を記述します。package.json"build": "webpack --mode development"もしくは
package.json"build": "webpack --mode production"modeにより、生成されるbundleファイルの内容が異なります。(動作は同じです。)
modeを指定しない場合は、ビルド実行時に警告が発生します。この設定により
npm run build
でビルドを実行する事が可能になりました。ビルド実行
エイリアスコマンドを設定したので、早速実行してみます。
$ npm run build実行後、bundleファイルである
dist/my-bundle.js
が生成されます。
ブラウザで出力した文字列を確認できれば、ビルド成功です。ビルドせずに動作を確認したい
ビルドせずに動作を確認したい場合は、
watchオプション
を利用します。
package.jsonのscriptに以下を記述します。package.json"watch": "webpack --watch"
npm run watch
を実行すると、コードの変更が検知されるようになります。そのため、(画面をリロードすれば)ビルドせずに動作を確認する事ができます。デベロッパーツール上で元のファイルを確認したい
デベロッパーツールの
Sourcesタブ
では、ビルド元のファイルを確認する事ができません。それらを表示させたい場合は、webpack.config.jsに以下を記述します。
webpack.config.jsdevtool: 'inline-source-map'これで、デベロッパーツール上で
my-main.js
とmy-main.js
の内容を確認する事ができます。参考記事
package.jsonの中身を理解する
package-lock.jsonについて知りたくても聞けなかったこと
勉強メモ/npmの使い方(node.js=v0.11.16, npm=2.3.0, 2015-03時点)
npm 5.2.0の新機能! 「npx」でローカルパッケージを手軽に実行しよう
webpack.config.jsがわからない
npmとwebpack4でビルド - jQueryからの次のステップ
step by stepで始めるwebpack
新人にドヤ顔で説明できるか、今風フロントエンド開発ハンズオン(Git/Node.js/ES6/webpack4/Babel7)
React.jsでビルドされる前のコードをブラウザで確認する
- 投稿日:2019-08-11T09:30:11+09:00
"Writing An Interpreter In Go " を JavaScriptで実装してみる その1 Lexer
なんとなく 個人的お盆休みの課題として
Writing An Interpreter In Goという本を読み始めた。内容としてはMonkeyという言語のインタプリタをGo言語で実装するという内容である
ページ数としては200ページほどで、軽すぎず、重すぎず丁度いい内容。Lexer・Parser・Evaluaterの流れですすんでいく。
ただ写経するだけではつまらないのでJavaScriptで実装し直してみている。
npmモジュールにMonkeyの日本語訳である"saru"というレポジトリで公開し始めた[npm]
https://www.npmjs.com/package/saru[code]
https://github.com/freddiefujiwara/saruインストール方法はこちら
npm i -g saru
使い方はこちら
$ saru Hello fumikazu! This is the Monkey programming language! Feel free to type in commands * Please type 'bye' if you want to exit >>>サンプル
>>> let me know Token { type: 'LET', literal: 'let' } Token { type: 'IDENT', literal: 'me' } Token { type: 'IDENT', literal: 'know' } >>> let str = "string"; Token { type: 'LET', literal: 'let' } Token { type: 'IDENT', literal: 'str' } Token { type: '=', literal: '=' } Token { type: 'STRING', literal: 'string' } Token { type: ';', literal: ';' } >>> let i = 10; Token { type: 'LET', literal: 'let' } Token { type: 'IDENT', literal: 'i' } Token { type: '=', literal: '=' } Token { type: 'INT', literal: '10' } Token { type: ';', literal: ';' } >>> !@ Token { type: '!', literal: '!' } Token { type: 'ILLEGAL', literal: '@' }また、始めて jestを使ってみた。
まだまだLexer部分しか作ってないが今後Parser部分やEvaluator部分も作ったら
Qiitaに投稿しようと思う
- 投稿日:2019-08-11T06:53:21+09:00
JavaScript の 変数 まとめ ~console.log(変数名);~
目的
- JavaScriptの変数のふるまいを知る。
押さえるポイント
- 変数を定義してから値や文字列を格納する。
- 同一ステップで変数定義と値や文字列の格納を行う。
- 変数名は英数字のみ使用すること。
- 2語以上の英単語から成る変数名のとき、2語目の頭文字を大文字にする。(user nameと変数名を命名したいときの変数名は
userName
となる)- 数字で始まるものは変数名にできない
書き方の例
- 下記に汎用的な変数の定義と格納方法を記したJavaScriptファイルの内容を記載する。
let 変数名 = 数値; let 変数名 = "変数に格納する文字列";
- 下記に変数に格納された文字列をコンソールに出力する処理を記したJavaScriptファイルの内容を記載する。
console.log(変数名); # ※console.log("変数名");としないこと。変数に格納された値や文字列ではなく、変数名がコンソールに出力されてしまう。より具体的な例
- 「name」という名前の変数を定義して、文字列「miriwo」を格納する。
- 下記に変数定義と変数への格納方法を記したJavaScriptファイルの内容を記載する。
let name = "miriwo";
- 定義された「name」変数に格納された文字列をコンソールに出力する。
- 下記に変数に格納された文字列をコンソールに出力する処理を記したJavaScriptファイルの内容を記載する。
console.loc(name);
- 投稿日:2019-08-11T05:07:07+09:00
Wasm By ExampleをAssemblyScriptで試してみた
こちらのWebAssemblyのサンプルがとてもわかりやすかったので、実際に自分で動かしてみました。
ただ、参考サイトに書いてあることをやっていくだけなので、参考サイトの方をいていただければ私の投稿は見なくても大丈夫です。サンプルはRustとAssemblyScriptのものがあったのですが、私はAssemblyScriptでやってみました。
私が実際にやってみたコードはこちらです
https://github.com/okumurakengo/wasm-sample
AssemblyScriptをインストール
yarn add -D AssemblyScript/assemblyscript
Hello World!
https://wasmbyexample.dev/examples/hello-world/hello-world.assemblyscript.en-us.html
hello-world.ts
を作成する。※TypeScriptではなく、AssemblyScriptで書きます。hello-world.tsexport function add(a: i32, b: i32): i32 { return a + b; }AssemblyScriptのコンパイラを使って、wasmのモジュールを作成します。
yarn asc hello-world.ts -b hello-world.wasm
これで
hello-world.wasm
が作成されます。
wasmモジュールをロードするための関数を追加します。参考サイトにもあるようにこちらのをそのまま使用させていただきました。
https://github.com/torch2424/wasm-by-example/blob/master/demo-util/instantiateWasm.js
instantiateWasm.jsexport const wasmBrowserInstantiate = async (wasmModuleUrl, importObject) => { let response = undefined; if (!importObject) { importObject = { env: { abort: () => console.log("Abort!") } }; } if (WebAssembly.instantiateStreaming) { response = await WebAssembly.instantiateStreaming( fetch(wasmModuleUrl), importObject ); } else { const fetchAndInstantiateTask = async () => { const wasmArrayBuffer = await fetch(wasmModuleUrl).then(response => response.arrayBuffer() ); return WebAssembly.instantiate(wasmArrayBuffer, importObject); }; response = await fetchAndInstantiateTask(); } return response; };
hello-world.js
を作成して、hello-world.wasm
を読み込み、add
関数を実行するようにします。hello-world.jsimport { wasmBrowserInstantiate } from "./instantiateWasm.js"; (async () => { const wasmModule = await wasmBrowserInstantiate("./hello-world.wasm"); const addResult = wasmModule.instance.exports.add(24, 24); document.body.textContent = `Hello World! addResult: ${addResult}`; })();
hello-world.html
を作成hello-world.html<!DOCTYPE html> <meta charset=utf-8> <title>sample</title> <script type="module" src=hello-world.js></script>なんでもいいのでサーバーを起動し、動きを確認してみます。今回はnode-staticでサーバーを起動しました。
#Example:nodeで簡易サーバ npx node-static -p 8000AssemblyScriptのadd関数を実行できたのを確認できました。
Exports
https://wasmbyexample.dev/examples/exports/exports.assemblyscript.en-us.html
exportした関数と定数で使い方が若干違ったので確認してみます。
exports.tsexport function callMeFromJavascript(a: i32, b: i32): i32 { return addIntegerWithConstant(a, b); } export const GET_THIS_CONSTANT_FROM_JAVASCRIPT: i32 = 2424; function addIntegerWithConstant(a: i32, b: i32): i32 { return a + b + ADD_CONSTANT; } const ADD_CONSTANT: i32 = 1;見ての通り、
callMeFromJavascript
とGET_THIS_CONSTANT_FROM_JAVASCRIPT
をexportしています。arn asc exports.ts -b exports.wasm
exports.jsimport { wasmBrowserInstantiate } from "./instantiateWasm.js"; (async () => { const wasmModule = await wasmBrowserInstantiate("./exports.wasm"); // instance.exports にエクスポートされた関数などが設定されるsetteisareru // 参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming#Return_value const exports = wasmModule.instance.exports; // エクスポートしたcallMeFromJavascriptを実行 console.log(exports.callMeFromJavascript(24, 24)); // 49 // 定数の場合は `.valueOf()` を使う // ※場合によっては単純に exports.GET_THIS_CONSTANT_FROM_JAVASCRIPT になると注意書きあり console.log(exports.GET_THIS_CONSTANT_FROM_JAVASCRIPT.valueOf()); // 2424 // exportしていない関数にはアクセスできない console.log(exports.addIntegerWithConstant); // undefined })();exports.html<!DOCTYPE html> <meta charset=utf-8> <title>sample</title> <script type="module" src=exports.js></script>WebAssembly Linear Memory
リニアメモリを使うと、wasmとjsの両方から読み書きができるバッファとして扱えるようです。
あまり詳しくないので、参考サイトのままですが動かすことはできました。memory.ts// Set up our memory // By growing our Wasm Memory by 1 page (64KB) // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory#Examples memory.grow(1); // インデックスの0番目に24を設定 const index = 0; const value = 24; store<u8>(index, value); // インデックスの1番目を取得する関数 // ※今回、jsファイルの方からインデックスの1番目を設定するコードを書きます export function readWasmMemoryAndReturnIndexOne(): i32 { let valueAtIndexOne = load<u8>(1); return valueAtIndexOne; }yarn asc memory.ts -b memory.wasm
memory.jsimport { wasmBrowserInstantiate } from "./instantiateWasm.js"; (async () => { const wasmModule = await wasmBrowserInstantiate("memory.wasm"); const exports = wasmModule.instance.exports; // memoryオブジェクトを取得 const memory = exports.memory; const wasmByteMemoryArray = new Uint8Array(memory.buffer); // wasmで設定したインデックスの0番目の値を取得できる console.log(wasmByteMemoryArray[0]); // 24 // インデックスの1番目に25と設定 wasmByteMemoryArray[1] = 25; // インデックスの1番目を取得する関数を実行 console.log(exports.readWasmMemoryAndReturnIndexOne()); // 25 })();memory.html<!DOCTYPE html> <meta charset=utf-8> <title>sample</title> <script type="module" src=memory.js></script>Importing Javascript Functions Into WebAssembly
jsで設定した関数をwasmで実行することができます。
index.ts// 関数がないとコンパイルエラーなので declare で設定 declare function consoleLog(arg0: i32): void; // .jsファイルで設定した consoleLog を実行 consoleLog(24);yarn asc index.ts -b index.wasm
index.jsimport { wasmBrowserInstantiate } from "./instantiateWasm.js"; (async () => { const wasmModule = await wasmBrowserInstantiate("index.wasm", { index: { consoleLog: value => console.log(value) }, env: { abort: () => console.log("Abort!") }, }); })();index.html<!DOCTYPE html> <meta charset=utf-8> <title>sample</title> <script type="module" src=index.js></script>jsで設定した関数をwasmで実行することができました。
最後まで読んでいただいてありがとうございました。m(_ _)m
- 投稿日:2019-08-11T05:01:28+09:00
【Electron】40万件のレコードを鬼のような速度で描画するクライアントアプリケーションの開発
背景
先日、【WPF】仮想化キャンバスを使用したDataGridの描画高速化という記事を投稿しました。
そこで仮想キャンバスによるデータ描画の高速化を行っていたのですが・・。
WPFの描画(主にスクロールパフォーマンス)めっちゃ遅い。そもそもWPFは柔軟性が高い分、大量データの描画は不得手だったのですが、
許容できないレベルで遅延が発生していたので困り果てていました・・。
そんな時に何気なく「Clusterize.js」という大量データ描画JSを発見このClusterize.js、描画が鬼のように速い。50万件のレコードもサクサク描画できる。
「あれ?このJSをASP.Net Codeで作成したページに埋め込んでElectronで動かしたら超高速じゃない?」
というわけでやってみました。まずElectronの導入部分を、Electronの環境構築(for Windows) - Qiita を参考に実施、
その後Clusterize.jsの動作確認まで行った自分用忘備録です。NodeJS - インストール
Nodejs.org - Downloads にアクセスして下記リンクからインストーラをダウンロード。
ダブルクリックで実行したら以下の画面が表示される。
しばらくボタンがDisabledだけど、少し待つと次のステップに進みます。
特に設定を変更する必要はないので、インストーラの誘導通り進めていってください。
上記画面が表示されたらNodeJSのインストール完了。
念のために完了後は以下のコマンドでバージョン確認を行いましょう。C:\>node -v v10.16.0プロジェクトの作成
てきとうなフォルダを作成します。
そのフォルダ内でGit Bashを開きます。ないならインストールしてね。
開いたら
npm init
でプロジェクトを初期化。
基本的にエンター連打で問題なし。
Is this OK? (yes)
の確認が出たら yes を打ち込んで完了。user_name@machine_name MINGW64 ~/source/repos/magicbullet $ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (magicbullet) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC) About to write to C:\Users\user_name\source\repos\magicbullet\package.json: { "name": "magicbullet", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this OK? (yes) yes最終イメージ。
ll
コマンドでpackage.json ができていることを確認してね。user_name@machine_name MINGW64 ~/source/repos/magicbullet $ ll total 1 -rw-r--r-- 1 owner 1049089 207 8月 9 15:01 package.jsonpackage.json{ "name": "magicbullet", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }Electron - インストール
続いてElectronをインストールします。以下のコマンドを投入。
npm install electron -g npm install --save-dev electronバージョン確認を行ってインストールできていることを確認。
user_name@machine_name MINGW64 ~/source/repos/magicbullet $ electron -v v6.0.1エントリーポイントの作成
package.jsonと同じパスに以下の二つのファイルを配置する。
magicbullet/index.js"use strct"; // Electronのモジュール const electron = require("electron"); // アプリケーションをコントロールするモジュール const app = electron.app; // ウィンドウを作成するモジュール const BrowserWindow = electron.BrowserWindow; // メインウィンドウはGCされないようにグローバル宣言 let mainWindow = null; // 全てのウィンドウが閉じたら終了 app.on("window-all-closed", () => { if (process.platform != "darwin") { app.quit(); } }); // Electronの初期化完了後に実行 app.on("ready", () => { //ウィンドウサイズを1280*720(フレームサイズを含まない)に設定する mainWindow = new BrowserWindow({width: 1280, height: 720, useContentSize: true}); //使用するhtmlファイルを指定する mainWindow.loadURL(`file://${__dirname}/index.html`); // ウィンドウが閉じられたらアプリも終了 mainWindow.on("closed", () => { mainWindow = null; }); });index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sample</title> </head> <body> <p>Hello World</p> </body> </html>Electron - コンパイル
以下のコマンドを投入します。
user_name@machine_name MINGW64 ~/source/repos/magicbullet $ electron .動いた!うれしい!!
Clusterize.js の導入
今回はClusterize.jsのデモページをElectronで試してみます。
まずはgitからデモページのソースを取得。### 任意のディレクトリで実行 user_name@machine_name MINGW64 ~/source/repos $ git clone https://github.com/NeXTs/Clusterize.js Cloning into 'Clusterize.js'... remote: Enumerating objects: 776, done. remote: Total 776 (delta 0), reused 0 (delta 0), pack-reused 776 Receiving objects: 100% (776/776), 1.89 MiB | 2.01 MiB/s, done. Resolving deltas: 100% (416/416), done. user_name@machine_name MINGW64 ~/source/repos $ cd Clusterize.js/ user_name@machine_name MINGW64 ~/source/repos $ git checkout gh-pages Switched to a new branch 'gh-pages' Branch 'gh-pages' set up to track remote branch 'gh-pages' from 'origin'.チェックアウトしたコードをElectron用ディレクトリにコピーします。
Electronを起動。
user_name@machine_name MINGW64 ~/source/repos/magicbullet $ electron .40万件のデータもサクサクスクロールできる!
いまクライアントアプリケーションでこれだけのデータをストレスなく描画するにはベストな選択なのではないでしょうか?(わかんない)以降は、実際のシステムに組み込めるか試していきたいと思います。
追記:
えっ!?Electron.Netってものがある!?
- 投稿日:2019-08-11T02:32:08+09:00
JavaScript APIについて メモ
APIとはApplication Programming Interface(アプリケーション・プログラミング・インターフェイス)」の略です。
例えば、ある地域の天気の情報をプログラムで取得する方法を考えてみましょう。すべて自分で行おうとしたら、膨大な手間がかかることが想像できます。
その地域に気象センサーを設置
得られた情報をデータ格納用のコンピュータに蓄積
雨量や湿度から、晴れや曇りといった天気を判断
プログラムからデータ格納用のコンピュータにアクセスし、該当する地域の天気の情報を取得
しかし本来行いたいことは「ある地域の天気の情報を取得する」ことです。そこでデータの準備を切り離し、条件を指定するだけで簡単に利用できるようにしたのがAPIです。APIを使うと以下のようになります。プログラムから天気の情報を提供するAPIに地域を指定してアクセスし、該当する地域の天気の情報を取得する
- 投稿日:2019-08-11T00:12:56+09:00
javascript A-Frameとは
A-Frame
VR体験を構築することができるウェブフレームワークである。HTMLで簡単に作ることができる。
<html> <head> <script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script> </head> <body> <a-scene> <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box> <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane> <a-sky color="#ECECEC"></a-sky> </a-scene> </body> </html>
- 投稿日:2019-08-11T00:04:54+09:00
enebularでTwitterAPI連携してNoodlに表示する
ふわもこ部とは
突然ですが、ハッシュタグ「#ふわもこ部」をご存知でしょうか?
猫や犬、鳥、フワフワモコモコした動物たちの写真や動画に付けられる動物好きにはおなじみのハッシュタグです。
ハリネズミも腹部はフワモコなので、このハッシュタグが付けられることがあります。
癒やされるので#ふわもこ部、ぜひチェックしてみてください。#ふわもこ部ツイートがされたらその画像を表示したい!
今日作るのはコレ!
誰かが#ふわもこ部 ツイートを投稿したら、ツイートに添付されてる画像を表示するデジタルフォトフレーム!
enebular でツイッター連携してNoodlで表示の巻 pic.twitter.com/J8RZYDiVoc
— はりねずみ麺 (@hedgehog_noodl) August 10, 2019enebularを核としてTwitter API・Noodlをつなぎます。
【enebular】Twitter APIをつなぐ!
フローはこんな感じです。バックエンドのコード書かないでTwitter APIが使えるだなんて最高!
twitterノード
検索条件: #ふわもこ部
出力: tweetdebutノードの設定「対象」は常に「オブジェクト全体」に設定すると良い!
functionノード
Twitter APIから受け取ったオブジェクトの中から、ツイートの文章と画像のURLだけを抜き出して返す関数です。
画像はついている場合とついていない場合があるので、
2行目でTwitterオブジェクトに「media」キー(画像URLのプロパティが入っている場所)が含まれているか否かを判定しています。
画像が含まれている場合のみ、メッセージを返します。var media = ""; if ("media" in msg.tweet.entities) { media = msg.tweet.entities.media[0].media_url; msg.payload = {tweet: msg.payload, media: media }; return msg; }すごく大事なことなのですが、NoodlとMQTT通信するときは、
ネスト(入れ子)になっていないJsonまたはJSオブジェクトで送りましょう!
(ネストされてても受け取れるけど、Noodl側でJavascriptノードを使ってあれやこれやする必要がある。)
- tweet: msg.payloadはツイートの文章
- media: media は画像のURL
mqttノード(出力)
enebular-Noodl間はMQTTでやり取りをします。
ブローカーはshiftr.ioを使っています。トピック: firstMessage(任意のものでOKです。あとでNoodlでも使用します。)
画像ではJsonノードが入っていますが、別に必要ありませんでした。
functionノードから直接mqttノードでOKです。【Noodl】フォトフレームを作る
enebularからデータの受信
Receive Messageノードを使います。
- Topic: /firstMessage (enebularのmqttノードのトピックと同じ設定に)
- Payload: 「tweet」「media」の2つを追加。(enebularのfunctionノードで指定したキーと合わせてください。functionノードの5・6行目参照)設定ができたら、図のようにReceive MessageのPayload tweetはtextノードに、mediaはimageノードのImage Pathにつなぎます。
うまく表示されていれば成功です!
ディゾルブ表現を作る
やっぱりフォトフレームといえばディゾルブ表現ですよね。
ジワッと変わるやつ。
2枚の写真を重ね、#ふわもこ部への新規ツイートをトリガーにそれぞれ写真の透明度を変えることで表現します。
使うノードは図の通り。
- Counterノード: enebularからツイッター情報を受け取るたびに+1カウントします。
- Expression:
count%2 == 0
カウンターが2の倍数のときにtrueを返す- Inverter: Expressionノードと逆のリザルトを出力する(=2の倍数じゃない時にtrueを返す)
- States: 2枚の画像の不透明度を制御します。図のようにプロパティを入力、つないでください。
- Model: 届いたURLを保持する
- pic1 idをpic1、Properties「img」を追加。counterが2の倍数のときにデータを保存する。
- pic2 idをpic2、Properties「img」を追加counterが2の倍数でないときにデータを保存する。
さらにディティールをつめてく
ここまでで、ジワッと画像を変更することはできるのですが、画像が変わる時に白っぽくなるのが気になるので細かい調整をしていきます。
2枚の写真と、その上にスタックされているRectangleノードに注目。
- Rectangle: Colorを
#000000
に設定- Image: Blend ModeをAddictiveに設定(2つとも設定してください)
これで理想のディゾルブ表現ができました!
Addictiveは加算。この辺の設定はPhotoshopっぽいですね。本編とあまり関係ない話
enebularのスペルを頻繁に打ち間違えてしまう!!!
ちなみにミスりポイントは以下の通り!辞書に単語登録するか・・・