- 投稿日:2019-05-23T23:53:32+09:00
Magic Leap MagicScript Landscape Application. UiTab
Prepare
Magic Leap One
https://www.magicleap.com/magic-leap-onemlsdk v.0.20.0
https://creator.magicleap.com/downloads/lumin-sdk/overviewmagic-script-cli v2.0.1
https://www.npmjs.com/package/magic-script-climagic-script-polyfills v2.2.0
https://www.npmjs.com/package/magic-script-polyfills3d model file
https://poly.google.com/view/esWz3QtmDCXCreate Project
magic-script init my-tab org.magicscript.tab "Tab" cd my-tab mkdir resplace imgae file and 3d model file in res folder.
Code
Change app.package
DATAS = "digest.sha512.signed" : "." \ "bin/" : "bin/" \ "res/" : "res/" OPTIONS = package/minApiLevel/2Change app.js
import { LandscapeApp, ui } from 'lumin'; const { UiTab, UiText, UiImage, UiLinearLayout, EclipseLabelType, Orientation, Alignment} = ui; export class App extends LandscapeApp { onAppStart () { const prism = this.requestNewPrism([0.9, 0.3, 0.5]); const topLayout = UiLinearLayout.Create(prism); const text = UiText.Create( prism, "Standard Tabs:", EclipseLabelType.kT7); text.setTextSize(0.0303); topLayout.addItem(text, [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT); topLayout.setOrientation(Orientation.kHorizontal); const tabLayout = UiLinearLayout.Create(prism); tabLayout.setOrientation(Orientation.kVertical); const tab1 = UiTab.Create(prism, "Tab1"); tab1.setTextColor([1, 1, 1, 0.4]); tabLayout.addItem(tab1, [0, 0, 0.02, 0.02], Alignment.CENTER_LEFT); const tab2 = UiTab.Create(prism, "Tab2"); tab2.setTextColor([1, 1, 1, 0.4]); tabLayout.addItem(tab2, [0, 0, 0.02, 0.02], Alignment.CENTER_LEFT); topLayout.addItem(tabLayout, [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT); const resource = prism.createModelResourceId( "res/ar-poop-vroom.glb", 0.1); const model = prism.createModelNode(resource); const image = UiImage.Create(prism, "res/diamond-1857736_640.png", 0.1, 0.1); topLayout.addItem(image, [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT); tab1.onActivateSub(function(event) { topLayout.removeItem(2); topLayout.addItem(image, [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT); } ); tab2.onActivateSub(function(event) { topLayout.removeItem(2); topLayout.addItem(model, [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT); } ); prism.getRootNode().addChild(topLayout); } }Build
magic-script build -i
Run
magic-script run --port=10000Reference
UiTab(Magic Script API Doc)
https://docs.magicscript.org/lumin.ui.UiTab.htmlTabs (UiTab)(Guide C++)
https://creator.magicleap.com/learn/guides/luminrt-uitabmagicscript
https://www.magicscript.org/Thanks!
- 投稿日:2019-05-23T22:48:15+09:00
フロントエンド未経験のSEがポートフォリオ作ってみた
はじめに
普段はSEとしてJavaでバックエンドの開発を行っているのですが、最近フロントエンドの学習を始めました。
学習の一環として自身のポートフォリオを作成しました!
私と同じ様にフロントエンドの経験が浅い方で、何か作ってみたいと考えている方の参考になれば幸いです。作ったもの
https://masayuki777.github.io/
作り方
以下の記事を参考にしてテンプレートベースに作成を行いました!
・エンジニアでポートフォリオ作りました
・うちゅういちかんたんなポートフォリオのつくりかた↓テンプレートはこちら
・テンプレート
編集したのはhtml(
index.html
)とcss(style.css
)で後は画像の差替えなどです。アイコンは以下のサイトのもを利用しました。
https://fontawesome.com/
無料で利用できるのと登録などが不要な点が良かったです。また、ヘッダーのグラデーションに関しては以下のサイトのCSSを利用しました。
https://uigradients.com/#Lunada
右上のGet css
からCSSをコピーして利用できる点が良かったです。ホスティングに関してはgithub.ioを利用しました。
公式サイトは以下ですが、他にも分かりやすくまとめられている記事はたくさんあるので簡単に公開できました。
https://pages.github.com/まとめ
今回初めてWEBサイトを作成したのですが、学ぶことが多くとてもいい経験になりました。
中身はスカスカですし、実力不足でデザインを諦めた部分もあるので、継続的にアップデートしていきたいと思います。(小さく作って大きく育てるの精神大事)
- 投稿日:2019-05-23T22:43:13+09:00
【jQuery】【Ajax】iTunes APIを使って音楽情報の検索フォームを作り、Ajaxを使って表示させる
iTunesAPIを使って音楽情報を検索したい!
はじめまして。記念すべき初めてのQiita投稿です。
私はRailsチュートリアルから勉強を初めて2〜3ヶ月のエンジニア転職志望者でございます。
ポートフォリオ制作として現在音楽のSNSアプリを作っています。その過程で、このwebアプリになくてはならない
音楽CDのアーティスト名とかタイトルとかジャケット情報をフォームから検索して表示
させる部分をド初心者なりに必死こいて作りなんとか動く形になったため、はじめてのアウトプットとしてまとめます!!動けば万々歳精神で作ったものなので、もっとこうすれば綺麗にできるよ!
というアドバイスありましたらバシバシご指摘いただけると嬉しいです使ったもの
当初、音楽系APIで使えるものを「音楽系API(主に音楽配信サービス)まとめ」等から探していました。
Apple Music APIを使おうとしたのですが、理解が全く追いつかず断念。いつかリベンジしたい...途方にくれていたところこんなものを発見。
なんと
https://itunes.apple.com/search
の後ろに色々検索パラメータをつけるだけでJSON形式でデータが取得できるとのこと!リクエストで返ってくるデータはこんな感じらしいです。
result{ "resultCount": 2, -"results": [ -{ "wrapperType": "track", "kind": "song", "artistId": 94949844, "collectionId": 317186212, "trackId": 317186341, "artistName": "the pillows", "collectionName": "Rock stock & too smoking the pillows", "trackName": "Funny Bunny", "collectionCensoredName": "Rock stock & too smoking the pillows", "trackCensoredName": "Funny Bunny (Rock Stock Version)", "artistViewUrl": "https://itunes.apple.com/jp/artist/the-pillows/id94949844?uo=4", "collectionViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4", "trackViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4", "previewUrl": "http://a744.phobos.apple.com/us/r20/Music/v4/2f/53/63/2f536341-4614-7453-1275-d68fdc0df2e2/mzaf_7461742241849893593.aac.m4a", "artworkUrl30": "http://is2.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.30x30-50.jpg", "artworkUrl60": "http://is1.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.60x60-50.jpg", "artworkUrl100": "http://is4.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.100x100-75.jpg", "collectionPrice": -1, "trackPrice": 250, "releaseDate": "2009-06-03T07:00:00Z", "collectionExplicitness": "notExplicit", "trackExplicitness": "notExplicit", "discCount": 1, "discNumber": 1, "trackCount": 14, "trackNumber": 10, "trackTimeMillis": 206600, "country": "JPN", "currency": "JPY", "primaryGenreName": "Rock" }, (以下略)詳しい使い方は記事を参照してください。
ちなみにiTunesで扱っているコンテンツであれば音楽以外も取得できるようです。ということで、今回こちらのAPIを使って検索フォームを実装してみました。
こんな感じでできた
検索キーワードとして
- アーティスト名
- アルバムタイトル
の2つでCD情報を検索したいと思います。
search.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script> $(function () { $('#search_button').on('click', function () { //入力したアーティスト名とアルバム名を検索フォームから取得。 var artistname = $('#my-form [name=artistterm]').val(); var albumname = $('#my-form [name=albumterm]').val(); //変数termに検索キーワードをがっちゃんこしていれます var term = artistname + albumname; //ajax通信を行い、クエリパラメーターをAPI側に送信 $.ajax({ url: 'https://itunes.apple.com/search', type: 'GET', data: { term: term, country: 'jp', media: 'music', entity: 'album', lang: 'ja_jp', limit: '10' }, dataType: 'json', timespan: 1000 }).done(function (data) { //通信が成功したときの動き var result = data.results[0]; if (result == null) { //検索結果がなかった場合 $("#image-box").children("img").attr({ 'src': '' }); $('#searchresult').text("検索結果が見つかりません"); } else { //検索結果が存在した場合 $("#searchresult").remove(); $('#artistname').text(result.artistName); $('#albumtitle').text(result.collectionName); $('#artwork').text(result.artworkUrl100); var url = result.artworkUrl100; $("#image-box").children("img").attr({ 'src': url }); } }); }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き $("#span1").text(jqXHR.status); //例:404 $("#span2").text(textStatus); //例:error $("#span3").text(errorThrown); //例:NOT FOUND }); }); </script> </head> <body> <!--以下、検索フォームなどの表示部分--> <p>アーティスト名:<span id="artistname"></span></p> <p>トラック名:<span id="albumtitle"></span></p> <p>アルバム画像URL:<p><span id="artwork"></span></p> <div id="image-box"><img /></div> <p id="searchresult"></p> <form id="my-form"> <input type="text" name="artistterm" value="" /> <input type="text" name="albumterm" value="" /> </form> <input type="button" id="search_button" value="検索"> <p>エラー原因</p> <p><span id="span1"></span></p> <p><span id="span2"></span></p> <p><span id="span3"></span></p> </body> </html>jQueryやAjaxの基本的な使い方の解説などは、
私が正直全然ここで説明できるほど身につけていないので他の記事におまかせいたします。。。自分のRailsアプリで使いたいとき
自身のアプリのapp/assets/javascript配下に
search.js$(function () { $('#search_button').on('click', function () { //入力したアーティスト名とアルバム名を検索フォームから取得。 var artistname = $('#my-form [name=artistterm]').val(); var albumname = $('#my-form [name=albumterm]').val(); //変数termに検索キーワードをがっちゃんこしていれます var term = artistname + albumname; //ajax通信を行い、クエリパラメーターをAPI側に送信 $.ajax({ url: 'https://itunes.apple.com/search', type: 'GET', data: { term: term, country: 'jp', media: 'music', entity: 'album', lang: 'ja_jp', limit: '10' }, dataType: 'json', timespan: 1000 }).done(function (data) { //通信が成功したときの動き var result = data.results[0]; if (result == null) { //検索結果がなかった場合 $("#image-box").children("img").attr({ 'src': '' }); $('#searchresult').text("検索結果が見つかりません"); } else { //検索結果が存在した場合 $("#searchresult").remove(); $('#artistname').text(result.artistName); $('#albumtitle').text(result.collectionName); $('#artwork').text(result.artworkUrl100); var url = result.artworkUrl100; $("#image-box").children("img").attr({ 'src': url }); } }); }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き $("#span1").text(jqXHR.status); //例:404 $("#span2").text(textStatus); //例:error $("#span3").text(errorThrown); //例:NOT FOUND }); });上記を「○○.js」の名前でファイルを作って格納。
またRails.5.x以降からjQueryを使えるように設定しないといけないらしいので、以下の記事などを参考に設定。
Rails 5.x標準で Ajax+(jQuery+Partial) でHTML部分更新する世界一シンプルなサンプルあとは
<body>
以下の検索フォーム部分をお好きなビューファイルに入れるだけで動くと思います。参考記事
上記を作るにあたって参考にした記事を以下に挙げます。
私のように「Railsチュートリアルは一通りやったけど、jQueryとかAjaxとか全くわからーん」という人は以下を参考にするとなんとなくやっていることが分かると思います。
まずこの記事のコードを基本としてアレンジしました。
その他大いに参考にしたもの
- railsふかぼり01:railsでWebAPIを叩く
- Ajax(非同期通信)についてわかりやすさ重視でまとめてみた(Rails使用のデモ付)
- jQuery.ajax()のまとめ
- Ajaxで外部APIへ通信する方法
- jQueryでフォームの値を取得する方法をまとめておくので、コピペでご利用ください。
最後に個人的感想
初めて記事を書いたので、
節目としてここまで勉強してきた中で個人的な感想を記録しておきます初ポートフォリオ制作はインスタの音楽ジャケ版的なアプリを作りたいなぁ〜と思い(そういうサービス既にありますが)、
作る前はなんとなく「音楽系のAPIあるだろうし、理想形はそこからデータ取ってきてそれをそのまま投稿できたらいいなぁ」とぼんやりイメージしていた程度でした。いざ作り始めてからは、必死に調べては参考記事のコードをアレンジし、試行錯誤して
あれよあれよと言う間に気づいたら結果的に理想と同じ機能を作ることができました。どのAPIを使うか?の段階でそもそもAPIの知識が足りず、若干壁にぶち当たりましたが
粘り強くググっていたら初心者にも簡易に使えるものを発見できたのが、今回の超ラッキーでしたそしてAPIの使い方の丁寧なまとめがあったことも
ありがたや。
とりあえず今回学んだことは粘り強くググることの大切さでしたまだアプリは完成していませんが、この機能を実装できたことがちょっとした自信とモチベーションにつながりました。今後もがんばるぞ!!
そして私と同じくエンジニア目指して勉強に勤んでいる方々、一緒に頑張りましょう!
ここまで読んでいただいてありがとうございました。
- 投稿日:2019-05-23T19:24:30+09:00
value を変えずに input に3桁カンマを振る
value を変えずに input に3桁カンマを振る
「数値に3桁カンマを振りたい」って要望は結構あって、ググると js で value 自体をゴリゴリいじる実装が多いですが、今回はちょっと違ったアプローチをしてみます。
コード
<html lang="ja"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head> <body> <style> /* number-input と number-overlap をまとめるコンテナ */ .number-container { position: relative; } /* カンマ区切りにしたい input 要素 */ .number-input { position: relative; } /* 普段は z-index:1 で number-overlap の後ろに居る。色は被ってしまうので transparent */ .number-input:not(:focus) { z-index: 1; color: transparent; } /* フォーカス中は z-index:3 で number-overlap の前に来る。色も戻す */ .number-input:focus { z-index: 3; color: #555; } /* number-input に覆い被さる要素。 pointer-events:none でクリックを透過させる */ .number-overlap { position: absolute; top: 1px; left: 0px; display: inline-block; background: transparent; width: 100% !important; pointer-events: none; z-index: 2; } </style> <script> $(function () { // フォーカスを抜けたり値が変わったりしたら覆い被さっているテキストをカンマ区切りにする $('#hoge').on('blur change input', function () { const $this = $(this); if ($this.val() !== '') { $this.next().text((+$this.val()).toLocaleString()); } }); }); </script> <form class="form-inline"> <div class="form-group"> <div class="number-container"> <input type="text" id="hoge" name="hoge" class="form-control number-input" placeholder="自動でカンマが付きます"> <span class="number-overlap form-control"></span> </div> </div> </form> </body> </html>殴り書きしたのでかなり荒いですが、コメントを読めば何をしているかは分かると思います。
一応説明を加えると下記のような感じです。.number-container
position
で要素を重ね合わせる必要があるので、そのためのコンテナです。
それ以上の意味は特にありません。.number-input
input
に付与します。
普段はz-index
で後述の.number-overlap
の後ろに隠れていますが、フォーカスすると前面に出てきます。.number-overlap
実際に3桁カンマの文字列が設定される要素です。
普段はz-index
で前述の.number-input
に被さっていますが、フォーカスされると後ろに引っ込みます。
pointer-events:none
なのでクリックを透過します。これが今回のキモです。これによって「被さっているんだけど後ろの要素がクリック(フォーカス)できる」が実現できます。javascript
フォーカスとか見え方とかは css だけで完結しますが、「カンマを振る処理そのもの」はどうしても js の力を借りる必要があるのでそれをしています。
できれば html + css だけでやりたい・・・。注意点
ただし上記はかなりサボっています。実際はいろいろ調整しないと使い物になりません。
まず
.number-overlap
のスタイルはnumber-input
に完全に合わせる必要があります(padding や font-size など多岐にわたる。合わせないとすぐズレる)。
上記は bootstrap を使っていますがform-control
はinput
に限定されないので流用してるだけです。しっかりやろうとすると結構めんどくさいです。
実際、上記は chrome で確認しましたが、他のブラウザではズレたりします。そういった微調整が必須になります。また、個人的に数値入力は
type=number
を使うことが多いです。さらに右寄せにしたいですが、スピンボタンがくっつくのでブラウザごとの調整が必要です(幅がブラウザごとに全く異なります)。
正直かなり大変なので、いっその事スピンボタンは消したほうが良いです。js サイドも上のコードだと数値以外を入れると NaN になるので、もっといろいろな分岐が必要です。
NaN はtype=number
でサボったり出来ますがe
の罠があったりして、正直「ロケール指定ができるtype=number
があればいいのに・・・」と思います。まとめ
注意点で記載した所が調整・クリアできれば変換の面倒をまったく見なくて済むようになるので結構有用だと思います。
(js で値をゴリゴリいじるのはなんか好かん)。
- 投稿日:2019-05-23T18:51:56+09:00
Javascriptにおける 論理演算子 || の動きについて
本記事について
おそらく初心者向けですが、
Javascriptで、a || b
という記述があった時の論理演算子の動きについて共有します。Javascriptの開発の中で、下記のような記述を読み解くのに少し時間がかかったので、自分用のメモとしても残しておきます。
※こちらはService Worker+Cache APIを実装した際のコードですが、Service Workerについては別の機会にまた解説したいと思います。
self.addEventListener('fetch', (event) => { event.respondWith( caches.open(CACHE_NAME) .then(function(cache) { return cache.match(event.request) .then(function (response) { return response || fetch(event.request) .then(function(response) { cache.put(event.request, response.clone()); return response; }); }); }) ); });Javascriptの処理
この処理を順番に整理すると
・caches.open(CACHE_NAME)
でCACHE_NAMEと一致したかどうかを確認し、Promiseを返す(成功していればthen内部の処理へ)
・cache.match(event.request)
でcacheの中身がevent.requestにマッチしていればPromiseを返す(そして2つ目のthen内部の処理へ)return response || fetch・・・・と続いていくわけですが、
ここの処理は、
レスポンスを返すか、fetchでリクエストを送るか
ということになります。(当たり前だ)a || bの処理の仕様
・・・なのですが、処理の中で
a || b
(aまたはb)となった時にえ、じゃあ結局どっちの処理をするの?
ということを疑問に思う方もいるかもしれません。
この「または」を表す || という論理演算子の考え方としては
処理a || 処理b
となっている場合は
処理aがtrueの場合、処理aのみを実行
処理aがfalseの場合、処理bを実行となります。
なんか、if文でやたらと
if(a || b) { //aまたはbなら処理 }と刷り込まれてしまっていると、「あれ?aかbかが実行されると書いてあるけど、それって何で決まるんだっけ?」
とふと思ったのでググって自分なりにコードの意味を噛み砕いていくことができました。一応先ほどのコードの意味を順番に書いておきます。
・caches.open(CACHE_NAME)
でCACHE_NAMEと一致したかどうかを確認し、Promiseを返す(成功していればthen内部の処理へ)
・cache.match(event.request)
でキャッシュの中身がevent.requestにマッチしていればPromiseを返す(そして2つ目のthen内部の処理へ)
・return response
がtrueならばそこでブラウザに処理を返す
・return response
がfalseならばfetchの処理に進む
・response.clone()
でレスポンスをクローンして、cache.put()
キャッシュを更新するという感じになります。
a || bをちゃんと調べてまとめてみましたとさ。
参考:MDN
- 投稿日:2019-05-23T18:07:34+09:00
BinaryASTでJavaScriptのロードを高速化
TL; DR
JavaScriptファイルをプリコンパイルしておける機能。
ただし、まだ実験段階なので2019年5月時点で実用は出来ません。
サポートしているのはFirefox Nightly版のみ。BinaryASTとは
BinaryASTは、JavaScriptの初回ロードを高速化します。
初回ロードの高速化というと、ダウンロード速度の方をイメージしてしまいますが、
ダウンロード後(あるいはキャッシュ読み込み後)のブラウザでのパース作業を高速化します。ASTについて
ブラウザで実行する通常のJavaScriptコードの場合、ソースはコードの構文構造を記述する抽象構文木(Abstract Syntax Tree, AST)と呼ばれる中間表現に解析されます。
この表現はバイトコードまたはネイティブのマシンコードにコンパイルして実行することができます。生のJavaScriptconst hoge = 3 + 4;AST表現されたJavaScript{ "type": "Program", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "hoge" }, "init": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Literal", "value": 3, "raw": "3" }, "right": { "type": "Literal", "value": 4, "raw": "4" } } } ], "kind": "const" } ], "sourceType": "script" }JavaScriptコードからASTへの変換は以下のページで試せます。
http://esprima.org/demo/parse.html高速化の仕組み
BinaryASTは、元のJavaScriptコードとそれに関連付けられたデータ構造をバイナリ形式で表現します。
これによりクライアントでの上記パース作業を簡略化しようという仕組みです。
モバイル端末などのローエンドなクライアントほど効果が出ます。pre-encode
エンコーダーはRustで実装されているようです。試しにエンコードしてみます。
お試しなのでサクッとDockerで実行します。Rustインストールdocker run --rm -it ubuntu apt-get update apt-get upgrade apt-get install curl curl https://sh.rustup.rs -sSf | sh
1を選択1) Proceed with installation (default)
cargoにパスを通すsource ~/.cargo/env
nodejsとnpmをインストールapt-get install nodejs npm
binastを取ってくるapt-get install git git clone https://github.com/binast/binjs-ref
エンコーダーとデコーダーcd binjs-ref cargo run --bin binjs_encode -- --help cargo run --bin binjs_decode -- --help
ここまでで環境準備は整いました。
エンコードを試してみます。hoge.jsconst hoge = 3 + 4; console.log(hoge);エンコード実行cargo run --bin binjs_encode -- -i hoge.js -o output cd output ll total 28 drwxr-xr-x 2 root root 4096 May 19 04:33 ./ drwxr-xr-x 15 root root 4096 May 19 04:33 ../ -rw-r--r-- 1 root root 379 May 19 04:33 hoge.binjs -rw-r--r-- 1 root root 10 May 19 04:33 hoge.grammar -rw-r--r-- 1 root root 39 May 19 04:33 hoge.js -rw-r--r-- 1 root root 10 May 19 04:33 hoge.strings -rw-r--r-- 1 root root 10 May 19 04:33 hoge.tree
binjsファイルが出来ていますね。
hoge.binjsBINJS[GRAMMAR]identity;(IdentifierExpression0LiteralNumericExpression(AssertedDeclaredName2AssertedScriptGlobalScope BinaryExpression"BindingIdentifier CallExpression&ExpressionStatement Script,StaticMemberExpression&VariableDeclaration$VariableDeclarator[STRINGS]identity;P hogeconsolelog+ constconst lexical[TREE]identity;Z @@デコーダーも試してみましょう。
デコード実行cargo run --bin binjs_decode -- ./output/hoge.binjs decode_hoge.js
スペースや改行は消されましたが、戻っていますね。
行末のセミコロンも消されているので、minifyされているんでしょうか。decode_hoge.jsconst hoge=3+4;console.log(hoge)ついでにダンプも実行してみます。
ダンプ実行cargo run --bin binjs_dump -- ./output/hoge.binjs
ダンプ結果
stdoutAttempting to decode as multipart. 0 : 10 # Script { : # .scope 1 : 06 # AssertedScriptGlobalScope { : # .declaredNames 2 : 02 # list (length=1) [ 3 : 04 # AssertedDeclaredName { : # .name 4 : 00 # string="hoge" : # .kind 5 : 0a # string="const lexical" : # .isCaptured 6 : 00 # bool=false : # } : # ] : # .hasDirectEval 7 : 00 # bool=false : # } : # .directives 8 : 00 # list (length=0) [] : # .statements 9 : 04 # list (length=2) [ 10 : 14 # VariableDeclaration { : # .kind 11 : 08 # string="const" : # .declarators 12 : 02 # list (length=1) [ 13 : 16 # VariableDeclarator { : # .binding 14 : 0a # BindingIdentifier { : # .name 15 : 00 # string="hoge" : # } : # .init 16 : 08 # BinaryExpression { : # .operator 17 : 06 # string="+" : # .left 18 : 02 # LiteralNumericExpression { : # .value 19 : 00 00 00 00 00 00 08 40 # float=3 : # } : # .right 27 : 02 # LiteralNumericExpression { : # .value 28 : 00 00 00 00 00 00 10 40 # float=4 : # } : # } : # } : # ] : # } 36 : 0e # ExpressionStatement { : # .expression 37 : 0c # CallExpression { : # .callee 38 : 12 # StaticMemberExpression { : # .object 39 : 00 # IdentifierExpression { : # .name 40 : 02 # string="console" : # } : # .property 41 : 04 # string="log" : # } : # .arguments 42 : 02 # list (length=1) [ 43 : 00 # IdentifierExpression { : # .name 44 : 00 # string="hoge" : # } : # ] : # } : # } : # ] : # }サーバーへの配置
binast-cf-worker を見る感じ、リクエストヘッダーの
Accept
にapplication/javascript-binast
が含まれていれば、Content-Type
でapplication/javascript-binast
を指定した.binjsファイルを返せば良さそうです。上記のサンプルでは、クライアント判定NGの場合は通常の.jsファイルを返すようにしていますが、今回はお試しなので、.binjsファイルなら無理やり
application/javascript-binast
にしてみます。また、httpsでないとbinASTは有効にならないようですので、ローカルでテストするときは注意。
とりあえずオレオレ証明書で回避しておきます。docker run --rm --name binjstest -v ~/output:/usr/share/nginx/html:ro -d -p 8080:80 nginx docker exec -it binjstest bash apt-get update apt-get install openssl openssl genrsa 2048 > server.key openssl req -new -key server.key > server.csr openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt mkdir /etc/nginx/ssl mv server* /etc/nginx/ssl apt-get install vim vim /etc/nginx/conf.d/default.conf vim /etc/nginx/mime.types nginx -s reload/etc/nginx/conf.d/default.confserver { listen 80 ssl; server_name localhost; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_certificate /etc/nginx/ssl/server.crt; :/etc/nginx/mime.typestypes { text/html html htm shtml; text/css css; text/xml xml; image/gif gif; image/jpeg jpeg jpg; application/javascript js; application/javascript-binast binjs; :~/output/hoge.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <script type="text/javascript" src="hoge.binjs"></script> </head> <body> </body> </html>クライアント準備
現在BinaryASTをサポートしているのはFirefox Nightly版のみとのことで、インストールします。
about:configにて以下を設定します。
dom.script_loader.binast_encoding.domain.restrict
をfalse
dom.script_loader.binast_encoding.enabled
をtrue
hoge.htmlをブラウザで表示
以下のエラー。えぇ、プレビュー版はconstは使えないのかよ!
SyntaxError: BinAST Parsing Error: Const is not supported in this preview release hoge.binjs
constをvarに変えて再度エンコード。デプロイして再確認。
やっとできました。ちゃんとBinary表現になっていますね。結果も正しく動いています。
まとめ
情報が少ない中、何とかここまでたどり着けました。
今回は短いソースなので、ファイルサイズも逆に大きくなってしまいましたが、次回は大きなファイルでどれだけ変わるかやってみようと思います。(そこが重要)ちなみに、公式デモサイトである下記は、正しくmime-typeを返していない為binjsになっていないようです。(2019/05/23)
https://serve-binjs.that-test.site/
- 投稿日:2019-05-23T17:35:44+09:00
Map Objectのイディオム集
Map Objectは JavaScriptで連想配列を実現するための機能です。
今まではObjectを連想配列として使っていたわけですが、ES6から、専用のクラスが用意されるようになりました。今まであまり使ってこなかったですが、せっかくなので、よく使う表現をメモしておきます
使い方はざっと以下のような感じ、
const user1: User = { userId: 1, userName: "user1" } const user2: User = { userId: 2, userName: "user2" } const userMap = new Map<number, User>() // Map Objectを作成。キーとバリューになる型をジェネリックを使って定義できる。また、定義しなくても型推論してくれる userMap.set(user1.userId, user1) // UserオブジェクトをMapに登録 userMap.set(user2.userId, user2) userMap.get(1) // userIdが1のuserを取得 userMap.delete(2) // userIdが2のuserを削除基本
上で紹介したもの以外に以下のようなメソッドが用意されています。
.has(key)
そのキーのメンバが存在するかどうかをチェックします。
.entries()
/.keys()
/.values()
それぞれ、Mapのメンバ、キー、バリューのイテレータを返します。
単体で使うことは少ないですが、後述する配列への変換処理だったり、マージ処理するときに活躍します。
.forEach()
Arrayで使えるforEachと同じような感じですね。Mapオブジェクトのメンバーを回しながら順番に処理してくれます。
userMap.forEach((val,key,self)=>{ console.log(key, val,self) })
.size
Arrayで言うところの
.length
ですね。Map Objectに入っている要素数を返してくれます。userMap.size // => 2配列への変換
Map Objectは便利なのですが、いかんせん繰り返し処理に使えるメソッドが
forEach
しかなく、map()
やfilter()
reduce()
などが使えるArrayが恋しくなるときが多々あります。以下のように書けば、Map ObjectをArrayに変換することができます。
const userArray = [...userMap.values()] // => [user1, user2 ....]MDNのサイトで紹介されているのは以下のような方法ですね。
こちらでも同様ですconst userArray = Array.from(userMap.values()) // => [user1, user2 ....]もしもkeyだけの配列がほしいのであれば以下のような感じです
const userArray = [...userMap.keys()] // => [1, 2 ....]配列からMap Objectへの変換
なにか処理をするためにArrayに変換するのであれば、処理が終わったあとはもう一度 Map Objectに戻したくなるでしょう。
もしくはAPIから取得した値は配列で帰ってくるかもしれません。
その場合は以下のように書くことでMap Objectに変換することができます。
Map Object → Arrayのときよりも若干面倒ですねconst userMap = new Map(userArray.map(user => [user.userId, user])) // => Map<number,User>へ変換Map Object同士をマージする
配列ではなくMap Objectを使う主な理由は、キーを使った値へのアクセスが容易(かつ高速)なことに加えて、常にキーが一意になるようにObjectの塊を取り扱うことができることです。
例えば、同じユーザーIDをを持つUserが存在する可能性のある2つの配列(
usersArray1
とusersArray2
)を一つに結合して、ユーザーIDが一意になるように重複を取り除く事を考えてみましょう。配列に格納されている値がプリミティブな値であれば、
Set
を用いて一意な配列を作る事はできます。
しかし今回扱いたいObjectの場合は、以下のようにうまく一意になってくれません。const user2: User = { userId: 2, userName: "user2" } const sameUser2: User = { userId: 2, userName: "user2" } const duplicateUserArray = [user2, sameUser2] // user2とsameUser2は、全く同じメンバーを持っているが、インスタンスが違うので、重複しているとはみなされない console.log([...(new Set(duplicateUserArray))]) // => [ { userId: 2, userName: 'user2' }, { userId: 2, userName: 'user2' } ]しかし、こんな時もMap Objectであれば解決です。以下のように書くことで、キーが重複した値を除去しつつ、2つのMap Objectをマージすることができます。
重複したキーが有る場合は、後に指定したMap(この場合はuserMap2
)が優先されることに注意してください。// userMap1とuserMap2をマージ const mergeUserMap = new Map([...userMap1.entries(),...userMap2.entries()])例.tsconst userMap1 = new Map() // Map Objectを作成 userMap1.set(user1.userId, user1) // user1を追加 userMap1.set(user2.userId, user2) // user2を追加 const userMap2 = new Map() // Map Objectを作成 userMap2.set(user1.userId, user1) // user1を追加 userMap2.set(user3.userId, user3) // user3を追加 // userMap1とuserMap2をマージ const mergeUserMap = new Map([...userMap1.entries(),...userMap2.entries()]) // => Map { // 1 => { userId: 1, userName: 'user1' }, // 2 => { userId: 2, userName: 'user2' }, // 3 => { userId: 3, userName: 'user3' } }Objectで言うところの
Object.assign()
と同じような動きですね// userObjectとuserObject2をマージ const mergeUserObject = Object.assign(userObject1, userObject2)キーの差し替え
あまり頻度は多くないですが、Map Objectに設定したキーを別の値に置き換えたMap Objectを作りたくなるときがあります。
そんなときは以下のようにかけます。// キーをuserIdからuserNameに変更 const nameMap = new Map([...userMap2.values()].map(user => [user.userName, user]))一度配列に変換してからMapに戻すというめんどくさいことをやっているのであまり良くないですね。ちなみに一意じゃない値をキーに設定すると、例によって重複削除されてしまうので注意してください。
オブジェクトへの変換
2019/05/24追記: コメントで、より良い書き方を頂きました。感謝!
Object.fromEntries()
を使う方法です。今はStage4の状態みたいですが、IE、Edge,Node.js以外の主要な環境では使えるみたいです。TypeScript側での対応はまだみたいですが、2019/05/24時点でmasterにマージされているようなので、次のリリースでは使えるようになるのではないかと思います。
const userObject = Object.fromEntries(userMap) // userMapをuserObjectに変換
以前のやり方。
Object.fromEntries()
が使えるなら読む必要はない
JSON.stringify()
したいときなど、どうしてもObjectにしたいときがあります。
そういうときは以下のコードでオブジェクトに変換することができます。
本当は一文で書きたかったんですが、難しくなりすぎるのでやめました。const userObject:any = {} userMap.forEach((u,uId)=>{ userObject[uId] = u })
いっそ関数化してしまっても良いかもしれません。/** * Map => Objectに変換する */ function mapToObject(map: Map<any,any>):any{ const object: any = {} map.forEach((val, key) => { userObject[key] = val }) return object }オブジェクトからMap Objectへの変換
2019/05/24追記: コメントでより良い書き方を頂きました。感謝!
Object.entries()
を使うことでObject→Map Objectへ変換することができます。const userMap = new Map(Object.entries(userObject)); // userObjectをuserMapに変換ただし Symbol のキーは処理の過程で除かれてしまうので、それも含めるなら、以下のように書きます
const userMap = new Map() for (const key of [...Object.keys(userObject), ...Object.getOwnPropertySymbols(userObject)]) { userMap.set(key, userObject[key]) }以上、
後半考えると、別にObjectでもいいのでは?とも思っちゃいますね。ただ、配列→オブジェクトへの変換はMapのほうが楽かなという印象があります。全体的に、Array FriendlyなObjectって印象ですね。Object ⇔ Map Objectの変換も簡単にかけるので、連想配列にMap Objectを使わない理由はなさそうです。
これからはもっと使っていきたいと思います。もっとこんな便利な書き方あるよって人がいれば教えてください
- 投稿日:2019-05-23T17:11:39+09:00
【JavaScript】Stringの数字をNumberに変換方法
- 投稿日:2019-05-23T16:48:47+09:00
VueでJsの中に静的と動的に外部Jsを読み込む書き方
Vueプロジェクトを開発する際に、使っているJsの中に、外部の第三者のJsファイルのファンクションやオブジェクトを読み込んで利用することがよくあります。外部Jsを読み込むときには、静的にと動的に二つの方法があります。以下で簡単に説明してみます。
例文
index.vue<template> <div> <p>{{ msg }}</p> </div> </template> <script> import { t3m1 } from './t3' export default { name: 'test', data () { return { msg: 'you are ok.' } }, mounted () { t3m1() } } </script>t1.jsconst t1m1 = function () { console.log('t1m1 ok') } function t1m2 () { console.log('t1m2 ok') } export { t1m1, t1m2 }t2.jsconst t2 = {} t2.t2m1 = function () { console.log('t2m1 ok') } t2.t2m2 = function () { console.log('t2m2 ok') } export default { t2 }t3.js// 外部Jsを静的に読み込む書き方 import t2 from './t2' function t3m1 () { console.log('t3m1 ok') // 外部Jsを動的に読み込む書き方 import('./t1.js').then((t1) => { t1.t1m1() }) t2.t2.t2m1() } export { t3m1 }実行結果
- 投稿日:2019-05-23T14:50:44+09:00
ajaxで非同期通信とかを頑張った話
この記事について
Javascriptがぜんぜん解らない()駆け出しフロントエンジニアが、勉強目的で触ったajaxのまとめです。
対象者
- これからJavascriptを触る人
- pureなJavascriptで非同期通信処理をやろうとしている人
- 未来に存在しているであろう、ど忘れしている私へ
そもそもAjaxとはなんぞや
[Asynchronous JavaScript + XML]
その名の通り、JavascriptとXMLを使った非同期通信。
XMLに送るデータ内容を記述し、JavascriptのAPIであるXMLHttpRequestを使って、サーバー側にデータ送信する処理の事です。
ただ、最近はもうXMLを使わずにJSONにデータを記述することが多い。pureなJavascriptだとどうコーディングするの?
main.jslet xhr = new XMLHttpRequest(); xhr.open('POST','hogehoge.php',true); xhr.setRequestHeader('content-type','application/x-www-form-urlencoded;charset=UTF-8'); xhr.send( //送りたい内容を記述 ); xhr.onreadystatechange = function() { //サーバーからの応答に応じた処理 }各メソッドに対しての説明は省略しちゃいます。
詳しくはMDNの方で。これを、初期読み込み時にサーバーからデータを取得してきたり、
更新ボタンを押下したときのイベント処理の中に組み込んだり…と使っていきます。ただし、問題が…
JavaScript はシングルスレッドで動くため、当然ながら
send
メソッドでデータをサーバー側に送信した後は、後続の処理を引き続き行われます。main.jslet xhr = new XMLHttpRequest(); xhr.open('POST','hogehoge.php',true); xhr.setRequestHeader('content-type','application/x-www-form-urlencoded;charset=UTF-8'); xhr.send( //送りたい内容を記述 ); xhr.onreadystatechange = function() { //サーバーからの応答に応じた処理 } alert('りんご')このような場合、
send
メソッド実行後はalert
が実行され、りんごがアラートで表示されちゃいます。
当然ながら、そこでHTTP通信で返されてきた結果を扱いたいとしても、まだonreadystatechange
内に結果は返されていないはずなので、クリティカルなことになっちゃいます。
えらいこっちゃ…。
なので、こうしちゃいます。main.jsxhr.onreadystatechange = function() { if(xhr.readyState = 4 && xhr.status == 200) { alert(xhr.responseText) } }これで、サーバーから帰ってきたデータを扱うことができます。
やったね!ただ…
この非同期処理を連続で行って行きたい場合はどうなるかというと…
main.jslet xhrA = new XMLHttpRequest(); //設定とかの処理 xhrA.onreadystatechange = function() { if(xhrA.readyState = 4 && xhrA.status == 200) { let xhrB = new XMLHttpRequest(); //設定とかの処理 xhrB.onreadystatechange = function() { if(xhrB.readyState = 4 && xhrB.status == 200) { let xhrC = new XMLHttpRequest(); //設定とかの処理 xhrC.onreadystatechange = function() { if(xhrC.readyState = 4 && xhrC.status == 200) { //続く… } } } } } }わぁ、ネストの宝石箱やー。
これはもう見るだけで拒否感が出てしまいますね。
当然ながら、色々処理を省いてこれなので実際に運用するとさらに可読性が下がっていっちゃいます。
ヤバイですね☆解決方法
その1 Callback
callbackで実装する。
ES6(ES2015)以前で主に使われていた手法です。
問題のコードよりかはわかりやすくなりますが、callbackもコールバック地獄というネストが深くなる問題を抱えてるので、
ブラウザ側も最新のJS仕様に対応してきているので、最近では使用されないことが多いのと思います。その2 Promise
今回私が使ったのがこれでした。
ES6(ES2015)で実装された仕様。
Promiseを使うPromiseは非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。
main.jsfunction promiseFunction() { return new Promise((resolve,reject) => { //非同期で行いたい処理をここに記述する }) }成功した場合は
resolve
メソッドを呼び出すことで処理が終わったことを、呼び出し元に伝えることができます。
また、promiseではpromiseチェーンを使うことで、非同期処理を連続して行うことができます。main.jspromiseFunction().then(() => { return promiseFunction(); }) .then((result) => { return promiseFunction(); }) .then((result) => { return promiseFunction(); }) .catch((err) => { console.log('エラー'); });
then
の中で次に行いたいpromiseオブジェクトを呼び出すことで、処理をつなげることができます。
callbackやpureな実装時よりも、かなり直感的でシンプルになりました。その3 async/await
ES2017で実装された仕様。
Promiseよりチェーンをなくし、よりシンプルでわかりやすくなった非同期処理です。
async/awaitで非同期処理はシンプルになる
async/awaitを使ったモダンな非同期処理とはいえ全く別物というわけではなく、非同期処理の部分ではPromiseを使用しています。
どれを使えばいいの?
async/await
を使えばいいと思います。
私は先にPromiseを見つけた関係でそちらを使いましたが、よりモダンにするならasync/await
一択と思われます。
ただ、Promise
も使う場合があると思いますので、どっちも使えるようになっておくといいかな。終わりに
長くなってしまいましたが、pureなajaxを頑張ってみた記事でした。
これからpureな非同期処理をやる方に、少しでも情報を伝えることができたなら幸いです。参考記事
MDN
Promiseを使う
async/awaitで非同期処理はシンプルになる
async/awaitを使ったモダンな非同期処理
- 投稿日:2019-05-23T14:46:29+09:00
devDependenciesを削除してAWS Lambdaに高速アップロード
*この記事はA JavaScript user’s First AWS Lambda functionからの一部転用です。
TL;DR
npm prune
コマンドでdevDependenciesを一旦削除したらいいよね。はじめに
Wataruです。カナダのバンクーバーというところでCivic Tech関連の小さな会社でソフトウェアデベロッパーとして働いています。なぜカナダで働いているか、もし興味があったらnote書きましたので読んで頂けると嬉しいです。
仕事の半分以上はフロントエンドを担当しています。Firebaseを触って以来、サーバレスアーキテクチャに興味があり、AWSの勉強を始めました。日本で購入したAWSによるサーバーレスアーキテクチャを参考に学習を進めています。本題
上記の書籍の第3章にて、node.jsアプリケーションのAWS Lambdaへのデプロイについて触れられています。デプロイするアプリケーションの
package.json
(一部)は以下の通りです。// package.json { ... "scripts": { ... "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:000000000000:function:transcode-video --zip-file fileb://Lambda-Deployment.zip", "predeploy": "zip -r Lambda-Deployment.zip * -x *.zip *.log" } ... }デプロイは以下のnpmコマンドで行なっています。
$ npm run deploy
コマンドが実行されると、
package.json
に書かれたpredeploy
が実行され、deploy
が実行される、という流れになっています。
predeploy
: 作業ディレクトリの全てを"Lambda-Deployment.zip"という名前で圧縮。deploy
: AWSサーバーに圧縮したzipファイルをアップロード。書籍で紹介されているサンプルプログラム程度ならこのままでいいのですが、unit testを書いたりESLintやprettierなんかを入れたりしていると、devDependenciesが増えてしまい、zipファイルのサイズが大きくなってしまいます。大きくなればなるほど、アップロードに時間もかかってしまいますね。devDependenciesはアップロードする必要が無いので、いっそのこと消してしまいましょう。
npm prune
npm pruneはnpmのCLIコマンドの一つです。ドキュメントを引用すると:
This command removes “extraneous” packages. If a package name is provided, then only packages matching one of the supplied names are removed.
Extraneous packages are packages that are not listed on the parent package’s dependencies list.
翻訳してみます。
このコマンドは"余計な"パッケージを削除します。パッケージ名がコマンド実行時に与えられていたら、その名前に一致したものだけが削除されます。
余計なパッケージとは、そのパッケージを使っているpackage.json内のdependenciesの一覧にないパッケージのことです。
npm prune
には--production
フラグというものがあります。このフラグをつけてコマンドを実行すると、devDependencies
で指定された全てのファイルが消去されます。devDependenciesを削除できればzipファイルを軽量化できそうですね。しかし、このコマンドで削除したパッケージは開発には必要なものばかりです。削除した後でまたダウンロードするのも時間がかかりそうですよね。
npm install --offline
npm install
にはドキュメントには記載されていない、おそらく非公式の--offline
というフラグがあります。npm CLIのデベロッパー@zkatがそのフラグについてnpmのissueの中で言及しています。以下、部分的に引用します。
npm@5
will automatically install from cache if it doesn't detect network, or if your router lacks external access. So if you want to use npm on a plane, you can just use it. You don't need--offline
. It'll Just Work™.--offline
will ONLY be useful when you want to guarantee that no network access is done, and you want npm to crash when it doesn't have something in cache (this does not include git dependencies right now). So, this is what you use if you've hit your data cap and want to make damn sure npm doesn't suck up more data. Or, like, if you're temporarily tethering off your phone.翻訳してみます。
npm@5
はネットワークが検知されなかったら自動的にキャッシュからインストールします。飛行機などwifiがない環境でnpmを使いたかったら普通に使うことができます。--offline
フラグを使う必要はありません。--offline
フラグは、あなたがどうしてもネットワーク接続なしでnpmを使いたい時"だけ"便利です。キャッシュにパッケージがなかったらnpmコマンドはクラッシュするでしょう(git dependenciesは2017年9月時点でキャッシュサポートされていません)。あなたがインターネットのデータ使用量を超過してしまった時や、電話を使ってテザリングしているときなどに使えます。
--offline
フラグを使って明示的にキャッシュからインストールするようにすれば、削除したdevDependenciesの再ダウンロードの時間を抑えることができそうです。これらのnpmコマンドを使った
package.json
に書き換えてみます。// package.json { ... "scripts": { ... "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:000000000000:function:transcode-video --zip-file fileb://Lambda-Deployment.zip", "predeploy": "rm Lambda-Deployment.zip > /dev/null && npm prune --production && zip -r Lambda-Deployment.zip * -x *.zip *.log", "postdeploy": "npm install --offline" } ... }先程と少し異なり、
postdeploy
を追加しました。
predeploy
:
- 今ある"Lambda-Deployment.zip"を削除。
npm prune --production
でdevDependenciesを削除。- 作業ディレクトリの全てを"Lambda-Deployment.zip"という名前で圧縮。
deploy
: AWSサーバーに圧縮したzipファイルをアップロード。postdeploy
: キャッシュから全てのパッケージをインストール。結果
devDependenciesを削除することで、私の場合はアップロードするzipファイルのサイズを26.3 MBから5.6 MBまで抑えることに成功しました。
おわりに
削除したり再インストールしたりするのでもしかしたら余計に時間がかかるかも、と思っていましたが、意外と時間をかけることなくデプロイすることができました。このあたりはdependenciesの量で変わったりするかもしれません。
元の記事では ESLint + prettier や husky + lint-staged + jest についても触れています。Qiitaではたくさんの方が書いているようですので省略しましたが、もし興味があれば教えてください。学び始めたばかりですが、共有できることもあるかと思います。読んでいただきありがとうございました。
- 投稿日:2019-05-23T14:07:23+09:00
jsonpの基本
はじめに
jsonpって言葉は聞いたことあるけど、jsonと何が違うのか全く理解していませんでした。
jsonと同じようにデータ取得できるだろうと思っていたら、全く取得できず、詰みました。
初めて違いを理解し、実際に使ったので、その備忘録です。仕組み
jsonpとはjson with paddingの略だそうです。jsonpのurlを叩くと、下記のように返ってきます。
callback({"りんご":1,"ぶどう":2,"もも":3});jsonpのurlを叩くと、scriptが実行されます。そのcallbackとして、データが返ってきます。
jsonとの違い
・jsonはjavascriptから生まれたデータ用フォーマットで、下記のような感じのデータを保持するものです。
{ "りんご":1, "ぶどう":2, "もも":3 }つまり、jsonはデータそのものだが、jsonpはjsの関数です。
jsonとjsonpはちょっと記述が違うだけ、という認識をすると混乱します。(私がそうでした。)データ取得方法
違いはわかったけど、じゃあデータをどうやって取得するのがいいのでしょうか。
方法はいくつかあると思います。
ライブラリを使用して取得する方法が一番簡単で良いのかもしれませんが、今回はライブラリを使用しない方法を記載します。
(モジュールの一部を記載するので、もし参考にされる場合は、適宜classの中とかに入れる必要があります。)ConfirmationAPI.jslet index = 0; confirmSerialCode(url) { index++; const callbackName = `confirm${index}`; const random = Math.round(Math.random() * 10000000); return new Promise((resolve, reject) => { const $base = document.getElementsByTagName('script')[0]; const $script = document.createElement('script'); window[callbackName] = function (res) { resolve(res); document.body.removeChild($script); }; $script.id = `jsonp${index}`; $script.async = true; $script.src = `${url}?callback=${callbackName}&cache=${random}`; $script.onerror = function () { reject('Request Error'); }; $base.parentNode.insertBefore($script, $base); }); }index.jsthis.confirmationAPI .confirmSerialCode(url) .then((res) => { console.log(res); }) .catch((err) => { console.log(err); });今回は、この関数が走って、callbackから返ってきた値をconsoleに出力しています。
また、2回走っても大丈夫なように、indexの連番を付与しています。
(連番をつける理由は、2回関数が実行されてしまって、コールバックが一緒だった時にうまく動かない可能性があるためです。)最後に
これはjsonpで値をどう取得すればよいかわかっていなかった私自身の備忘録です。
もし使いやすいライブラリをご存知の方いらっしゃいましたら、教えていただきたいです。
- 投稿日:2019-05-23T12:33:53+09:00
位置情報を取得するAPIを解説してみた
完成形はこちらになります↓
See the Pen
NVXQYE by Yuji Yakena (@yuji-yakena)
on CodePen.
1.まずは簡単なhtmlを作成
index.html<!DOCTYP html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Geolocation</title> </head> <body> <h1>位置情報サンプル</h1> <button onclick="getPosition();">位置情報を取得する</button> <script src="app.js"></script> </body> </html>ポイントは2箇所あります
<button onclick="getPosition();">
ボタンをクリックすることで、jsファイルで定義したgetPosition()メソッドを呼び出して実行します。
<script src="app.js"></script>
jsファイルを読み込みます。2.jsファイルにてメソッドの定義
app.jsfunction getPosition(){ navigator.geolocation.getCurrentPosition( function(position){ let nowLatitude = position.coords.latitude; let nowLongitude = position.coords.longitude; alert("緯度:"+nowLatitude+"、経度"+nowLongitude); }, ); }
function 関数名()
任意の関数名を定義し、{ }内に実際の動作を記述します。
html側では、この関数名で呼び出します。
navigator.geolocation.getCurrentPosition()
デバイスの現在位置を取得するメソッドです。
引数として、success・error が使用されます。メソッドの構文・定義など詳細はこちら↓
https://developer.mozilla.org/ja/docs/Web/API/Geolocation/getCurrentPosition
function(position)
先程引用した位置取得メソッドの引数として認識されます。
let 変数名
任意の変数を定義して、緯度・経度プロパティを格納します。
position.coords.latitude
position.coords.longitude
それぞれ緯度・軽度プロパティとして取得できます。
alert(引数)
先程定義した変数をアラート表示します。まとめ
APIを理解する最初のステップとして、参考になれれば幸いです。。
参考元
MDN JavaScript リファレンス
https://developer.mozilla.org/ja/docs/Web/JavaScript
- 投稿日:2019-05-23T12:12:02+09:00
[Vue.js] vue.jsでアンカーリンクを作成する
やりたいこと
vue.jsでアンカーリンク(ページ内リンク)の実現
手順
[その1] vue-smooth-scrollのインストール
cd *** // プロジェクトのディレクトリに移動 npm i -D vue-smooth-scroll[その2] main.jsファイルにプラグインの使用を宣言
import vueSmoothScroll from 'vue-smooth-scroll' Vue.use(vueSmoothScroll);[その3] 挿入
<a href="#section1" v-smooth-scroll="{ duration: 1000, offset: -50 }">セクション1</a> <div id="section1"> <!-- セクション1のコンテンツ --> </div>
- 投稿日:2019-05-23T11:59:14+09:00
TensorFlow.jsでDeepLearning(学習用画像データURLの収集)
こんにちわ。Electric Blue Industries Ltd.という、ITで美を追求するファンキーでマニアックなIT企業のマッツと申します。TensorFlow.jsでDeepLearningをする際に学習データ用の画像を収集することがあると思いますが、Google画像検索を使って簡単におこなう小ネタです。
Google画像検索で取得したい画像がリスト表示されたところで、下記のjavascriptをブラウザ(Google Chrome推奨)の開発者ツールのコンソールで実行すると、表示された画像のソースURLがCSVファイルとしてダウンロードできます。
ggl_image_urls.js// 検索結果のHTMLソースから各検索結果のアイテムごとにソースURLを取得し配列化 urls = Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el=>JSON.parse(el.textContent).ou); // 改行コードを各行末尾に付けたCSVとして吐き出す window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));
追伸: Machine Learning Tokyoと言うMachine Learningの日本最大のグループに参加しています。作業系の少人数会合を中心に顔を出しています。基本的に英語でのコミュニケーションとなっていますが、能力的にも人間的にもトップレベルの素晴らしい方々が参加されておられるので、機会がありましたら参加されることをオススメします。
- 投稿日:2019-05-23T11:53:13+09:00
JsDocをもとにドキュメントを自動生成する
Documentation.js
コマンドひとつで、ドキュメントを自動生成してくれる!
まずは、JsDocで記述
JsDocの概要については、おググりください。
VSCodeとかだと、関数の上の行で/** + Enter
でJsDocテンプレートが挿入されるimport { TweenMax } from 'gsap'; /** * TweenMax の基本操作 */ export default class TweenAnimation { /** * @param {any} target * @param {number} duration * @param {gsap.TweenConfig} vars */ constructor(target, duration, vars) { this._tween = TweenMax.to(target, duration, vars); } /** * アニメーションを止める * @param {any} [atTime] * @param {boolean} [suppressEvents] */ pause(atTime, suppressEvents) { this._tween.pause(atTime, suppressEvents); } }生成されたドキュメント (HTML形式の場合) の一部
使い方
インストール
yarn add -D documentation
(ornpm i -D documentation
)HTML形式でドキュメント生成
documentation build path/to/your/javascript.js -f html
documentation build src/** -f html -o docs
MarkDown形式でドキュメント生成
documentation build path/to/your/javascript.js -f md
documentation build src/** -f md -o docs.md
オプションについて
-f
: フォーマット (html
,md
)-o
: アウトプット(出力)
- HTML形式の場合、生成するディレクトリ名を指定
index.html
やassets/
が格納される- MarkDown形式の場合、ファイル名
.md
番外編: ファイルに変更があったら、ドキュメントを更新
- さらに、HTMLでもMarkDownでも生成したい場合の例
yarn add -D documentation chokidar-cli npm-run-all
(ornpm i -D documentation chokidar-cli npm-run-all
)package.json"scripts": { "watch": "chokidar 'src/es6/**' -c 'npm run docs'", "docs": "npm-run-all -p docs:*", "docs:html": "documentation build src/es6/** -f html -o docs/es6", "docs:md": "documentation build src/es6/** -f md -o docs/es6/README.md" }実行
yarn watch
参考
- Documenting Javascript Projects With JSDoc, Flow, and Documentation.js
- 投稿日:2019-05-23T11:34:14+09:00
JavaScript: オブジェクトの分割代入とスプレッド構文を使ってみる
ECMAScript 2015の分割代入は、配列のほかオブジェクトに用いることもできます。また、ECMAScript 2018では、オブジェクトにスプレッド構文が採り入れられました。これらの構文の使い方を、簡単にご説明します。なお、配列の場合については、「JavaScript: ECMAScript 2015のスプレッド構文・残余引数・分割代入を使ってみる」をお読みください。
スプレッド構文を使う
スプレッド構文(
...
)をオブジェクトに用いると、プロパティと値の組みが展開されて取り出せます。つぎのコードは、オブジェクトを複製する例です。const rect = {type: 'rectangle', width: 50, height: 20}; const rectClone = {...rect}; console.log(rectClone); // {type: "rectangle", width: 50, height: 20}ただし、つくられるのは浅いコピーです。値がオブジェクトのときは、参照が渡されることになります1。
const rect = {type: 'rectangle', coords: {width: 50, height: 20}}; const rectClone = {...rect}; rect.coords.height = 25; console.log(rectClone); // {type: "rectangle", coords: {width: 50, height: 25}}複数のオブジェクトを展開すれば、ひとつにまとめられます。ただし、同じプロパティがあると、あとの値で上書きされることにご注意ください。
const rect = {type: 'rectangle', width: 50, height: 20}; const circle = {type: 'circle', radius: 25}; const union = {...rect, ...circle}; console.log(union); // {width: 50, height: 20, type: "circle", radius: 25}展開したオブジェクトは、入れ子にしてまとめることもできます。
const type = {type: 'rectangle'}; const coords = {x: 20, y: 10}; const size = {width: 50, height: 20}; const rect = {...type, coords: {...coords, ...size}}; console.log(rect); // {type: "rectangle", coords: {x: 20, y: 10, width: 50, height: 20}}同じかたちの構文でも、関数の残余引数は配列です。任意の数のオブジェクトをまとめようとしたとき、つぎのように定めた関数では意図した結果が得られません。配列インデックスをプロパティとして、オブジェクトにまとめられてしまうからです。
function mergedObjects(...objects) { return {...objects}; } console.log(mergedObjects(type, coords, size)); // {0: {type: "rectangle"}, 1: {x: 20, y: 10}, 2: {width: 50, height: 20}}関数として定めるなら、残余引数の配列からオブジェクトをひとつずつ取り出してまとめなければなりません。つぎのコードは、
Array.prototype.reduce()
メソッドを使った例です(「配列要素の平均を求める」参照)。function mergedObjects(...objects) { // return {...objects}; return objects.reduce( (objects, object) => ({...objects, ...object}) , {}); } console.log(mergedObjects(type, coords, size)); // {type: "rectangle", x: 20, y: 10, width: 50, height: 20}分割代入を使う
オブジェクトの分割代入は、オブジェクトからプロパティを取り出して対応する変数に代入する構文です。
let {a, b} = {a: 0, b: 1}; console.log(a, b); // 0 1対応するプロパティがない変数には、
undefined
が入ります。また、すべてのプロパティを取り出す必要はありません。let {a, b, x} = {a: 0, x: 10, y: 20}; console.log(a, b, x); // 0 undefined 10取り出したプロパティ値を、名前の異なる変数に割り当てることもできます。また、変数にデフォルト値を与えれば、対応するプロパティがない場合にその値が用いられます。
let {x, a: y, z = 0} = {a: 1, x: 10, y: 20}; console.log(x, y, z); // 10 1 0また、オブジェクトの残余プロバティ(Rest Properties)が、ECMAScript提案のステージ4の段階になりました。関数の残余引数とは異なり、オブジェクトにまとまります。
let {a, b, ...rest} = {a: 0, b: 1, x: 10, y: 20}; console.log(a, b, rest); // 0 1 {x: 10, y: 20}つぎのコードは、関数の引数にオブジェクトの分割代入を用いた例です。入れ子のオブジェクトから、プロパティを取り出すこともできます。
function getRectArea({type, coords: {width, height}}) { if (type === 'rectangle') { return width * height; } } console.log(getRectArea({ type: 'rectangle', coords: {x: 20, y: 10, width: 50, height: 20} })); // 1000ただし、入れ子の親に対応するプロパティがないとエラーになります。
console.log(getRectArea({type: 'rectangle'})); // TypeError: Right side of assignment cannot be destructuredつぎの関数は、このエラーを避けるため、引数とプロパティにデフォルト値を用いた例です。
// function getRectArea({type, coords: {width, height}}) { function getRectArea({type, coords = {}}) { if (type === 'rectangle') { const {width = 0, height = 0} = coords; return width * height; } } console.log(getRectArea({ type: 'rectangle', coords: {x: 20, y: 10, width: 50} })); // 0 console.log(getRectArea({type: 'rectangle'})); // 0つぎの関数は引数にオブジェクトの分割代入を用い、さらに残余プロパティも使ってみました。
function getArea({type, ...coords}) { switch (type) { case 'rectangle': const {width, height} = coords; return width * height; case 'circle': const {radius} = coords; return radius ** 2 * Math.PI; } } console.log(getArea({ type: 'rectangle', width: 50, height: 20 })); // 1000 console.log(getArea({ type: 'circle', radius: 10 })); // 314.1592653589793オブジェクトの種類(
type
)をもうひとつ増やしてみましょう。ここで注意したいのは、case
はブロックをつくらず、switch
文がひとつのブロックなので、その中で変数の重複が許されないことです。プロパティ名がかぶってしまうときは、変数名を変えることが考えられます。function getArea({type, ...coords}) { switch (type) { case 'rectangle': const {width, height} = coords; return width * height; case 'triangle': const {base, height: _height} = coords; // 変数名を変える return base * _height / 2; case 'circle': const {radius} = coords; return radius ** 2 * Math.PI; } } console.log(getArea({ type: 'triangle', base: 50, height: 20 })); // 500あるいは、
switch
文の外で変数宣言する手もあります。このときは、また別の注意をしなければなりません。分割代入を変数宣言で始めないときは、左辺がブロック{}
とみなされ、代入演算子=
が続けられないことです。そのため、代入文はかっこ()
でくくらなければなりません。function getArea({type, ...coords}) { let width, height, radius; switch (type) { case 'rectangle': ({width, height} = coords); return width * height; case 'triangle': ({base, height} = coords); return base * height / 2; case 'circle': ({radius} = coords); return radius ** 2 * Math.PI; } }オブジェクトを展開してまとめるスプレッド構文と、プロパティが簡単に取り出せる分割代入、少し注意すべき点はあるものの、コードがすっきり書けます。いろいろと工夫できそうです。
深いコピーをつくるには、つぎのようなやり方が考えられます。
↩const rectClone = JSON.parse(JSON.stringify(rect));
- 投稿日:2019-05-23T05:36:59+09:00
Shape Detection APIでQRコード検出を試してみました
はじめに
初めまして、k.s.ロジャースの西谷です。
直近でPCカメラからQRコードの読込を行いたい話がありました。
利用者の環境が未定であったことから、ブラウザ上で動作するQRコードリーダがあれば良い話があり、調査しておりました。
こちらの記事を紹介頂き、非常に参考になりました。しかし、Shape Detection APIが全てのブラウザで非対応は「何かあるのでは?」と考え調査と検証を行いました。
間違い・助言等があればコメントにてお知らせいただけたらと思います。
Shape Detection APIとは
顔検出、ORコード読込、テキスト認識の3つのAPIを提供します。
これらはハードウェアアクセラレーションに対応しており、既存のWebライブラリに比べて低い負荷で実行することができます。また、本APIは2019/05時点では草案で、試験的な機能となります。
本来上記3つの機能がありますが、今回は要件がQRコード読み取りであるため、QRコード周りの検証を行います。
実装
chromeの以下URLから
Experimental Web Platform features
をEnabledにすることで利用できるようになります。chrome://flags/#enable-experimental-web-platform-featuresQRコード識別の部分に関しては
detector.detect(image)
をするだけで、後は識別した後のデータを返してくれます。
検証はこちらのコードを利用して進めました。
https://qiita.com/massie_g/items/a203cf2071f96d910743動作状況
今回の一番の嵌りどころでした。
本APIはハードウェアアクセラレーションを利用するため、利用デバイスによって挙動が異なります
。私の手元のデバイスではそれぞれ次のようになりました。
OS バージョン等 検証結果 Android Nexus 5X 正常動作 iOS iPhone8 Chromeのフラグが存在せず macOS Mojave 10.14.1 動作するがQRコードを検出せず Windows windows10 1809 非対応 Androidについて
こちらは正常動作しました。
macOSについて
macOSはサポート済み判定されており、detect関数もエラーなく動作していました。
しかし、公式サンプルの固定画像でもQRコードを認識出来ませんでした。その他のデバイス
Windowsは公式で非対応との記載があり、QRコードは対象外になります。
iOSのChromeはenable-experimental-web-platform-features
がなく、非対応となっています。参考資料
https://wicg.github.io/shape-detection-api/#introduction
https://www.chromestatus.com/feature/4757990523535360おわりに
今回はWebブラウザでQRコードを認識する予定でしたが、現状のShape Detection APIはかなりデバイスに依存することが分かりました。
標準化後に依存周りが良くなれば、開発コストやパフォーマンスを改善に期待できると思います。今回は知見がほぼないため、間違いや改善案などがあれば是非教えて頂けたらと思います!
Wantedlyでもブログ投稿してます
Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers
- 投稿日:2019-05-23T05:36:59+09:00
Shape Detection APIでQRコード検出を試しました
はじめに
初めまして、k.s.ロジャースの西谷です。
直近でPCカメラからQRコードの読込を行いたい話がありました。
利用者の環境が未定であったことから、ブラウザ上で動作するQRコードリーダがあれば良い話があり、調査しておりました。
こちらの記事を紹介頂き、非常に参考になりました。しかし、Shape Detection APIが全てのブラウザで非対応は「何かあるのでは?」と考え調査と検証を行いました。
間違い・助言等があればコメントにてお知らせいただけたらと思います。
Shape Detection APIとは
顔検出、ORコード読込、テキスト認識の3つのAPIを提供します。
これらはハードウェアアクセラレーションに対応しており、既存のWebライブラリに比べて低い負荷で実行することができます。また、本APIは2019/05時点では草案で、試験的な機能となります。
本来上記3つの機能がありますが、今回は要件がQRコード読み取りであるため、QRコード周りの検証を行います。
実装
chromeの以下URLから
Experimental Web Platform features
をEnabledにすることで利用できるようになります。chrome://flags/#enable-experimental-web-platform-featuresQRコード識別の部分に関しては
detector.detect(image)
をするだけで、後は識別した後のデータを返してくれます。
検証はこちらのコードを利用して進めました。
https://qiita.com/massie_g/items/a203cf2071f96d910743動作状況
今回の一番の嵌りどころでした。
本APIはハードウェアアクセラレーションを利用するため、利用デバイスによって挙動が異なります
。私の手元のデバイスではそれぞれ次のようになりました。
OS バージョン等 検証結果 Android Nexus 5X 正常動作 iOS iPhone8 Chromeのフラグが存在せず macOS Mojave 10.14.1 動作するがQRコードを検出せず Windows windows10 1809 非対応 Androidについて
こちらは正常動作しました。
macOSについて
macOSはサポート済み判定されており、detect関数もエラーなく動作していました。
しかし、公式サンプルの固定画像でもQRコードを認識出来ませんでした。その他のデバイス
Windowsは公式で非対応との記載があり、QRコードは対象外になります。
iOSのChromeはenable-experimental-web-platform-features
がなく、非対応となっています。参考資料
https://wicg.github.io/shape-detection-api/#introduction
https://www.chromestatus.com/feature/4757990523535360おわりに
今回はWebブラウザでQRコードを認識する予定でしたが、現状のShape Detection APIはかなりデバイスに依存することが分かりました。
標準化後に依存周りが良くなれば、開発コストやパフォーマンスを改善に期待できると思います。今回は知見がほぼないため、間違いや改善案などがあれば是非教えて頂けたらと思います!
Wantedlyでもブログ投稿してます
Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers
- 投稿日:2019-05-23T02:09:41+09:00
【2分でイメージをつかむ】fsオブジェクト
なんでこれを書いたか
- Node.jsでサーバーを立てた後、やることはこれ
- ファイル読み込みをどうやってやるかのイメージをつかみたかったから
fsオブジェクトとは
- file system オブジェクト
- ファイルを扱うオブジェクトでHTMLファイルなど読み込める!
使い方
男は黙って、変数に読み込む
var fs = require('fs');まずはこれだけ使えるようになればOK
fs.readFile( './hello.html', 'UTF-8', コールバック関数 );
- 第1引数にファイルパスを指定(例えば・・'./hello.html')
- 第2引数には'UTF-8'を指定(一旦はこういうもんだと思っておけばOK)
- 第3引数には、読み込み完了後の処理を行うコールバック関数をかく
コールバック関数
- コールバック関数は、このファイルの読み込みが完了したら、発動します!
- 仮にこのファイルが、読み込みに1時間を要する超膨大ファイルだったとしましょう
- 1時間後、コールバック関数が発動し、そのあとに書いてるソースコードはようやく読まれるようになります
例えるなら、お会計をしている人が財布を探していて、なかなか列が進まない・・
しかし
- そんなことしてたら、ずーっと待ちっぱなしで列に並ぶ気が失せる
- というか財布探すのに1時間もかけて、店員もずっと待ってるって・・・店としてどうなんだ!
というご意見がありましたので、、
お財布探している間、次の方どうぞ〜
- に変更しました
- お財布が見つかり次第、お会計(コールバック関数)に移らせていただきます。
- 探している間は、次のお客様(readFile以降の処理)からお会計させていただきます。
簡単なコード例
function doRequest(req, res) { //hello.htmlを読み込もう! fs.readFile('./hello.html', 'UTF-8', // 読み込みが完了したら、コールバック関数が発動 function(err, data) { res.writeHead(200, {'Content-Type': 'text/html'}); // 読み込んだファイル(data)を書き込む res.write(data); res.end(); }); console.log('readFileのファイル読み込みが完了するまで、お待ちください'); console.log('おや、このログを見ているということは、まだファイルの読み込みが完了していないようですね'); }
- hello.htmlのファイル読み込みがおそーーかった場合、コールバック関数は発動せずに、readFile関数の下にある、console.logが呼び出される
- ファイル読み込みが完了した時点で、コールバック関数が発動する
こっから脱線
- コールバック関数は、「非同期処理」になります
- つまり、割り込みができるということです!
- 財布見つかった時点で、最後尾に列びなおさなくても、お会計に割り込めるというイメージです
じゃあ、それって並列処理なんですか?
- 違います
- 並列処理は、複数の作業を同時に実施します。
- レジいっぱいあるから財布探している人が割り込んでくる心配はない。同時にお会計ができるということです
参考