20210428のJavaScriptに関する記事は24件です。

Gmailで既読のメールを自動アーカイブする

メールボックスを重要なメールややり取りの最中のものだけ残してきれいにしておくと、気が散らなくていいということに気づき、実践しています。ところが、パソコン版は一括選択できて簡単にまとめてアーカイブできるものの、スマホ版だといちいち一個ずつ選択しなければアーカイブできず、それが面倒でした。 これを改善するため、「既読かつ☆マークがついていないメールをアーカイブする」スクリプトを組みました。 コピペすれば動くやつ const main = () => { const inboxThreads = GmailApp.getInboxThreads() const shouldArchivedThreads = inboxThreads.filter(thread => !thread.isUnread() & !thread.hasStarredMessages()) archiveThreads(shouldArchivedThreads) } const archiveThreads = (threads) => { threads.forEach(thread => thread.moveToArchive()) } 使い方 コピペする 保存する タイマーのマークからトリガーを設定する。僕は一日一回に設定しましたが、毎時または毎分でも問題なく動作します。ただし、スターマークをつける前にアーカイブされるとこまるので、あまり頻度は上げないほうがいいと思います。 ちょっと解説 getInboxThreads()を使うことで、受信箱の全てのスレッド(メールに対する返信などをまとめたもの)を取得しています。次の行でそれぞれのthreadsの中に未読のものがないか(isUnread())と☆がついているものがないか(hasStarred())確認します。 ちなみに、jsやgasの初心者の人は下記のコードの方がわかりやすいと思います。 const main = () => { const inboxThreads = GmailApp.getInboxThreads() inboxThreads.forEach(threads => { if (!thread.isUnread() & !thread.hasStarredMessages()) { thread.moveToArchive() } }) } やっていることは同じです。僕の場合inboxから全て取得ではなくいろいろなボックスやくくりから取得して、それぞれをアーカイブしたり削除したりを分けることも考えていたため、機能を分離するという目的のためにarchiveThread()関数に書き出していましたが、inboxから取得してアーカイブするだけならこのコードの方がいいと思います(繰り返しますが同じことができます)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カウントボタンの書き方

Vue.jsでクリックすると数が増えていくカウントボタンの書き方をメモとして残しておこうと思います。 App.vue <template> <div id="app"> <button v-on:click="number += 1">ボタン</button> <h1>{{ number }}回</h1> </div> </template> <script> export default { data: function() { return { number: 0 , } }, }; </script> 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JS観察メモ

初投稿です。 (役に立た)ないです。 いい加減にJavaScriptを手に馴染ませようと思い。 コメントアウト解くとエラー吐きます。 "use strict"; {} ; // () ; ({}).toString (); ({} .toString)(); // {} .toString () ※nodeに直打ちした場合エラーになりません。 // {} .toString (); console . log (".") ; (console . log)(".") ; // console .(log ( )); // console .(log) ; // console(.)log ; // (console .)log ; // console(. log) ; { let x = console.log; x("."); } console["log"]("."); globalThis.console.log("."); { let x = globalThis; (x.console)["log"]("."); x = x["console"]; x.log("."); x = x["log"]; x("."); } (x) => x ; (x) => ( x ); (x) => {return x;}; // ((x))=> x ; { let x = [0,1,2,3]; let f = (x)=>console.log(x); x.map(console.log); // エラーではないが意図しない動作 x.map (f); (x.map)(f); ((x)[("map")])((f)); Array.prototype.map.call(x,f); // Array.map(x,f); x = x.map; x.call([0],f); // x(f); // x([0],f); } 検証環境:Node.js v16.0.0 / Google Chrome 90 DevTools 参考文献:JavaScript リファレンス - JavaScript | MDN くぅ~疲れました1w これにて完結です!2 コンソールにxと打ち込むことがglobalThis["x"]と等価であること、つまりコンソール(=globalThis)に"x"を渡すことをそのまま表現できていることに気づいたときは感動しました。 本当の本当に終わり2 ブロック文とオブジェクト初期化子、グループ化演算子と関数呼び出し子、区切り子とカンマ演算子等々混乱の素。オブジェクトが連想配列であること、関数も名前空間もモジュールもオブジェクトであること、プロトタイプチェイン、プロパティアクセサによる動的な処理等の特徴がC系統の文法に覆い隠され把握しづらい。パーサの挙動が若干非直感的かつ各種オブジェクトの実装が洗練されてない(おそらく後方互換性のため。)設計思想を理解すればそれなりに付き合える言語という印象。まあ設計思想と構文が正面衝突してるんですがね……。デフォルト引数が存在したり。付属のデバッガがなかったら耐えられなかった。 ↩ え?死ね ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クリックで表示画面が切り替わるメモアプリをjQueryのindexメソッドで作る

localstrageを使ったメモアプリを作成している途中で、indexメソッドについての学びがあったのでこちらに書き残したいと思います。 作った機能 一覧になったメモをクリックすると右側に大きく表示されるという機能。 ※jQueryのバーションは3.6.0です。 コード 前提 実際はlocalstrageを用いてデータの取得、表示を行うためこのようにメモ一覧がベタ打ちされることはありません。今回は、クリックしたメモを別枠に表示させる機能をメインに記載したいのでこのようなコードとなっておりますことご理解いただければ幸いです。 index.html <!-- メモ一覧 --> <div class="memo"> <div class="memo-all"> <p class="title">桃太郎</p> <p class="text">鬼退治に行く</p> </div> <div class="memo-all"> <p class="title">さる</p> <p class="text">鬼退治についていく①</p> </div> <div class="memo-all"> <p class="title">いぬ</p> <p class="text">鬼退治についていく②</p> </div> <div class="memo-all"> <p class="title">きじ</p> <p class="text">鬼退治についていく③</p> </div> </div> main.js // クリックしたメモのタイトルを右側に表示 $(".memo-all").on("click", function () { let index = $(".memo-all").index(this); let title = $(".title").eq(index).html(); $(".textarea-title").html(title); console.log(index, title); }); // クリックしたメモの本文を右側に表示 $(".memo-all").on("click", function () { let index = $(".memo-all").index(this); let text = $(".text").eq(index).html(); $(".textarea-text").html(text); console.log(index, text); }); 解説 main.js // クリックしたメモのタイトルを右側に表示 $(".memo-all").on("click", function () { let index = $(".memo-all").index(this); let title = $(".title").eq(index).html(); $(".textarea-title").html(title); }); まずメモのタイトルを表示させます。 最初、memo-allに対してon("click)を付与します。 main.js $(".memo-all").on("click", function () { }); しかしこのままではmemo-allのclassを持つ全てに適用されるため、indexメソッドを使います。indexメソッドを記述することで同じclass名でも何番目の要素かを指定することができます。 main.js let index = $(".memo-all").index(this); let title = $(".title").eq(index).html(); indexについて参考にさせていただきた記事 jQueryで特定の要素が何番目にあるのかを取得する:index() main.js $(".textarea-title").html(title); 最後に表示させたいエリアのclassを指定してhtmlメソッドで取得した要素を挿入してあげれば完了です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ZONe 'mad_hacker'; にデザインされたプログラムを実行してみた

はじめに 先日、みんな大好きエナジードリンクのZONeより、'mad_hacker'; Ver1.0.0が発売されました。 何というネーミングでしょうか。マッドハッカー... 早速買ってみたところ、何やら缶にプログラムがデザインされていました。 かっこいい!!これは実行してみたい...! コード入力 何はともあれ、まずは写経です。 だまってコードを打ち込みます。 缶という円柱に描かれただけでも読みにくいのに、改行もない、他のデザインにコードが邪魔されてる、文字色も見えにくいですが、とにかくカッコいいので頑張って読み取ります。 全部で缶に30行ほどのコードがありますが、4行半ばまででひとつの関数が終わってるぽいので、今回はそこまで。 何やらJavascript という言語らしいです。 経験がない言語ですが、何となく改行しながら書きます。 mad_hacker.js function binarySearch(arr, val, min, max){ if (max < min) { return false; } const half = Math.floor(min + (max - min) / 2); if(arr[half] === val){ return half; } return arr[half] > val ? binarySearch(arr, val, min, half - 1) : binarySearch(arr, val, half + 1, max); } binarySearch(二分探索) 関数名から察するに、これは二分探索アルゴリズムです。 ソート済の配列やリストの中から、特定の値を見つけるアルゴリズムですね。 Wikipedia様の例を参考に、値を入れてみます。 下記のような配列から25を探しだすことを考える。 位置 1 2 3 4 5 6 7 8 9 10 データ 1 3 5 11 12 13 17 22 25 28 main.js // 配列作成 search_list = [1,3,5,11,12,13,17,22,25,28] // 関数実行 ans = binarySearch(search_list, 25, 0, search_list.length); // 結果確認 console.log(ans); 第一引数に探索対象配列のsearch_listを、第二引数に探索対象である25を渡します。 第三、第四引数のminとmaxは探索対象の数値ではなく、配列の要素数を渡してあげるようです。 minには0、maxにはsearch_list.lengthで取得した配列の要素数(=10)を渡します。 実行結果 Javascript は未経験で環境もないため、今回はpaiza.ioでJavascript を実行しました。 実行結果 8 ここでもう一度例題を見てみましょう。 下記のような配列から25を探しだすことを考える。 位置 1 2 3 4 5 6 7 8 9 10 データ 1 3 5 11 12 13 17 22 25 28 この25という数字が配列のどの位置にあるか探索するのが二分探索でした。 位置としては9番目ですが、配列は0始まりなので、8で正解ですね! まとめ グレープソーダのようで美味しかったです。 ZONe 'mad_hacker'; にはソートされた配列から任意の値を取り出す二分探索がコーディングされていました。 ハッカー関係あるのでしょうか? しかし何にせよ、Javascript 未経験だったので良い勉強になりました。 残りの30行もどうやら探索アルゴリズム系っぽいです。気になりますね。 AtCoderと連携しているみたいです。 続きは気が向いたら。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ひらがな⇔カタカナ】あいまい検索にヒットしたテキストを一括置換する方法【大文字⇔小文字】

あらすじ ひらがな・カタカナの曖昧検索をした上で、ヒットしたテキストの置換をする処理をつくる必要がありました。 参考記事をググると、ドンピシャな記事はなかなか出てきませんでしたが、 「正規表現であいまい検索をする方法」みたいな記事はいくつかでてきました。 内容としては、 「検索対象と検索文字列をそれぞれひらがな・カタカナに変換して、正規チェックする」という方法でした。 素直さがウリであるわたし(?)は言われたとおりにコードを書いてみたのですが、 ヒットしたテキストを置換しようとしたとき、 ヒットしたテキスト自身を含んだ状態で置換するには、結構複雑な処理が必要でした。 JavaScriptには、正規マッチした全ての文字列のインデックスを取得する関数だったり、 正規マッチした全ての文字列を置換するような関数が存在しないので、 1つ1つヒットしたテキストを置換しつつ、 インデックスがズレないように調整する必要があったのです。 絶望に暮れ、悠久とも思える10分もの長い期間悩んでいたわたしに、発想の女神の祝福が舞い降りました。 検索対象を置換するんじゃなくて、そもそも正規表現をひらがなでもカタカナでもヒットする形にすれば、とってもシンプルになる! そのことに気づいたわたしは、早速コードを書きはじめました・・・。 コード function katakanaToHiragana(src) { return src.replace(/[\u30a1-\u30f6]/g, function(match) { const chr = match.charCodeAt(0) - 0x60; return String.fromCharCode(chr); }); } function hiraganaToKatagana(src) { return src.replace(/[\u3041-\u3096]/g, function(match) { const chr = match.charCodeAt(0) + 0x60; return String.fromCharCode(chr); }); } function escapeRegExp(string) { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); } function generateFuzzyRegExp(searchWord) { searchWord = escapeRegExp(searchWord); const chars = searchWord.split('').map(char=>{ const hiragana = this.katakanaToHiragana( char ); const katakana = this.hiraganaToKatagana( char ); if( hiragana===katakana ) return char; return `(${hiragana}|${katakana})`; }); const fuzzyRegExp = new RegExp( `(${chars.join('')})`, 'ig' ); return fuzzyRegExp; } 説明 katakanaToHiragana hiraganaToKatakana カタカナをひらがなに、ひらがなをカタカナに変換する関数です。 こちらのリポジトリを参考にさせていただきました。 escapeRegexp 正規表現と認識されてしまう文字をエスケープする関数です。 こちらの記事を参考にさせていただきました。 generateFuzzyRegExp 引数に渡された文字列から、 カタカナ・ひらがな・大文字・小文字を区別しない正規表現を生成する関数です。 エスケープ後の文字列を1文字ずつ分割し、 (あ|ア)のようなパターンに変換して配列に格納しています。 joinで1つの文字列にまとめたら、 まとめて $1 で拾えるようにカッコで囲ってあげて、 正規表現に変換してあげれば完成です! (正規表現のiオプションによって、英大文字・英小文字は自動的に区別しない仕様となります。) 感想 技術レベル的にはたいしたことないですが、 発想の転換って大事だな〜と思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チェックボックスで選択したものをサブテーブルにセットする

kintoneでルックアップ。 1つルックアップするのはそんなに問題ないですが、 サブテーブル内などでたくさんルックアップしないといけないとき面倒だと思いませんか? ↓面倒かもしれないルックアップの作業 今回はそんなときに使えるかも知れないカスタマイズです。 アプリの準備 マスタアプリとカスタマイズするアプリの2種類作りましょう。 マスタアプリ 文字列(1行)フィールドと数値フィールドを設置しました。 重複禁止設定は不要です。 フィールド種類 フィールドコード 文字列(1行) だんご 数値 確率 カスタマイズするアプリ フィールド種類 フィールド名 フィールドコード スペース -- spDango テーブル テーブル テーブル 文字列(複数行) 結果 結果 テーブル内フィールド フィールド種類 フィールド名 フィールドコード 文字列(1行) 注文しただんご だんご 数値 確率 確率 JavaScript まずはおなじみ、追加・編集画面表示イベント。 処理を書くところ~にコードを追記していきます。 非同期処理するのでasyncをお忘れなく // 追加/編集画面表示 kintone.events.on( ["app.record.create.show", "app.record.edit.show"], async (event) => { // 処理を書くところ~ return event; } ); マスタアプリからレコードを取得する まずはだんごマスタからだんごの一覧をゲットしましょう。 一括取得には100件縛りはありますがひとまず100件超えしないのでそのまま取得してきます。 // だんご一覧を取得 const resp = await kintone.api( kintone.api.url("/k/v1/records", true), "GET", { app: マスタアプリのID } ); スペースフィールドに取ってきたレコードの情報を表示する 次に、スペースフィールド内にマスタから取ってきただんごの情報を並べます。 document.createElement()でhtmlを書いている感じです。 もっとシンプルに書ける方法ないかな・・・。 // スペースフィールド内にだんご一覧を作る const spDango = kintone.app.record.getSpaceElement("spDango"); const dangoDiv = document.createElement("div"); // ボタン作成 const btn = document.createElement("button"); btn.textContent = "テーブルにセット"; dangoDiv.appendChild(btn); // だんご一覧(チェックボックス付き)を作成 const dangoTable = document.createElement("table"); const dangoTBody = document.createElement("tbody"); // tbodyには、ボタンクリック時に必要になるのでidをつけておく dangoTable.id = "dangoTBody"; // だんごの数分行<tr>を作る resp.records.forEach((record) => { const fields = ["だんご", "確率"]; // マスタのフィールドコード const tr = document.createElement("tr"); tr.id = record.レコード番号.value; const chktd = document.createElement("td"); const chk = document.createElement("input"); chk.type = "checkbox"; chktd.appendChild(chk); tr.appendChild(chktd); fields.forEach((f) => { const td = document.createElement("td"); const txt = document.createTextNode(record[f].value); td.appendChild(txt); tr.appendChild(td); }); dangoTBody.appendChild(tr); }); dangoTable.appendChild(dangoTBody); dangoDiv.appendChild(dangoTable); spDango.appendChild(dangoDiv); ボタンクリックイベント 次に、作成したボタンをクリックしたときの動きを作ります。 チェックした行のだんごをサブテーブルに追加します。 // ボタンクリック btn.addEventListener("click", (event) => { // レコードのフィールド値を取得 const tbr = kintone.app.record.get(); // だんご一覧のtbodyのオブジェクトをid指定で取得する const dtb = document.querySelector("#dangoTBody"); // チェックしただんごを重複してサブテーブルに追加できないようにするにはココでクリアしておくとOK tbr.record.テーブル.value = []; // サブテーブルに追加する行 const tmpRows = []; dtb.rows.forEach((r, idx) => { if (r.cells[0].firstElementChild.checked) {// チェックのあるだんごを追加する // サブテーブルに追加する行を作成する const tmpRow = { id: null, value: { だんご: { type: "SINGLE_LINE_TEXT", value: resp.records[idx].だんご.value, }, 確率: { type: "NUMBER", value: resp.records[idx].確率.value, }, }, }; tmpRows.push(tmpRow); } }); // フィールド値を更新 tbr.record.テーブル.value = tmpRows; // サブテーブルに反映 kintone.app.record.set(tbr); }); おまけ要素 記事の主旨からは離れますが、乱数が確率より大きかったら発動!みたいな小ネタも実装してみました。 // 保存 kintone.events.on( ["app.record.create.submit", "app.record.edit.submit"], async (event) => { if (event.record.テーブル.value.length > 0) { let memo = "?発動しただんごパワー?"; event.record.テーブル.value.forEach((r) => { // 乱数が確率より大きかったら発動 if (Number(r.value.確率.value) > Math.random() * 100) { memo = memo + "\n" + r.value.だんご.value + "パワー"; } }); event.record.結果.value = memo; } else { event.record.結果.value = ""; } return event; } ); 動作確認 & まとめ このように一覧からチェックボックスで選択したものをサブテーブルに追加できます。 ルックアップの代わりにすることもできるかも!? 何かの参考になったらいいなぁ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

深い階層構成のJavaScriptオブジェクトを設定する時に便利なset-valueメソッド

Immutable.js型だったら、imtRecord.setIn(['a', 'b', 'c'], 'd');のようにシンプル書けるが、純粋なJavaScriptオブジェクトなら、当然できない。 自分で頑張って描くなら、次のような感じだと思う。 obj.aがあったら、その中にbを設定する obj.bがなかったら、まずobj.bに空オブジェクトを設置してから、bを中に設定する c以降も同様のロジックで繰り返す 便利そうなライブラリない? 何処でもいつでもこんなロジックが発生するから、ないとおかしい。 set-valueというライブライはgithubにある。 set-valueの使い方 npm install set-value // hoge.js import set from 'set-value'; const obj = {}; set(obj, 'a.b.c', 'd'); console.log(obj); // output: { a: { b: { c: 'd' } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

`String.prototype.split`で区切り文字を複数使いたい場合の書き方

以前書いた内容の抜粋だが、本題と離れた話かつ、どう考えても検索に引っかからないので仕切り直し。さらに動作原理について可能な限り詳しく書く。 The quick brown fox jumps over the lazy dog.という文字列をfoxもしくはdogのどちらかでsplitさせて、なおかつ両方ともsplitの内容として残したい場合、正規表現はこう書く。 split.js "The quick brown fox jumps over the lazy dog.".split(/(fox|dog)/) しかしこれでは、foxやdogの代わりに長い正規表現になった場合区切りが見つかりづらくなる。明示的に2つの正規表現が区切れていることを示すにはどうすればよいのだろうか。 車輪の再発明 まず考えたこととしては、foxとdogを正規表現で並立させることである。正規表現で区切り文字を残すには表現を括弧で囲めばいい。これを素直に書けば以下の様になる。 (fox)|(dog) しかし、これで検索すると以下の様な結果が返ってくる。 (fox)|(dog)でsplitした結果 Array ["The quick brown ", "fox", undefined, " jumps over the lazy ", undefined, "dog", "."] なに、Undefindedだと? と筆者は最初たじろいでしまった。しかし種が分かればそう慌てるものでもない。 要はsplitでは両方の要素を検索するわけだ。最初のfoxが引っかかった時についでにdogも調べられ、見つからないからundefinedとなってしまうのだ。これはdogとマッチした際にも発生する。 では、((fox)|(dog))とした場合はどうなるのか。まず結果を想像してほしい。きっと実際のものとは違うものになるはずだ。 ((fox)|(dog))でsplitした結果 ((fox)|(dog))でsplitした結果 Array ["The quick brown ", "fox", "fox", undefined, " jumps over the lazy ", "dog", undefined, "dog", "."] なんと大きな括弧のマッチング結果→個別の括弧のマッチング結果という分け方になってしまった! あなたの考えと合っていましたか? 大当たり? そいつは良かった。 非キャプチャリング括弧 「やっぱ正規表現は難しいわ…」と、思わず弱音を吐きそうになるが、実は正規表現には面白い機能がある。それが「非キャプチャリング括弧」だ。 この機能は、括弧内に?:と書くことで発動し、括弧の内容に「マッチしますが、マッチした内容は記憶しません」。今回の場合どんな意味があるかというと、具体例を見ていただこう。 (?:fox)|(?:dog)でsplitした結果 Array ["The quick brown ", " jumps over the lazy ", "."] これは、区切り文字をfox|dogと指定した場合と同じだ。ということは、最初に示した例と同様の書き方をしてみると… ((?:fox)|(?:dog))でsplitした結果 Array ["The quick brown ", "fox", " jumps over the lazy ", "dog", "."] つまり、最初の目的を達成する書き方は((?:fox)|(?:dog))である。無駄に遠回りをしているが、これでsplitの区切りが分かりやすくなった! …はずだ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ノードをbodyタグに挿入するなら、bodyタグの中に入れる

はじめに ノードの作成、挿入を勉強しました。 今までhtmlファイルのheadタグでjsファイルを読み込んでいましたが、 挿入箇所、例えば document.body.appendChild(container); とbodyタグの中に挿入するときは、bodyタグの開始以降に記述しないと Cannot read property 'appendChild' of null 挿入先がないと怒られます。 さいごに jsファイルの読み込み場所の正解がわかりません。body終了タグの直前が丸いのでしょうか。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ノードをbodyタグに挿入するなら、jsファイルの読み込みはbody開始タグより下で

はじめに ノードの作成、挿入を勉強しました。appendChildって難しい 以下ありがたい参考ページ 【DOM基礎】要素内容の取得・設定/ノードの作成・挿入・削除 appendChildがnull 今までhtmlファイルのheadタグでjsファイルを読み込んでいましたが、 挿入箇所、例えば document.body.appendChild(container); とbodyタグの中に挿入するときは、bodyタグの開始以降に記述しないと Cannot read property 'appendChild' of null 挿入先がないと怒られます。 appendChildがundefined 挿入先の指定が document.body.appendChild(container); または id名.appendChild(container); でしかできませんでした。 クラス名やタグ名を指定するのは無理そう? さいごに jsファイルの読み込み場所の正解がわかりません。body終了タグの直前が丸いのでしょうか。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptの基礎を改めて Part1

経緯 Reactを学習していてJavaScript(以下JS)の基本構文の理解が足りていないと感じので  こちらのサイトを元に学習し直した。 自分が重要だと思った、知らなかった箇所に焦点を当ててまとめる。 まとめ JavaScriptはデータの型を明示しなくても暗黙的にデータの型を変換してくれる。(暗黙的な型変換) 例1 1 == "1"; >> true // "1"は文字列(string)になっているが、==だと判定を行う際に暗黙的な型変換を行う 例1のような暗黙的な型変換を回避するには上記の例なら厳密等価演算子(===)を使うことで、判定を行う際に暗黙的な型変換を回避することができる。 1 === "1"; >> false オブジェクトのプロパティにアクセスする際、プロパティ名は文字列型に変換される(暗黙的な型変換) また、オブジェクト内にオブジェクトをプロパティ名として定義している場合、 そのプロパティは文字列"[object Object]"になってしまう。 例2 const obj = {}; const key1 = { object: 1 }; obj[key1] = "1"; console.log(obj); >> { "[object Object]": "1" } // プロパティ名に定義しているobj[key1]が"[object Object]"になっている。 オブジェクト内のプロパティ名と値が一緒の場合、値を省略することができる。 ※ES2015から有効な記法 const name1 = "名前"; const name2 = "なまえ"; const obj = { name1, name2: name2 }; console.log(obj); >> {"name1":"名前","name2":"なまえ"} ArrayやFunctionでもtoStringなどのプロトタイプメソッドが使えるのはObjectを継承しているため。 ※プロトタイプメソッドと同名のオブジェクトを定義した場合はプロトタイプメソッドに定義したオブジェクトが上書きされる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DynamoDBクライアントでインスタンスが同じならセッション再利用されるのか実験してみた

AWS SDK for JavaScriptを使ってDynamoDBを利用する場合、AWS.DynamoDBインスタンスを生成し、これを通してAPIアクセスを行うことになりますが、その際のセッションがどうなっているのか気になって調べてみました。 お急ぎの方に結論 オプションもしくは環境変数を設定すればセッションは再利用されます。 参考 準備 DynamoDB localを起動し、これにアクセスするコードを用意します。 (DynamoDB localがポート18005で動いています) TCPセッションの状態はlsof -i@localhostで確認します。 実験1 以下のようなコードを用意しました。 DynamoDBインスタンスは1つ作成し、APIを2回実行します。 import { DynamoDB } from 'aws-sdk'; import { execSync } from 'child_process'; process.env.AWS_REGION = 'ap-northeast-1'; function getDynamoDB() { return new DynamoDB({ endpoint: 'http://localhost:18005', }); } function printSession() { try { const stdout = execSync('lsof -i@localhost | grep node'); console.log(stdout.toString()); } catch (e) { console.error('null'); } } async function main() { console.log('#0'); printSession(); const cl = getDynamoDB(); console.log('#1'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#2'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#3'); printSession(); } main(); 以下が出力されました。 #0 null #1 null #2 node 98657 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56434->localhost:18005 (CLOSE_WAIT) #3 node 98657 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56435->localhost:18005 (CLOSE_WAIT) インスタンス作っても接続はされないんですね。 クライアント側のポートが異なるものが・・・ということは、それぞれでセッションが作られていそうです。 実験2 公式がズバリな記事を用意していました。 Reusing Connections with Keep-Alive in Node.js keepAliveを設定してみます。 import { DynamoDB } from 'aws-sdk'; import { execSync } from 'child_process'; import * as http from 'http'; process.env.AWS_REGION = 'ap-northeast-1'; function getDynamoDB() { return new DynamoDB({ endpoint: 'http://localhost:18005', httpOptions: { agent: new http.Agent({ keepAlive: true, }), }, }); } function printSession() { try { const stdout = execSync('lsof -i@localhost | grep node'); console.log(stdout.toString()); } catch (e) { console.error('null'); } } async function main() { console.log('#0'); printSession(); const cl = getDynamoDB(); console.log('#1'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#2'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#3'); printSession(); } main(); 実行すると以下が出力されました。 #0 null #1 null #2 node 99003 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56503->localhost:18005 (ESTABLISHED) #3 node 99003 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56503->localhost:18005 (ESTABLISHED) ちゃんと再利用されていそうです。 実験3 ちなみに、別インスタンスを作ると?と思って念のための追試です。 import { DynamoDB } from 'aws-sdk'; import { execSync } from 'child_process'; import * as http from 'http'; process.env.AWS_REGION = 'ap-northeast-1'; function getDynamoDB() { return new DynamoDB({ endpoint: 'http://localhost:18005', httpOptions: { agent: new http.Agent({ keepAlive: true, }), }, }); } function printSession() { try { const stdout = execSync('lsof -i@localhost | grep node'); console.log(stdout.toString()); } catch (e) { console.error('null'); } } async function main() { console.log('#0'); printSession(); const cl = getDynamoDB(); console.log('#1'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#2'); printSession(); await cl.describeTable({ TableName: 'test-table' }).promise(); console.log('#3'); printSession(); const cl2 = getDynamoDB(); console.log('#4'); printSession(); await cl2.describeTable({ TableName: 'test-table' }).promise(); console.log('#5'); printSession(); } main(); 以下が出力されました。 #0 null #1 null #2 node 99143 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56510->localhost:18005 (ESTABLISHED) #3 node 99143 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56510->localhost:18005 (ESTABLISHED) #4 node 99143 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56510->localhost:18005 (ESTABLISHED) #5 node 99143 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56510->localhost:18005 (ESTABLISHED) node 99143 user 35u IPv4 0x108da27b1d53185d 0t0 TCP localhost:56511->localhost:18005 (ESTABLISHED) インスタンスまたいで共有はされませんね。 結果 短い時間に複数回の操作を呼び出すユースケースがある場合は、keepAlive設定するか、公式に記載のAWS_NODEJS_CONNECTION_REUSE_ENABLEDを利用するのがよさそうです。 AWS_NODEJS_CONNECTION_REUSE_ENABLEDを設定すると別インスタンスでもセッションが共有されます。 keepAliveを使う場合、この実験ではローカル接続でhttpでしたが、AWS環境で有効にするにはhttps.Agentにする必要があります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScrip基礎メモその6

pushメソッド pushメソッドは配列の最後に新しい要素を追加するメソッドである。 const numbers = [1, 2, 3]; console.log(numbers); number.push(4); console.log(numbers); 結果 [1, 2, 3] [1, 2, 3, 4] 4が新しくついかされた。 forEach forEachメソッドは配列の中の要素を1つずつ取り出して、全ての要素に繰り返し同じ処理を行うメソッドである。 const numbers = [1, 2, 3]; numbers.forEach((number) => { console.log(number); }); // (number) => {console.log(number);}はコールバック関数という 結果 1 ※引数numberに1が代入され、関数の処理が実行 2 ※引数numberに2が代入され、関数の処理が実行 3 ※引数numberに3が代入され、関数の処理が実行 forEachメソッドの引数には、アロー関数が入っている。 配列内の要素が1つずつ順番にアロー関数の引数に代入され、処理が繰り返し実行される。 配列 = [要素1, 要素2, 要素3]; 配列.forEach((引数) => {処理}); // アロー関数 配列の中の要素を1つずつ取り出して同じ処理をする。 引数に入っている関数はコールバック関数と呼ぶ。 findメソッド findメソッドは条件式に合う1つ目の要素を配列の中から取り出すメソッドである。 const numbers = [1, 3, 5, 7]; const foundNumber = numbers.find((number) => { return number > 3; }); console.log(foundNumber); 結果 5 条件に合う最初の要素が取り出される。 1つしか取り出さない。 const characters = [ {id: 1, name: "kei"}, {id: 2, name: "gen"} ]; const foundCharacter = characters.find((character) => { return character.id === 1; }); console.log(foundCharacter); 結果 {id: 1, name: "kei"} filterメソッド filterメソッドは条件に合う要素のみを取り出して新しい配列を作成するメソッドである。 const numbers = [1, 3, 5, 7]; const filteredNumbers = numbers.filter((number) => { return number > 3; }); console.log(filterdNumbers); 結果 [5, 7] mapメソッド mapメソッドは配列内の全ての要素に処理を行い、その戻り値から新しい配列を作成するメソッドである。 const numbers = [1, 2, 3]; const doubledNumbers = numbers.map((number) => { return number * 2; }); console.log(doubledNumbers); 結果 [2, 4, 6] 配列numbersの全ての数値が2倍された
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FizzBuzzゲーム

概要 イベント発生時は計算ボタンをクリック。inputタグからユーザーが入力した値を受け取り、FizzuBuzzの結果を返すようプログラムしていきます。 HTML index.html <input type="number" id="number"> <button id="fire">計算</button> JS処理① index.js   function fizzBuzz(value) { if (value % 3 === 0 && value % 5 === 0) { return 'fizzBuzz'; } else if (value % 3 === 0) { return 'fizz!'; } else if (value % 5 === 0) { return 'Buzz!'; } else { return value; } } FizzBuzzの処理関数は上記になります。 仮引数をvalueとします。 ①3の倍数かつ5の倍数の数字のときはfizzBuzz ②3の倍数のときには、fizz ③5の倍数のときには、Buzzを ④それ以外の数字のときには入力値を返します。 JS処理② index.js document.getElementById('fire').onclick = () => { const num = document.getElementById('number'); console.log(num.value); alert(num.value + 'は' + fizzBuzz(num.value)); } 今回は、ユーザーがfire(計算)をクリックしたときにイベントが始まります。 idと紐付け、クリックイベント。 定数numにユーザーが入力した値を代入します。 結果をアラートで返します。num.valueで入力値を取得します。 関数名FizzBuzzを呼び出し引数としてユーザーからの入力値を引数として渡します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

electronでpython ファイルを使用する際の覚書

概要 electronはHTML, CSS, Javascriptを使って簡単にWindows, mac, linux のマルチプラットフォームにアプリを作成できるフレームワークです。 今回electronとpythonコードを連携させる際のファイル構造に関しての覚書です。 electronにおけるFrontend,Backend間の情報のやり取りの変遷 https://qiita.com/pochman/items/64b34e9827866664d436 こちらの記事に詳しく書かれていますが、electronはこれまでversionが上がるたびにSecurityの観点からipc通信の仕様を変更しています。これから先も変更になる可能性はありますが、2021年4月の時点の情報です。 全体の構成図 main.js設定 const electron = require('electron'); const app = electron.app; const path = require("path"); const BrowserWindow = electron.BrowserWindow; const ipcMain = electron.ipcMain; const ipcRenderer = electron.ipcRenderer; //ここでpyhonshell library を呼び出す const {PythonShell} = require("python-shell"); let mainWindow; function createWindow() { // メインウィンドウを作成します mainWindow = new BrowserWindow({ width:1100, height: 1000, webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js') }, }); // メインウィンドウに表示するURLを指定します // (今回はmain.jsと同じディレクトリのindex.html) mainWindow.loadFile('index.html'); // デベロッパーツールの起動 mainWindow.webContents.openDevTools(); // メインウィンドウが閉じられたときの処理 mainWindow.on('closed', () => { mainWindow = null; }); } // 初期化が完了した時の処理 app.on('ready', createWindow); // 全てのウィンドウが閉じたときの処理 app.on('window-all-closed', () => { // macOSのとき以外はアプリケーションを終了させます if (process.platform !== 'darwin') { app.quit(); } }); // アプリケーションがアクティブになった時の処理(Macだと、Dockがクリックされた時) app.on('activate', () => { // メインウィンドウが消えている場合は再度メインウィンドウを作成する if (mainWindow === null) { createWindow(); } }); //ここでpreload.jsからのipc通信を受け取り、python-shellを起動 //pythonで書かれたコードを呼び出す ipcMain.handle('hoge',async (event, data) => {  //pythonに渡す何らかのパラメーター var options = { data:data } //ここでパラメーターと共に渡す let pyshell =await new PythonShell('hogehoge.py', options);  //pythonでのコード処理が終わったらpythonからexportされたメッセージを受け取る //preload.jsにpythonから吐き出されたデータを送る pyshell.on('message', async function(message) { event.sender.send("return_data", message) }) }) preload.js const { contextBridge, ipcRenderer} = require("electron") //main.jsに書かれた"hoge" handlerを起動している contextBridge.exposeInMainWorld('api', { send: async (data) => await ipcRenderer.invoke('hoge', data), on: (channel, func) => { ipcRenderer.on(channel, (event, data) => func(data)); } } ); index.html <script> //ここでpreload.jsで作成されたapiを呼び出して何らかのデータを渡す const message= window.api.send({"send_data":send_data}) const message2=window.api.on("return_data", async (data)=>{ //returnされたデータを使った処理をこちらに書く }) </script> 最後に electronを使用してpythonコードをつかったアプリを作る必要があったため、自分用としてまとめておきました。非常にざっくりとした記事ですが、どなたかのお役に立てれば幸いです。 もし不備などありましたらお知らせいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jQueryでこんな書き方は要注意

jQuery 1系や3系でも動くスタート関数を書く 次のよく見かけるグローバル汚染を防ぐ為に書かれる無名関数で梱包する必要は無い。 (function() { $(function() { // ここから任意のコード }); }) (); jQueryをスタートするには $(function() { // ここから任意のコード }); これ一つだけで良い。 $(callback)はHTMLの解析が終わった後にコールバック関数を呼び出す。scriptタグの書く位置に左右されず、綺麗に実行します。 このように、余分で意味の無い関数は省いて、ブロックネストを減らす書き方をしよう。write less, do more! 互換の無い書き方 readyイベント 次のクセの悪い書き方をするとjQuery 3.0では動かなくなった1。 $(document).on('ready', function() {/* 任意のコード */}) $(document).ready(function() {/* 任意のコード */}); この書き方を検索すると結構出てくるし新規記事も多い。 Ajax 上記の続きとなるがjQuery 3.0以降ではAjaxのsuccess error completeが無事昇天してしまったのでなるべく互換をとりたいならthen()を使おう。 $.get('example.html').then( function(value) { alert('OK'); alert(value); }, function(error) { alert('NG:' + error.status); } ); 本当はFetch APIっぽく書きたかったがcatch()は3.0からなので、っぽく書くならこうなるかな。上記の書き方のthen()なら1.52から使えます3。 bodyタグの最後に書かれるscriptタグ氏 <body> <div id="button-container"> <button>ボタンAさん</button> </div> <script> $('button'); </script> </body> よくbodyの閉じタグ前にscriptタグを書く手法ですが、HTML解析が終わる前のセレクターやDOM操作はリスキーです。できれば 「jQuery 1.12.4や3系でも動くスタート関数を書く 」で書いたスタート関数内で処理しよう。 上記の例は正常に動くコードなので余計にクセが悪いです。scriptタグ位置に制約はありませんが各所に散らすと可読性が下がります。 理解せずこうしたら動くと当たり前のようにbodyの閉じタグ前に書くようにしてるポリシー持ちは良くありません。 scriptタグのdefer属性がどのブラウザでも通用しない 知っている方も多いと思いますが、HTML5ではscriptタグにdefer属性をつけるとHTMLの解析が終わった後にスクリプトを実行します。だが <script defer src="example.js"></script> IE君、君のことだよ…。 ひとまとめにしよう こんなコードはイヤだ1 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>こんなコードはイヤだ1</title> <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script> </head> <body> <div id="button-container"> <button id="button-a">ボタンAさん</button> <script> $('#button-a').click(function() { alert('ボタンAが押されたよ!'); }); </script> <button id="button-b">ボタンBさん</button> <script> $(function() { $('#button-b').click(function() { alert('ボタンBが押されたよ!'); }); }); </script> </div> </body> </html> 冗長的だ!しかもコピペ臭い。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>こんなコードはイヤだ1</title> <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script> <script> $(function() { $('#button-container > button').click(function() { alert( $( this ).data('message') ); }); }); </script> </head> <body> <div id="button-container"> <button data-message="ボタンAが押されたよ!">ボタンAさん</button> <button data-message="ボタンBが押されたよ!">ボタンBさん</button> </div> </body> </html> こうするとスッキリまとまり、無駄も少ない。jQueryはデータ属性もサポートされてるから活用していこう。 こんなコードはイヤだ2 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>こんなコードはイヤだ2</title> <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script> </head> <body> <div id="button-container"> <script> $(function() { document.write('<button>ボタンAさん</button>'); document.write('<button>ボタンBさん</button>'); }); </script> </div> </body> </html> なんか悲しくなってくる。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>こんなコードはイヤだ2</title> <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script> <script> $(function() { $('<button>ボタンAさん</button>' + '<button>ボタンBさん</button>').appendTo('#button-container'); }); </script> </head> <body> <div id="button-container"></div> </body> </html> せめてこう書くか、html()を活用しよう。 さいごに 「いまさらjQuery」感はスゴイのですが、初めてjQuery扱う、事情があってjQueryを使うという者に対して、我々の基準を押しつけてしまうような事は好きでは無い。 10年以上前のライブラリなので情報はたっぷりある代わりに?も多い。 Qiitaも例外では無い。 の新規記事を観てて思ったことはちゃんとスタート関数を書けてないケースが多い。サンプルコードだから省かれてるのかガチなのかよく分からないところだけど。 なのでこの記事の冒頭がアレなのだ。 https://jquery.com/upgrade-guide/3.0/#deprecated-document-ready-handlers-other-than-jquery-function ↩ progressFilterを追加する場合は1.8からとなります。 https://api.jquery.com/deferred.then/ ↩ done()やfail()でも同じ動作しますが「コード量を減らしたい」のと「Fetch APIっぽく」が目的なのでスルー ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript基礎メモその5

クラスのエクスポート、インポート クラスの定義の後で『export default クラス名』とすることで、そのクラスをエクスポート(出力)し 、他のファイルへ渡すことができる。 animal.js class Animal { // } export default Animal; // Animalクラスを他のファイルでも使用できるようにする設定 『dog.js』内でAnimalクラスを使用できるようにする。 他のファイルで定義されているクラスを使用するにはインポート(読込)をする必要がある。 使用するファイルの先頭で『import クラス名 form "./ファイル名"』と書くことでインポートすることができる。 ファイル名の拡張子の『.js』は省略できる。 import Animal from "./animal"; 値のエクスポート、インポート エクスポートする場合は『export default 定数名』 インポートする場合は『import 定数名 form "/ファイル名"』 sample1.js const text = "Hello World"; export default text; sample2.js import text from "./sample1"; console.log(text); 結果 Hello World デフォルトエクスポート 『export default』はデフォルトエクスポートと呼ばれ、そのファイルがインポートされると自動的に『export default 値』の値がインポートされる。そのためエクスポート時の値の名前と、インポート時の名前に違いがあっても問題ない。 dogData.js const dog = new Dog("reo", 4, "チワワ"); export default dog; script.js import doggy from "./dogData" // 名前に違いがあっても定数dogの値が入っている (dog → doggy) doggy.info() デフォルトエクスポートは1ファイル1つ飲み使える。 dogDate.js const dog1 = new Dog("han", 3, "柴犬"); const dog2 = new Dog("ryo", 2, "プードル"); export default dog1; export default dog2; // export default dog2; はエラーになる 名前つきエクスポート 名前付きエクスポートは複数の値をエクスポートできる。またデフォルトエクスポートと違い、複数の定数やクラスを指定してエクスポートできる。 『export{名前1, 名前2}』という形で書くことにより、1つのファイルから複数のエクスポートができる。 dogData.js const dog1 = new Dog("han", 3, "柴犬"); const dog2 = new Dog("ryo", 2, "プードル"); export { dog1, dog2 }; script.js import { dog1, dog2 } from "./dogData"; dog1.info(); dog2.info();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-routerでリファラを取得する

前のページ情報を取得したいときに、vue-routerを使ってリファラを取得する方法について書きます。 ちなみにvue-routerを使ってるとJSのdocument.referrerでは正しい値を取れません。 本文 ルーティングファイル内でbeforeEachを定義して、リファラとなる値を保存する。 src/router/index.js const router = new VueRouter({ // ... }); router.beforeEach((to, from, next) => { router['referrer'] = from; next(); }) そうすると使いたいコンポーネントでリファラの値を取得することができる。 Hoge.vue <script> export default { method: { hoge () { let referrer = this.$router.referrer; console.log(referrer); } } }; </script> 補足 ちなみに特定のページでリファラを取得するこっち↓のやり方もある。 Vue.jsでアクセス元(referrer)を取得する │ エトセトラ備忘録 データ取得 | Vue Router 参考 Vue.js - VueRouterで前のページのURLを取得することは可能でしょうか?|teratail ナビゲーションガード | Vue Router ナビゲーションガードについてもうちょっと勉強せねば・・・ 間違いなどあったらご指摘ください。 おしまい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Javascript】Promiseをいろいろ試してみた(No.1)

背景 Ruby on Railsの非同期通信を学習していて、そこから非同期処理&Promiseというキーワードが出てきたのでその辺りについて調べてみることに。合わせて同期処理と非同期処理の概念も学習した内容として纏めました。 ※内容に間違いなどがある場合はご指摘をよろしくお願いします。 Javascriptの同期処理と非同期処理 同期処理はプログラムを書いた順番に実行される処理のこと、非同期処理は順番を待たずに処理を行うことです。 同期処理の場合、重いプログラムを読み込む際にはローディングに時間が掛かってしまいます。その待ち時間の間に何かしらの処理を挟むことができないか。そこから生まれたのが非同期処理。また、とあるタイミングで処理を実行したいという場合に非同期処理を利用する場面もあるみたいです。 Promise 非同期処理の最終的な完了または失敗した時の結果を戻り値として返します。このpromiseには3つのstateがあります。 ①pending: 初期状態 ②fulfilled: 完了(処理が成功) ③rejected: 拒絶(処理が失敗) 成功した時と失敗した時をそれぞれ分けていることがポイントです。 Promiseの基本的な書き方 まずはfunctionを用意してその中にPromiseを定義。以下のような形式で書きます。stateは成功と失敗の2つ用意することができますが、シンプルに成功した場合だけにしました。 new Promise( state名 => { 何らかの処理; state名(処理の結果); }) HTMLの中にscriptタグで囲み、testPromise()という関数を定義。戻り値としてPromiseを返します。その結果をconsole.logで出力してみました。state名は何でもいいですが、state名とその後の処理に使われるstate名は必ず一致する必要があります。ここではresolveにします。 <script> function testPromise() { return new Promise(resolve => { //state名はresolve resolve(1); //state名(処理の結果); という形で処理の結果の値を表す }) } console.log(testPromise()); //testPromise()関数を実行した結果をconsole.logで表示 </script> consoleの結果を見てみると、fulfilledというstateとその結果の値'1'が表示されました。このようにPromiseはState(状態)とResult(結果)になっていることが分かります。 関数のパラメーターを指定した場合 実行する関数に引数を入れてその結果を表示させることもできるので試してみました。 <script> function testPromise(input) { return new Promise(resolve => { resolve(input); }) } console.log(testPromise('プロミスです。')); </script> testPromiseという関数にパラメーターとしてinputを指定し、引数'プロミスです'を渡しその結果をみると fulfilledと成功したstate(状態)と引数として渡した"プロミスです"が結果として表示されています。 stateが失敗(reject)の場合 処理が失敗した場合にconsoleでどういう結果が出るのか試してみました。 <script> function testPromise(input) { return new Promise((resolve, reject) => { //stateにreject(失敗)を追加します。 if (input) { //inputの値があった場合はresolve resolve(input); } else { //inputがない場合はreject reject('失敗です') } }) } console.log(testPromise()); </script> inputというパラメーターを取る関数testPromiseにわざと引数なしでconsole.logで出力してみます。 Promiseのstateはrejected(失敗)になり、結果として"失敗です"という文字列が出力されました。仮にinputに何かしらの引数を渡せば処理が成功しresolveが発動します。 参考サイト https://qiita.com/kiyodori/items/da434d169755cbb20447 https://qiita.com/Takagi_/items/84b4a2184f42ee77867c https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises https://hirocorpblog.com/post-383/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GASの排他制御(ロック)の利用方法を調べた

はじめに ここでいう排他制御(ロック)とは「何かの処理中は他の処理は待たせる」といった類いのものです。 自分自身、 GAS 以外の開発では排他に触れていたものの GAS では経験が無く、そのうち必要になるかなと思い気になったので調べてみました。 わかったことをまとめました。 対象の方 基本的な JavaScript が読める方 GAS で何かを作ったことがある方 ロックの利用手順 GAS で提供されるロック機能は、以下の流れで利用します。 LockService から目的に応じた Lock オブジェクトを取得 取得した Lock の tryLock() や waitLock() を実行しロックを取得 (ここで排他制御開始) 排他が必要な処理が終わったら Lock の releaseLock() でロックを開放 (ここで排他制御終了) まず利用するロックの種類を決めて、排他制御を開始し、用事が済んだら排他制御を終える、という流れです。 3種類のロック GAS のロックを利用するにあたって最初に決めるべきロックの種類を紹介します。 GAS では 3 種類のロックが提供されています。 それぞれ以下の特徴があり、目的に応じて使い分けます。 ドキュメントロック (リファレンス) Googleドキュメントなどのリソース単位で排他制御する場合に使い、これらに内包された GAS から利用する 上記のためか、Webアプリやスタンドアロンの GAS からは利用できない 対象のリソースを利用するすべてのユーザーに対して排他制御が適用される LockService.getDocumentLock() で取得する スクリプトロック (リファレンス) スクリプト単位で排他制御する場合に利用する たとえば複数人(スレッド)から同時に実行されたくない関数を作る場合に使える ドキュメントロックと異なり、Webアプリやスタンドアロンの GAS からも利用可能 LockService.getScriptLock() で取得する ユーザーロック (リファレンス) 同一ユーザーが特定のコードを同時実行するのを制御する目的で利用できる このロックは ユーザーが異なれば同時にコード実行できる 点に注意する 例えばボタンをダブルクリックされた場合に、関数が二重で動くのを抑止する目的で使えそう LockService.getUserLock() で取得する 上記 3 種類のロックはすべて Lock オブジェクトとして取得されます。 2種類の排他制御開始(ロック取得)方法 排他制御開始(ロック取得)方法は 2 種類あります。 いずれも Lock オブジェクトのメソッドです。 tryLock(timeoutInMillis) 指定したミリ秒の間、ロックの取得を試みます。ロックを取得できれば true を、そうでなければ false を返します waitLock(timeoutInMillis) tryLock と同様の動作をしますが、こちらはロックを取得できなかった場合に例外を投げます 3種類とか2種類とか... ややこしく感じるかもしれませんが、要はこういうことです。 まず 3 種類あるロックの種類から、目的に応じたロックを選ぶ 次に 2 種類の排他制御開始方法があるので、好きな方法で排他制御を開始する 使ってみる ドキュメントロック スプレッドシートで動きを確認しました。 新規にスプレッドシートを作り、「ツール」→「スクリプトエディタ」を開いて、下記のコードを記述します。 Code.gs function onEdit(e) { // ドキュメントロック const lock = LockService.getDocumentLock(); // ロックを取得する if (lock.tryLock(1 * 1000)) { // A1をB1~F1にコピーする const sheet = SpreadsheetApp.getActiveSheet(); const a1 = sheet.getRange('A1'); a1.copyTo(sheet.getRange('B1:F1')); SpreadsheetApp.flush(); // ちょっと待つ Utilities.sleep(5 * 1000); // ロック開放 lock.releaseLock(); } } この関数は各セルの値を変更する度に走ります。(詳細) やっていること ドキュメントロックを取得 A1 セルを B1~F1 セルにコピー 少し待ってからロック開放 A1 セルに何か入力すると B1~F1 にコピーされますが、この前にロックを取得しているため、ロックを取得できなかった場合は、B1~F1 へコピーされません。 つまり連続して A1 セルを書き換えると、初回はコピーされるもののそれ以降(ロックが開放されて次にロックが取れるまで)はコピーされなくなる、という動きを期待しています。 ドキュメントロックはすべてのユーザーに排他が適用されるため、1アカウントで動作確認できます。(複数アカウントでも試し、期待通りに動きました) スクリプトロック Web アプリを作って確認しました。 単体の GAS プロジェクトを作って、下記 2 ファイルを追加します。 Code.gs function doGet(e) { return HtmlService.createTemplateFromFile('index').evaluate(); } function incrementWithScriptLock() { // スクリプトロック const lock = LockService.getScriptLock(); // ロックを取得する if (lock.tryLock(10 * 1000)) { // A1の値をインクリメント incrementA1(); // ロック開放 lock.releaseLock(); } } function incrementWithoutScriptLock() { // A1の値をインクリメント incrementA1(); } function incrementA1() { const SSID = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; const a1 = SpreadsheetApp.openById(SSID).getActiveSheet().getRange('A1'); let value = a1.getValue(); a1.setValue(++value); SpreadsheetApp.flush(); } index.html <!DOCTYPE html> <html> <head> <base target="_top"> </head> <body> <button id="withLock">ロック取得あり</button> <button id="withoutLock">ロック取得なし</button> </body> <script> document.querySelector('#withLock').addEventListener('click', function() { for (let i = 0; i < 5; i++) { google.script.run.incrementWithScriptLock(); } }); document.querySelector('#withoutLock').addEventListener('click', function() { for (let i = 0; i < 5; i++) { google.script.run.incrementWithoutScriptLock(); } }); </script> </html> やっていること HTML 上のボタンが押されたら、下記処理を 5 回繰り返します。 スプレッドシートの A1 セルの値を取得する その値をインクリメントして A1 セルに書き込む スクリプトロックの働きを確認できるように、この処理の前にスクリプトロックを取る関数と取らない関数を作りました。期待する結果は、インクリメントを 5 回繰り返すので処理前より 5 増えていること、です。 動かすと、ロックを取得しない方は A1 セルの値が 5 増えていません。何回か動かしましたが増えたのは 1 だけでした。 対してロックを取得する方は期待通り 5 増えています。 (排他あるあるですね) なお、この画面を別のアカウントで開いてボタンを押した場合、ロックを取得する方は期待通りの動きをすることが確認できました。 ユーザーロック 上記、スクリプトロックで使ったコードを一部書き換えて確認しました。 具体的にはスクリプトロックを取得する箇所を、ユーザーロックを取得するように書き換えました。 Code.gs /* // スクリプトロック const lock = LockService.getScriptLock(); */ // ユーザーロック const lock = LockService.getUserLock(); 動かしてみると、スクリプトロックで動かしたときと特に変化はありません。 ロックを取得する方は期待通りの動きをしていました。 ただしこちらはユーザーごとのロックなので、別アカウントも含めて動かした場合にスクリプトロックとは挙動が異なりました。別々のアカウントで同時に画面のボタンを押すと、期待する結果とはならず、ユーザーごとのロックだということが確認できました。 所感 GAS にも排他制御の機能が備わっていて、割と簡単に利用できることが分かって良かったです。 個人的なぼやきですが、記事を書く時にわかりやすいサンプルコードが書ければなあと常々思っています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列と繰り返し処理

概要 配列についての復習と配列の取り出し方。 for文での配列の繰り返し処理について。 配列について データが順番に並んだ1つのデータを指します。 例えば、以下のようにフルーツデータがあるとします。 index.js const fruits1 = "りんご"; const fruits2 = "バナナ"; const fruits3 = "ぶどう"; const fruits4 = "なし"; const fruits5 = "みかん"; データが膨大で、管理するのに苦労します。 そこで、同じ意味合いのデータとして管理していきます。 以下になります。 index.js const fruits = ["りんご","バナナ","ぶどう","なし"]; 定数フルーツにりんごなどの要素が配列として格納されています。 コンソールで確認すると以下のような結果になります。 配列は0番始まりになります。 lengthは要素の数を表すプロパティになります。 for文使わない場合 配列の要素を取り出す際には以下のようになります。 index.js console.log(fruits[0]); console.log(fruits[1]); console.log(fruits[2]); console.log(fruits[3]); console.log(fruits[4]); 要素をとりだすことができました。 しかし、要素数が増えたときにコード量が膨大になってしまいます。 そこで、ループ処理を使って行きます。 for文 index.js for (let i = 0;i <= fruits.length;i++) { console.log(fruits); } index.js for (let i = 0;i <= fruits.length;i++) { console.log(`フルーツ${i}: ${fruits[i]}`); } 変数は0~始まります。上記のコードは実質6つ取り出すことになっています。 undifeunedは要素がありませんという意味です。 よってコードを、以下のように変更しました。 index.js for (let i = 0;i < fruits.length;i++) { console.log(`フルーツ${i}:${fruits[i]}`) } iは要素数5未満。つまり0~4の繰り返しになります。 よって要素数とiの変数が合致したことになります。 forEach forEachを使用するとコードが更にわかりやすくなります。 index.js fruits.forEach((fruit,index) => { console.log(`フルーツ${index}:${fruit}`) }); 引数は自由に決めることができます。 配列の要素を1つずつ受け取って、要素数がなくなるまで繰り返し処理を実行してくれます。 このforEachを使うことで、配列要素が増えたとしてもforEach内を修正することなく使うことができます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Webpack】jsファイルのimportがうまくいかない時のエラー2連続と対処

typescriptのコンパイルをwebpack経由でコンパイルを試しているときにエラーが発生。 発生エラーと対処内容が順番を追ってのものとなりました。 環境 ・ローカル環境で動作確認。 ・webpack5系 typscriptで書いたコードをwebpackでjsにコンパイルし、そのコンパイル後のjsファイルをindex.htmlで読み込んでいます。 で、読み込んでいるhtmlファイルのコンソールに出たエラーと対処方法です。 発生エラー1 SyntaxError: import declarations may only appear at top level of a module モジュールではimportはトップレベルで宣言してね、というもの コンパイル後のjsファイルでも最初に宣言していたのですが。。 少し調べてみるとそもそものhtmlから、スクリプトファイルを読み込むtypeにmoduleを指定している必要があったようです。 参考:https://stackoverflow.com/questions/42237388/syntaxerror-import-declarations-may-only-appear-at-top-level-of-a-module こちらを参考に <script type="module" src="./index.js"></script> と指定。 すると、今度は下記のエラーとなりました。 発生エラー2 クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、file:///Users/xxxx/プロジェクトフォルダ/dist/index.js にあるリモートリソースの読み込みは拒否されます (理由: CORS 要求が http でない)。 これはいろいろ調べていると、 ローカル環境でもサーバー起動上での動作ではない場合、クロスオリジン要求になってしまうみたいですね。。。 ここ最近のブラウザにセキュリティ対策の高まりによるものっぽい。 参考:https://qiita.com/terufumi1122/items/39b2a3659bc585c07f64 ということでwebpack-live-serverでローカルサーバを起動し正常に読み込めました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PAYJP 設定引き数一覧 リファレンスより

引数 カード番号 card[number] String 必須 有効期限月 card[exp_month] String 必須 有効期限年 card[exp_year] String 必須 3~4桁のCVCコード card[cvc] String 都道府県 card[address_state] String 市区町村 card[address_city] String 番地など card[address_line1] String 建物名など card[address_line2] String 郵便番号 card[address_zip] String 2桁のISOコード(e.g. JP) card[country] String カード保有者名(e.g. "YUI ARAGAKI”) card[name] String
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む