20220116のJavaScriptに関する記事は14件です。

【JavaScript】ES6 モジュールのエクスポート&インポート、およびbabelのインストール方法

モジュールとは 部品と近い意味ですね。 大きなファイルを機能ごととか、役割ごととかで細分化する。 細分化したものを組み合わせて一つのものにする。 モジュールのメリット ・重複命名防止 ・コード再利用可能 ・保守しやすい モジュールの文法 export モジュールをエクスポートする import モジュールをインポートする エクスポートの方法 主に三つに分けられる。 1.個別エクスポート 2.一括エクスポート 3.デフォルトエクスポート 個別エクスポート エクスポートしようと思う データの前に、「export」を加えること。 individualExport.js export let language = "JavaScript"; export function output(){ console.log("I'am from individualExport.js file!! "); } 一括エクスポート export{ // エクスポートしようと思うデータ} batchExport.js let language = "React"; function output(){ console.log("I'am from batchExport.js file!! "); } export {language,output} デフォルトエクスポート export defaultを使用して、エクスポートする。 出力したdefaultのは、object型 defaultExport.js export default{ language : "Jquery", change : function(){ console.log("I'am from defaultExport.js file!!"); } } インポートの方法 1.全部インポート 2.分割代入 3.リネーム代入 4.別名でデフォルトインポート import.js //1.全部インポート import * as i from "individualExport.js"; import * as b from "batchExport.js"; import * as d from "defaultExport.js"; /********************************************************************* 要注意:defaultの場合、戻り値はオブジェクト型のため、 オブジェクト中のものを呼び出すときには、普通のオブジェクト型と同じ扱いしないといけない。 *********************************************************************/ d.default.change()  //I'am from defaultExport.js file!! //2.分割代入 import {language,output} from "individualExport.js"; //3.リネーム代入 import {language as language2 ,output as output2} from "batchExport.js"; //4.別名でデフォルトインポート import {default as one} from "defaulExport.js"; 他のモジュール導入方法 モジュールをapp.jsに集約して、HTMLファイルで app.jsをインポートする。 app.js import * as i from "individualExport.js"; import * as b from "batchExport.js"; import * as d from "defaultExport.js"; forImport.html <html> <head> <meta charset="utf-8"> <title>モジュールインポート</title> </head> <body> <script src="./js/app.js" type="module"></script> </body> </html> おまけ 一部のブラウザ(*)はまだES6以上の仕様に対応していないので、最新の書き方で書いたJavaScriptを反映できない。 そこで、Babelを使って、ES5に変換してもらえる。 *https://kangax.github.io/compat-table/es6/ インストール npm i babel-cli babel-preset-env browserify babelで変換 npx babel js(変換前のjsフォルダパース) -d dist/js(変換前のjsフォルダパース) --presets=babel-preset-env バンドル npx browserify ./js/app.js -o dist/bundle.js インポート <script src="./js/bundle.js" type="module"> </script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】privateな変数の双方向バインディング方法

はじめに JavaScriptでprivateな変数やメソッドが作れるようになったようです。 私は業務でVue.jsを触っているため、「双方向バインディングする場合はどのように書くんだろう?」 と気になったので試してみました。 結果 getterとsetterを用意して、「v-model="クラス名.変数名"」で良いみたいでした。 検証 変数に「#」を付けることでprivateになるようです。 一旦setterは用意せず、以下のようなクラスを作成しました。 module.exports = class Test{ #detail; #tag; constructor() { this.#detail = null; this.#tag = null; } get detail() { return this.#detail; } get tag() { return this.#tag; } }; 画面側は以下のように実装しました。 <template> <div> <div class="control column"> <base-select v-model="test.tag" :options="testTags"></base-select> </div> <div class="control column"> <base-input v-model="test.detail" ></base-input> </div> </div> </template> <script> import BaseSelect from "~/components/BaseSelect.vue"; import BaseInput from "~/components/BaseInput.vue"; export default { components: { BaseSelect, BaseInput }, data() { const Test= require("~/server/api/models/test"); return { testTags: require("~/assets/testTags.json"), test: new Test(), }; } }; </script> この状態で画面上で値を変更してみると以下のようなエラーが発生します。 TypeError: Cannot set property tag of #<Test> which has only a getter 「setterを用意しないと値がセットできないぞ」ということです。 ということでsetterを用意したところ、想定通りに値を変更することができました。 結果に書いている通り「v-model="クラス名.変数名"」で良いみたいでした。 追加検証 次に「#」を外して同様に実行してみます。 getterもsetterも無い以下のようなクラスを用意します。 module.exports = class Test{ constructor() { this.detail = null; this.tag = null; } }; 画面のコードは同じもので、再度画面から値を変更してみるとエラーも何も発生せず変更することができます。 しかし、クラス内の変数をgetterとsetterを介さず直接操作をすることになってしまいます。(と思っています。) おわりに 親側にsetterを呼ぶメソッドを書いて、子側でそのメソッドを呼ぶことで値が変更できる、 そんな実装になるのかと思ったのですが、その必要はなくシンプルでよさそうでした。 参考になりましたら幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LIFFとStripe Checkoutで決済画面を作るときの個人的Tips

LIFFとは LIFF(LINE Front-end Framework)とは、LINEのアプリ上で動くWebアプリのプラットフォームのことです。LIFFを使って動作するアプリを「LIFFアプリ」といいます。 LIFFを使用するとLINEのユーザー情報を取得したり、トークルームにメッセージを送ったりできます。 1. LINEログインのユーザー情報を使う 特にサブスクリプション決済をする場合は、各ユーザーの支払いステータスを追うため、認証機能の実装が必要になります。LIFFを使うとユーザーはすでにLINEにログインしている状態なので、LINEログインのユーザー情報を使用できます。 liff.getDecodedIDToken()という関数で、ユーザーの表示名、プロフィール画像のURL、メールアドレスなどの情報を取得できます。 https://developers.line.biz/ja/reference/liff/#get-decode-id-token メールアドレスの取得は、チャンネル毎に申請が必要となります。 LINE Developersから申請した後、LIFFのemailスコープを有効にしてください。 2. 決済時のメールアドレス入力を省く Stripe.jsからCheckoutを生成する時に、上で取得したメールアドレスをcustomerEmailというパラメータに渡すとCheckout画面のメールアドレス欄にPrefillされた状態でリダイレクトされます。 https://stripe.com/docs/js/checkout/redirect_to_checkout ユーザーの入力の手間を減らして成約率を高めましょう。 stripe.redirectToCheckout({ lineItems: [{price: '<YOUR_PRICE_ID>', quantity: 1}], mode: 'subscription', successUrl: `${location.href}/success?id={CHECKOUT_SESSION_ID}`, cancelUrl: `${location.href}/cancel`, + customerEmail: email, // LINEから取得したメールアドレス clientReferenceId: userId, locale: "ja", submitType: "pay" }) 3. LINEのユーザーIDをStripeのリソースに紐づける Checkoutセッションの生成時にclientReferenceIdというパラメータにユーザーIDを渡すことで、CheckoutオブジェクトにLINEのユーザーIDを乗せることができます。 stripe.redirectToCheckout({ lineItems: [{price: '<YOUR_PRICE_ID>', quantity: 1}], mode: 'subscription', successUrl: `${location.href}/success?id={CHECKOUT_SESSION_ID}`, cancelUrl: `${location.href}/cancel`, customerEmail: email, + clientReferenceId: userId, // LINEのユーザーID locale: "ja", submitType: "pay" }) Webhookレシーバーでcheckout.session.completedなどのイベントを受け取ってCustomerリソースに紐づけたり、といったことが可能です。 または、LINE MessagingAPIのWebhookでFollowイベント(友だち追加時に発火)を受け取れるので、そのときにStripe側にCustomerを作ってもOKです。 https://developers.line.biz/ja/reference/messaging-api/#follow-event Customer作成のタイミング別のメリットデメリットはStripe社の岡本さんの記事がとても分かりやすいので、そちらをご参照下さい。 4. ボットリンク機能を有効にする ボットリンク機能を有効にすることで、LIFFアプリの権限要求時に公式アカウントの友だち登録を促します。決済機能を付けたいLINE公式アカウントと紐づけることをおすすめします。 https://developers.line.biz/ja/docs/line-login/link-a-bot/ サンプルアプリ サンプルアプリ(LINEが立ち上がります) ソースコード(GitHub) まとめ サブスクリプションの支払いステータス管理には会員登録&ログインが必須です。 ですが、LIFFを併せて使用することで、会員登録やログインをなくせる & LINEログインのユーザー情報を使うことができます。 LIFFとStripeはとても相性が良いので、ぜひ試してみて下さい? ご興味ある方は下の記事もぜひ!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ساندویچ پانل

مصالح مختلفی براای ساخت یک بنای ساختمانی مورد نیاز است که بعضا بسیار سنگین هستند و آماده سازی آنها نیاز به زمان طولانی دارد. در این شرایط استفاده از یک مصالح سبک و از پیش ساخته شده می تواند بسیار منطقی و پر بازده باشد. ساندویچ پانل ها دقیقا به همین هدف مورد استفاده قرار می گیرند. ساندویچ پانل سازه ای است که عموما برای روکش دیوارها و یا سقف ساختمان ها به کار برده می شود. نکته قابل ذکر درباره پانل ها این است که هسته آنها از مواد عایق حرارتی تشکیل شده که بواسطه به کار گیری دو فلز در اطراف، پوشانیده می شود. نیروهای سازه ای توسط چارچوب فولادی یا قاب حامل دیگری که ساندویچ پانل ها به آن متصل شده اند،حمل می شوند.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

論理演算子(&&, ||)を「かつ」「または」と覚えるのはもうやめよう

※こちらの記事は初学者向け・丁寧に解説を心がけております。  その分、文章量も多いのでご了承ください。 プログラミングを学びたての頃、論理演算子の章で && を「かつ」 || を「または」 と覚えてしまった人も多いのではないでしょうか? 高校数学 A で習う「集合」のカリキュラムに登場する「A∩B」や「A∪B」の考え方に沿えば一番馴染みのある表現ではありますが、プログラムの振る舞いに着目すると厳密には異なります。 また、経験を積んでいくにつれて、&&を「かつ」、||を「または」と表現すると、説明がしづらいこと場面にも遭遇します。 この記事を読んで、「かつ」「または」という考え方から脱却していきましょう。 また、この記事では ブラウザですぐ実行できること 多くの初学者にも馴染みがある という点からJavaScriptを基に解説していきますが、一部挙動は異なるものの、Ruby や PHP などでも考え方は同じです。 用語解説 AND 演算子 &&のこと アンパサンド2つと書くのがめんどくさい。 OR 演算子 ||のこと パイプライン2つと書くのがめんどくさい。 オペランド 式を構成する要素だよ 演算子じゃない方だよ 数字とか変数とかだよ オペランド (operand)とは|「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典 a || bであれば、aやbのことです。 正式名称ではないかもしれませんが、この記事ではaを左オペランド、bを右オペランドと言わせてください。 Boolean trueとfalse の2つの値を取るデータ型のこと。 先に結論 AND演算子&&は、左から順にfalseを探しにいき、 OR演算子||は、左から順にtrueを探しにいき、 見つかればその値を返す。 見つからなければ最後の値を返すよ。 true/falseでない値は、 trueに変換できるtruthyな値か、 falseに変換できるfalsyな値かに着目しよう。 よくある例題 初学者向け教材にはこんな例題が載っています。 [例題1] 数学と理科のテストの点数を変数に格納し、 どちらも 70 点以上であれば「合格」 そうでなければ「不合格」 と出力するプログラムを書いてみましょう。 test1.js const math = 80 const science = 65 if (math >= 70 && science >= 70) { console.log('合格!') } else { console.log('不合格…') } 頭の中で日本語に直すと if 文は ・もし (「math が 70 以上」かつ「science が 70 以上」)ならば という意味だな。 math は 80 で 70 以上だから true だけど、science が 65 で 70 以上ではないので false。 2つとも true じゃないから、「かつ」を満たさない。 よって、if 文全体の評価は false となり、else の方に進み、「不合格」が表示される。 こんな感じでしょうか。 この思考自体は間違っていません。この例題の回答としては十分クリアできています。 「かつ」「または」という考え方の弊害 しかし、AND 演算子&&を「かつ」と暗記していると、こんなイジワル問題に遭遇した時に、瞬時に答えられないかもしれません。 [例題2] 以下のコードを実行すると、 console にどのように出力されるでしょうか? test2.js const math = 80 const science = 65 if (math && science) { console.log('ifの中です') } else { console.log('elseの中です') } 変数を展開するとこうなりますが if (80 && 65) 80かつ65ってなんなんだ?と混乱してしまいます。 つまり、「かつ」「または」と覚えたことの弊害は、左右のオペランドが論理式(70 >= 80 のように、比較して true か false が返される式)の場合に理解が限定されてしまうことです。 >や==などが出てこない場合でも、JavaScript がどのように判断しているのか理解できるようになりましょう。 AND 演算子&& AND 演算子&&は、現代の JavaScript チュートリアルではこのように解説されています。 ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。 順番に見ていきましょう。 ① 左から右にオペランドを評価する これはプログラミングの原則通りの向きなので問題ないでしょう。 「評価する」という表現がわかりにくければ、「1つ1つ見ていく」と変換しても構いません。 ② それぞれのオペランドで、それを Boolean に変換します。 Boolean という言葉が分かりにくければ、「それぞれのオペランドで、それを真か偽のどちらかに変換する」と言い換えることができます。 では「Boolean に変換する」とはどういうことでしょうか? truthy / falsy JavaScript には、 'リンゴ', ''(空文字)などの文字列 0, -15, 3.14などの数値 ['アメリカ', '日本', 'ドイツ']などの配列 { name: '鈴木', age: 20 }などのオブジェクト 他にも true, false, null, undefined など、様々な型の値が存在しますが、それらは全て true または false に変換することができます。 変換した時に true になるものをtruthy、false になるものをfalsyと言います。 JavaScript ではfalseそのものの他に、0, ''(空文字), null, undefinedなどは Boolean に変換するとfalseになることからfalsyな値です。 falsy な値以外は全て Boolean に変換するとtrueになることから、truthyな値です。 もっと詳しく知りたい方は以下の記事をご覧ください。 Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN (ちなみにRubyではnilとfalseの2つだけが falsyで、それ以外は全てtruthyになります。 数値の 0 が JavaScript では falsy でも、Ruby では truthy になります。 ポートフォリオ作りで両方同時に学ぶときには注意しましょう。) これを踏まえて、例題2の80 && 65をもう1度考えてみましょう。 80 は truthy な値なので true に、 65 も truthy な値なので true に変換されます。 if (80 && 65) ↓ if (true && true) ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 80 も 65 もどちらも true に変換されたため、false がありません。 ③ はスキップします。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。 ④ に該当するため、最後のオペランド、今回は 65 を返します。 つまり、80 && 65は評価されると65になるのです。 if (80 && 65) ↓ -- if (true && true)を経由 -- ↓ if (65) 論理演算子はなくなったものの、例題の答えにたどり着けていないので補足をすると、 if 文も AND 演算子のときと同様に、かっこの中の式を評価し、Boolean に変換した結果で、if の中に入るかどうかを決定します。 if (65) { //if文がtrueと評価されるのでこちらに進みます。 console.log("ifの中です"); } else { console.log("elseの中です"); } 先ほどと同様、65はtruthyな値なので、この if 文全体は true となり、例題の答えは「'ifの中です'と表示される」でした。 例題1を振り返る 改めて一般的な論理式が含まれた if 文で復習してみましょう。 test1.js const math = 80 const science = 65 if (math >= 70 && science >= 70) { console.log('合格!') } else { console.log('不合格…') } ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 評価対象が元々論理式の場合は、Boolean に変換するというより、評価結果そのものが true か false なので、 math >= 70の評価結果はtrue science >= 70の評価結果はfalseです。 と言うべきでしょうか。 if (math >= 70 && science >= 70) ↓ if (true && false) ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 左から順に見ていって、左オペランドはtrueですが、右オペランドはfalseなので右側でストップし、本来の値(今回は評価結果のfalse)が返されます。 if (math >= 70 && science >= 70) ↓ -- if (true && false)を経由 -- ↓ if (false) -------------------------------- if (false) { console.log('ifの中です'); } else { //if文がfalseなのでこちらに進みます console.log('elseの中です'); } いかがでしょうか? 当然「かつ」と読んでいたとしても結果は変わりませんが、この記事を読むまで AND 演算子&&を「かつ」と考えてきた人は、左右のどちらもtrueであることで式全体が「真」と評価されるように、trueの方に重きを置かれがちですが、③ で見てきた通り、プログラムはfalseを探すことに一生懸命になっていますね。 着眼点が異なりますね。 この違いに慣れるまでは少し大変かもしれませんが、慣れてしまえば自然と読めてくるようになります。 OR 演算子|| OR 演算子||は、AND 演算子&&の③④の太字部分を反転させたものです。 ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 ③ もしも結果が true であれば、停止しオペランドの本来の値を返します。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 偽 のとき), 最後のオペランドを返します。 例題を見ながら流れを確認していきましょう。 [例題3] 以下のコードを実行すると、 console にどのように出力されるでしょうか? test3.js if (0 || 0.1) { console.log('ifの中です'); } else { console.log('elseの中です'); } AND 演算子のときほど丁寧には解説しませんが、 0 は falsy な値なので false に、 0.1は truthy な値なのでtrueに変換されます。 左から順に評価し、trueを探しにいきます。 左オペランドはfalseですが、右オペランドがtrueなので、ここで処理がストップし、本来の値0.1が返されます。 test3.js if (0 || 0.1) ↓ -- if (false || true)を経由 -- ↓ if (0.1) -------------------------------- if (0.1) { //if文がtrueと評価されるのでこちらに進みます。 console.log('ifの中です'); } else { console.log('elseの中です'); } OR 演算子||は、1つ1つtrueを探すので、「または」のように「どちらかが」という文脈には沿っていますね。 役立つ場面 論理演算子を「かつ」「または」ではなく、プログラム本来の振る舞いで意識できると役立つ場面を紹介します。 React の条件付きレンダー React の JSX で使える書き方紹介の中に、AND 演算子&&が出てきます。 sample.jsx return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); これが動作するのは、JavaScript では true && expression は必ず expression と評価され、false && expression は必ず false と評価されるからです。 条件付きレンダー – React ここまで読んでくれた方であれば、上記解説の内容を既にご自分で理解できるようになっていると思いますが、 AND 演算子&&は左から順にfalseを探しにいきます。 左オペランドがtrueなら右オペランドを見にいきます。 右オペランドの HTML タグは truthy なので、最後の要素である右オペランドのHTMLタグが返される(=表示される) 左オペランドがfalseならそこで処理はストップし、falseを返すため、右オペランドのHTMLタグは表示されない という流れですね。 OR 演算子を用いた代入 a = b || cのような OR 演算子||を用いた代入はよく見かけます。 例えば、メモアプリの投稿フォームで、 タイトルを入力して投稿したらそのタイトルを タイトルを空欄のまま投稿したら現在日時に置き換えてデータベースに保存する このような処理を考えてみましょう。 memo.js const defaultTitle = dateTimeFunction(); // '20220116_235940'などの現在日時を返す関数とする const enteredTitle = formData.title; // ユーザーがメモ投稿フォームのタイトル欄に入力した内容 const title = enteredTitle || defaultTitle; 右オペランドが必ずtruthyになるように文字列を与えることで、 左オペランドのenteredTitleが何かしらの文字列なら、そこで処理はストップし、enteredTitleの中身を返す 左オペランドのenteredTitleが''(空文字)なら、右オペランドのdefaultTitleの中身を返す となります。 他にも、a = a || bのように、 aがtruthyならa aがfalsyならb を代入するものもあります。 (厳密には異なりますが、省略形の論理和代入||=なんてものもあったりしますが、滅多に出てきませんのであまり気にしないで大丈夫です。) まとめ 論理演算子の左右のオペランドは、どのような値でもtrueかfalseに評価できることから、必ずしも論理式である必要はありません。 ①〜④ のルールを覚えておくことで、&&や||の左右に複雑なものが含まれていても、コードリーティング等で不用意に臆することがなくなるでしょう。 少々アカデミックな堅苦しい内容となりましたが、皆様のプログラミング学習のお役に立てばこの上なく嬉しく思います。 参考文献 オペランド (operand)とは | 「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典 論理演算子 | 現代の JavaScript チュートリアル 論理積 (&&) - JavaScript | MDN 論理和 (||) - JavaScript | MDN Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN 条件付きレンダー – React
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptの論理演算子(&&, ||)を「かつ」「または」と覚えるのはもうやめよう

※こちらの記事は初学者向け・丁寧に解説を心がけております。  その分、文章量も多いのでご了承ください。 プログラミングを学びたての頃、論理演算子の章で && を「かつ」 || を「または」 と覚えてしまった人も多いのではないでしょうか? 高校数学 A で習う「集合」のカリキュラムに登場する「A∩B」や「A∪B」の考え方に沿えば一番馴染みのある表現ではありますが、プログラムの振る舞いに着目すると厳密には異なります。 また、経験を積んでいくにつれて、&&を「かつ」、||を「または」と表現すると、説明がしづらいこと場面にも遭遇します。 この記事を読んで、「かつ」「または」という考え方から脱却していきましょう。 また、この記事では ブラウザですぐ実行できること 多くの初学者にも馴染みがある という点からJavaScriptを基に解説していきますが、一部挙動は異なるものの、Ruby や PHP などでも考え方は同じです。 (追記:コメントをいただき調べましたところ、CやJava, PHPでは大きく挙動が異なるため、タイトルの先頭に「JavaScriptの」を追加させていただきました。誤解を生む表現を使ってしまい、申し訳ございませんでした。) 用語解説 AND 演算子 &&のこと アンパサンド2つと書くのがめんどくさい。 OR 演算子 ||のこと パイプライン2つと書くのがめんどくさい。 オペランド 式を構成する要素だよ 演算子じゃない方だよ 数字とか変数とかだよ オペランド (operand)とは|「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典 a || bであれば、aやbのことです。 正式名称ではないかもしれませんが、この記事ではaを左オペランド、bを右オペランドと言わせてください。 Boolean trueとfalse の2つの値を取るデータ型のこと。 先に結論 AND演算子&&は、左から順にfalseを探しにいき、 OR演算子||は、左から順にtrueを探しにいき、 見つかればその値を返す。 見つからなければ最後の値を返すよ。 true/falseでない値は、 trueに変換できるtruthyな値か、 falseに変換できるfalsyな値かに着目しよう。 よくある例題 初学者向け教材にはこんな例題が載っています。 [例題1] 数学と理科のテストの点数を変数に格納し、 どちらも 70 点以上であれば「合格」 そうでなければ「不合格」 と出力するプログラムを書いてみましょう。 test1.js const math = 80 const science = 65 if (math >= 70 && science >= 70) { console.log('合格!') } else { console.log('不合格…') } 頭の中で日本語に直すと if 文は ・もし (「math が 70 以上」かつ「science が 70 以上」)ならば という意味だな。 math は 80 で 70 以上だから true だけど、science が 65 で 70 以上ではないので false。 2つとも true じゃないから、「かつ」を満たさない。 よって、if 文全体の評価は false となり、else の方に進み、「不合格」が表示される。 こんな感じでしょうか。 この思考自体は間違っていません。この例題の回答としては十分クリアできています。 「かつ」「または」という考え方の弊害 しかし、AND 演算子&&を「かつ」と暗記していると、こんなイジワル問題に遭遇した時に、瞬時に答えられないかもしれません。 [例題2] 以下のコードを実行すると、 console にどのように出力されるでしょうか? test2.js const math = 80 const science = 65 if (math && science) { console.log('ifの中です') } else { console.log('elseの中です') } 変数を展開するとこうなりますが if (80 && 65) 80かつ65ってなんなんだ?と混乱してしまいます。 つまり、「かつ」「または」と覚えたことの弊害は、左右のオペランドが論理式(70 >= 80 のように、比較して true か false が返される式)の場合に理解が限定されてしまうことです。 >や==などが出てこない場合でも、JavaScript がどのように判断しているのか理解できるようになりましょう。 AND 演算子&& AND 演算子&&は、現代の JavaScript チュートリアルではこのように解説されています。 ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。 順番に見ていきましょう。 ① 左から右にオペランドを評価する これはプログラミングの原則通りの向きなので問題ないでしょう。 「評価する」という表現がわかりにくければ、「1つ1つ見ていく」と変換しても構いません。 ② それぞれのオペランドで、それを Boolean に変換します。 Boolean という言葉が分かりにくければ、「それぞれのオペランドで、それを真か偽のどちらかに変換する」と言い換えることができます。 では「Boolean に変換する」とはどういうことでしょうか? truthy / falsy JavaScript には、 'リンゴ', ''(空文字)などの文字列 0, -15, 3.14などの数値 ['アメリカ', '日本', 'ドイツ']などの配列 { name: '鈴木', age: 20 }などのオブジェクト 他にも true, false, null, undefined など、様々な型の値が存在しますが、それらは全て true または false に変換することができます。 変換した時に true になるものをtruthy、false になるものをfalsyと言います。 JavaScript ではfalseそのものの他に、0, ''(空文字), null, undefinedなどは Boolean に変換するとfalseになることからfalsyな値です。 falsy な値以外は全て Boolean に変換するとtrueになることから、truthyな値です。 もっと詳しく知りたい方は以下の記事をご覧ください。 Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN (ちなみにRubyではnilとfalseの2つだけが falsyで、それ以外は全てtruthyになります。 数値の 0 が JavaScript では falsy でも、Ruby では truthy になります。 ポートフォリオ作りで両方同時に学ぶときには注意しましょう。) これを踏まえて、例題2の80 && 65をもう1度考えてみましょう。 80 は truthy な値なので true に、 65 も truthy な値なので true に変換されます。 if (80 && 65) ↓ if (true && true) ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 80 も 65 もどちらも true に変換されたため、false がありません。 ③ はスキップします。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。 ④ に該当するため、最後のオペランド、今回は 65 を返します。 つまり、80 && 65は評価されると65になるのです。 if (80 && 65) ↓ -- if (true && true)を経由 -- ↓ if (65) 論理演算子はなくなったものの、例題の答えにたどり着けていないので補足をすると、 if 文も AND 演算子のときと同様に、かっこの中の式を評価し、Boolean に変換した結果で、if の中に入るかどうかを決定します。 if (65) { //if文がtrueと評価されるのでこちらに進みます。 console.log("ifの中です"); } else { console.log("elseの中です"); } 先ほどと同様、65はtruthyな値なので、この if 文全体は true となり、例題の答えは「'ifの中です'と表示される」でした。 例題1を振り返る 改めて一般的な論理式が含まれた if 文で復習してみましょう。 test1.js const math = 80 const science = 65 if (math >= 70 && science >= 70) { console.log('合格!') } else { console.log('不合格…') } ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 評価対象が元々論理式の場合は、Boolean に変換するというより、評価結果そのものが true か false なので、 math >= 70の評価結果はtrue science >= 70の評価結果はfalseです。 と言うべきでしょうか。 if (math >= 70 && science >= 70) ↓ if (true && false) ③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。 左から順に見ていって、左オペランドはtrueですが、右オペランドはfalseなので右側でストップし、本来の値(今回は評価結果のfalse)が返されます。 if (math >= 70 && science >= 70) ↓ -- if (true && false)を経由 -- ↓ if (false) -------------------------------- if (false) { console.log('ifの中です'); } else { //if文がfalseなのでこちらに進みます console.log('elseの中です'); } いかがでしょうか? 当然「かつ」と読んでいたとしても結果は変わりませんが、この記事を読むまで AND 演算子&&を「かつ」と考えてきた人は、左右のどちらもtrueであることで式全体が「真」と評価されるように、trueの方に重きを置かれがちですが、③ で見てきた通り、プログラムはfalseを探すことに一生懸命になっていますね。 着眼点が異なりますね。 この違いに慣れるまでは少し大変かもしれませんが、慣れてしまえば自然と読めてくるようになります。 OR 演算子|| OR 演算子||は、AND 演算子&&の③④の太字部分を反転させたものです。 ① 左から右にオペランドを評価します。 ② それぞれのオペランドで、それを Boolean に変換します。 ③ もしも結果が true であれば、停止しオペランドの本来の値を返します。 ④ もしもすべての他のオペランドが評価された場合(i.e. すべて 偽 のとき), 最後のオペランドを返します。 例題を見ながら流れを確認していきましょう。 [例題3] 以下のコードを実行すると、 console にどのように出力されるでしょうか? test3.js if (0 || 0.1) { console.log('ifの中です'); } else { console.log('elseの中です'); } AND 演算子のときほど丁寧には解説しませんが、 0 は falsy な値なので false に、 0.1は truthy な値なのでtrueに変換されます。 左から順に評価し、trueを探しにいきます。 左オペランドはfalseですが、右オペランドがtrueなので、ここで処理がストップし、本来の値0.1が返されます。 test3.js if (0 || 0.1) ↓ -- if (false || true)を経由 -- ↓ if (0.1) -------------------------------- if (0.1) { //if文がtrueと評価されるのでこちらに進みます。 console.log('ifの中です'); } else { console.log('elseの中です'); } OR 演算子||は、1つ1つtrueを探すので、「または」のように「どちらかが」という文脈には沿っていますね。 役立つ場面 論理演算子を「かつ」「または」ではなく、プログラム本来の振る舞いで意識できると役立つ場面を紹介します。 React の条件付きレンダー React の JSX で使える書き方紹介の中に、AND 演算子&&が出てきます。 sample.jsx return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); これが動作するのは、JavaScript では true && expression は必ず expression と評価され、false && expression は必ず false と評価されるからです。 条件付きレンダー – React ここまで読んでくれた方であれば、上記解説の内容を既にご自分で理解できるようになっていると思いますが、 AND 演算子&&は左から順にfalseを探しにいきます。 左オペランドがtrueなら右オペランドを見にいきます。 右オペランドの HTML タグは truthy なので、最後の要素である右オペランドのHTMLタグが返される(=表示される) 左オペランドがfalseならそこで処理はストップし、falseを返すため、右オペランドのHTMLタグは表示されない という流れですね。 OR 演算子を用いた代入 a = b || cのような OR 演算子||を用いた代入はよく見かけます。 例えば、メモアプリの投稿フォームで、 タイトルを入力して投稿したらそのタイトルを タイトルを空欄のまま投稿したら現在日時に置き換えてデータベースに保存する このような処理を考えてみましょう。 memo.js const defaultTitle = dateTimeFunction(); // '20220116_235940'などの現在日時を返す関数とする const enteredTitle = formData.title; // ユーザーがメモ投稿フォームのタイトル欄に入力した内容 const title = enteredTitle || defaultTitle; 右オペランドが必ずtruthyになるように文字列を与えることで、 左オペランドのenteredTitleが何かしらの文字列なら、そこで処理はストップし、enteredTitleの中身を返す 左オペランドのenteredTitleが''(空文字)なら、右オペランドのdefaultTitleの中身を返す となります。 他にも、a = a || bのように、 aがtruthyならa aがfalsyならb を代入するものもあります。 (厳密には異なりますが、省略形の論理和代入||=なんてものもあったりしますが、滅多に出てきませんのであまり気にしないで大丈夫です。) まとめ 論理演算子の左右のオペランドは、どのような値でもtrueかfalseに評価できることから、必ずしも論理式である必要はありません。 ①〜④ のルールを覚えておくことで、&&や||の左右に複雑なものが含まれていても、コードリーティング等で不用意に臆することがなくなるでしょう。 少々アカデミックな堅苦しい内容となりましたが、皆様のプログラミング学習のお役に立てばこの上なく嬉しく思います。 参考文献 オペランド (operand)とは | 「分かりそう」で「分からない」でも「分かった」気になれる IT 用語辞典 論理演算子 | 現代の JavaScript チュートリアル 論理積 (&&) - JavaScript | MDN 論理和 (||) - JavaScript | MDN Truthy (真値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN 条件付きレンダー – React
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webの勉強はじめてみた その21 〜Slack上で動くbot作成1〜

N予備校の「プログラミング入門Webアプリ」を受講しています。 今回は第3章7〜9節です。 講義内ではTODOのbotでしたが、復習も兼ねて自分で作りました。 学んだ用語 CRUD クラッド ソフトウェアが情報の永続化をしようとしたときに出てくる操作、Create, Read, Update, Deleteの頭文字をとったもの。 リファクタリング ソースコードを見易くする。可読性の向上。 テスト駆動開発 実装や修正よりも先にテストを用意し、テストに適合するようにリファクタリングを繰り返すことでコードの品質を高めていく開発手法 とにかくほめてくれるbotを作る 要件 「ほめて」 : あらかじめ用意された配列からランダムで発言 「もっと ~」 : ~という内容を配列に登録。 「ほめないで ~」 : ~(インデックス)番のメッセージを削除 「見せて」 : 登録されているメッセージを一覧で表示 手順 praise-bot-testディレクトリを作成 1. yeomanのインストール(Dockerfileに記述) 2. yarn initで package.json を作成 3. index.js(モジュールを記述するファイル)とtest.js(テストを実行するファイル) 4. モジュールの実装 5. リファクタリング praise-botディレクトリ作成 1. yeomanのインストール(Dockerfile) 2. cp -r でpraise-bot-testをコピー 3. yo hubot --adapter=slack でbot作成 4. scriptsディレクトリに実行ファイル(index.js)を作成 5. 実装 6. slack上でhubotの作成 7. コンソール上で環境変数の指定と実行 Docker FROM --platform=linux/x86_64 node:14.15.4 RUN apt-get update RUN apt-get install -y locales RUN locale-gen ja_JP.UTF-8 RUN localedef -f UTF-8 -i ja_JP ja_JP ENV LANG ja_JP.UTF-8 ENV TZ Asia/Tokyo RUN yarn global add yo@3.0.0 RUN yarn global add generator-hubot@1.1.0 RUN yarn global add coffeescript@1.12.7 RUN useradd praise-bot -m USER praise-bot WORKDIR /home/praise-bot version: '3' services: app: build: . tty: true volumes: - .:/home/praise-bot モジュール 'use strict'; /** * slack上で欲しい言葉をくれるボット */ const praiseWords = [ {index:1, msg:"今日はよくがんばりました。すごいです。"}, {index:2, msg:"生きていてくれて嬉しいです。ありがとう。"}, {index:3, msg:"あなたの頑張りは私だけが知っています。お疲れ様でした。"}, {index:4, msg:"あなたの優しさはきっと届いています。"}, {index:5, msg:"今のあなたはイケてます。すごくいい感じです。"}, {index:6, msg:"たくさんがんばりましたね。今日もお疲れ様でした。"}, ]; /** * インデックスの最大値にプラス1をする * @returns 最大値 */ function maxIndex(){ let newInt = 1; //配列のindexのみ取得 const maxArray = praiseWords.map(m => m.index); //最大値 for(let max of maxArray){ if(max >= newInt){ newInt = max + 1; } } return newInt; } /** * メッセージの追加 * @param {integer} index * @param {string} msg メンションするメッセージ */ function addPraise(msg){ //indexの最大値取得 const newIndex = maxIndex(); praiseWords.push({index:newIndex, msg:msg}); } /** * 指定したインデックスでメッセージの削除 * @param {integer} index */ function delPraise(index){ //指定されたインデックスの存在チェック const indx = praiseWords.findIndex(p => p.index === index); if(indx !== -1){ praiseWords.splice(indx, 1); } } /** * 一覧を表示 * @returns index : メッセージ */ function listPraise(){ return praiseWords.map(w => w.index + " : " + w.msg); } /** * ランダムにメッセージを返す * @returns {string} メッセージ */ function praise(){ if(praiseWords.length > 0){ //ランダムな添字の取得 const randomIndex = Math.floor(Math.random() * praiseWords.length); return praiseWords[randomIndex].msg; }else{ return "ほめてもらいたい言葉が見つかりませんでした"; } } module.exports = { praise, add:addPraise, del:delPraise, list:listPraise } 'use strict'; // Description // 「ほめて」というとほめてくれるbot // Commands: // ほめて - 登録された言葉をランダムに発言する // もっと メッセージ内容 - ほめて欲しい言葉を登録する // ほめないで index番号 - 指定された番号の言葉を削除する // 見せて - 登録されている言葉を一覧表示 const praise = require('praise-bot'); module.exports = robot =>{ robot.hear(/ほめて/i, msg =>{ const words = praise.praise(); msg.send(words); }); robot.hear(/もっと (.+)/i, msg =>{ const words = msg.match[1].trim(); praise.add(words); msg.send("登録しました。もっとほめますね♪"); }); robot.hear(/ほめないで (.+)/i, msg =>{ const index = msg.match[1].trim(); praise.del(parseInt(index)); msg.send("削除しました。他の言葉でほめますね。"); }); robot.hear(/見せて/i, msg =>{ const words = praise.list().join('\n'); if(words.length === 0){ msg.send("登録されているメッセージがありません。") }else{ msg.send(words); } }); } コンソール上で実行するとき、APIトークンだけではうまくいかなかった HUBOT_SLACK_RTM_CLIENT_OPTS='{"dataStore":"false","useRtmConnect":"true"}' これを一緒に実行すると大丈夫みたい。 HubotのRTM?ちょっと詳しいことはわからないけれど後で調べたい。 最終的に下で実行するとよい。 env HUBOT_SLACK_TOKEN=xoxb-xxxxxxx HUBOT_SLACK_RTM_CLIENT_OPTS='{"dataStore":"false","useRtmConnect":"true"}' bin/hubot --adapter slack まとめ 実際に自分で考えて作って、ようやく全体の流れみたいなものが理解できた。作業量が増えてきてるので難しい。 次はbotの永続化とのことなので、とにかくほめるbotも引き続き改良したい。入力チェックも特にしてない状態なので。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

redux のソースコード を読んでみる

試しに redux を読んでみたので、そのまとめ。 この記事では、react-redux を除いた redux、つまり createStore を使った getState・dispatch 操作を、実際の redux のコードを読んで理解してみます。 対象読者 redux の使い方を知っている人 redux まとめ まずは、簡単な概要を説明します。 redux は 状態管理 のライブラリ。 React の State状態管理 のバケツリレーを改善するために、大きな State を作っておき そこにデータアクセス(react-redux)することで状態管理をする。ここでは「大きな State を作る」部分である redux だけを解説します(暇があれば、後半の「データアクセス」部分をしている react-redux も読んでみます)。 React で呼び出している redux は、reducer をまとめる combineReducers と createStore です(細かい redux の使い方は省きます)。 index.js import {createStore} from "redux" import {reducers} from "reducers" import {someAction} from "actions" ・・・省略 store = createStore(reducers) store.dispatch(someAction) console.log(store.getState()) reducers/index.js import {combineReducers} from "redux" import {hoge} from "./hoge" export const reducers = combineReducers({hoge: hoge}) reducers/hoge.js export const hoge = (action, state) => { switch(action.type) { case: "HOGE": /* do some stuffs */ return new_state default: return state } } combineReducers では、まずは combineReducers から読んでみましょう。 ソースコードは、 redux:src/combineReducers.ts から。 src/combineReducers.ts function getUnexpectedStateShapeWarningMessage(...){ /* stateの型を確認する関数 */ } function assertReducerShape(...){ /* reducerの型を確認する関数 */ } export default function combineReducers(reducers: ReducersMapObject) { const reducerKeys = Object.keys(reducers) const finalReducers: ReducersMapObject = {} for (let i = 0; i < reducerKeys.length; i++) { /* 関数なら、finalReducers に reducer を登録 */ const key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers)  ・・・reducer の型チェック return function combination(state, action){ let hasChanged = false const nextState: StateFromReducersMapObject<typeof reducers> = {} for (let i = 0; i < finalReducerKeys.length; i++) { /* reducer を 実行 */ /* 上の例で言えば、hoge の switch文 */ const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) /* state が 更新された */ nextState[key] = nextStateForKey /* 更新されたか確認 hasChanged */ } return hasChanged ? nextState : state } } ここでは、上記の reducers/hoge.js export const hoge = (action, state) => { switch(action.type) { case: "HOGE": /* do some stuffs */ return new_state default: return state } } などの reducer を受け取って、まずは function の reducer のみのオブジェクト finalReducers を作っています。 そして、後で出てきますが 実際に 引数(action, state) で呼び出された場合(クロージャーになっています) に、finalReducers の1つ1つの reducer に 引数(action, state) を渡し、reducer で state を更新します(上の hoge.js のような形で)。 createStore 次に、createStore を読んでみましょう。 ソースコードは、src/createStore.ts から。 createStore では、主に getState・subscribe・dispatch・replaceReducer の4つの関数があります。 createStore.ts export default function createStore(reducer, preloadedState? , enhancer?) { ・・・省略 let currentReducer = reducer let currentState = preloadedState let currentListeners: (() => void)[] | null = [] let nextListeners = currentListeners let isDispatching = false ・・・省略 function getState(){ ・・・省略 } function subscribe(listener){ ・・・省略 return function unsubscribe(){ ・・・省略 } } function dispatch(action){ ・・・省略 } function replaceReducer(nextRuducer){ } } ここで出てくる getState() と dispatch(action) は 使ったことがあるかもしれませんね。react-redux がないと、この 2つの関数を使って「State の取得」と「State の更新」をしていましたね。 他の subscribe と replaceReducer は、それぞれ「変更があるたびに呼び出す関数の登録」と「reducer の変更」ができます。 今回は、この中の getState と dispatch を見てみましょう。 まずは、getState() createStore.ts function getState(){ ・・・省略 return currentState } これは簡単ですね。単に currentState を返しているだけです。 では次に dispatch を見てみましょう。 createStore.ts let currentReducer = reducer ・・・省略 function dispatch(action){ ・・・型チェック try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } ・・・subscribe() で登録した listener の呼び出し return action } dispatch では、combineReducers した currentReducer でクロージャーされた関数を呼び出しています。 なので前の currentReducer で出てきた src/combineReducers.ts return function combination(state, action){ ・・・省略 for (let i = 0; i < finalReducerKeys.length; i++) { ・・・省略 const nextStateForKey = reducer(previousStateForKey, action) ・・・省略 } return hasChanged ? nextState : state } の関数がそれに当たり、state の更新を行なっています。 他にも applyMiddleware などありますが、大体、内容は以上になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript]forEachに弄ばれた話

はじめに JavaScriptを書いており、配列のループをしようとして弄ばれたときのメモ&ネタ話です。 内容 やりたいこと 最近JavaScriptを業務で使う中で、 配列を処理して配列内の値がtrueならエラーにしたい! ということがあったので、JavaScriptのループ処理についてググることがあった。 やったこと 自分の普段のメイン言語はPHPなので検索ワードは 「JavaScript foreach」 で調べたところ、JavaSccriptのforEach()メソッドがあったので「キタコレ」という感じで実装 具体的な実装イメージは以下の通り。 const hasDummys = [ false, true, false ]; // 配列hasDummyの値にtrueであれば、アラートだす hasDummys.forEach(function(hasDummy){ if (hasDummy) { alert('不正な処理です。'); // (1 return false; } }); // 配列hasDummyの値に問題なければ、ログイン画面に遷移 window.location.href = '/login'; // (2 これで画面遷移前に配列内チェックができ、不正値であった場合は処理を止められる、と思っていた 結果 結論、想定外の挙動でした。 挙動としては、 (1のアラートが発令されるが、処理が止まらずに(2のログイン画面へ遷移処理が実行 という状況。 PHPのforeachであれば処理は止まるはずなのになぜ?といった具合でした。 対策 JavaScriptの他のループ処理を使う 具体的にはfor-ofを使いました。 ※PHPなどのforeach的に使うなら直感的に使いやすい。 コードを以下のように書き変えました。 const hasDummys = [ false, true, false ]; // [ 変更 ] forEachからfor...ofに変更 // 配列hasDummyの値にtrueであれば、アラート for(hasDummy of hasDummys){ // ★ if (hasDummy) { alert('不正な処理です。'); // (1 return false; } } // 配列hasDummyの値に問題なければ、ログイン画面に遷移 window.location.href = '/login'; // (2 実行すると、 値がfalse:問題なくログイン画面へ遷移 値がtrue:アラートがでて、ログイン画面への遷移処理は実行されない という想定通りの挙動をしてくれました。 補足 最初に使ったforEachではfor-ofと違ってづいう挙動をするかというとforEachの場合は与えられた配列の値を一旦すべてループ処理するらしいです 上記の実例で行くと★に処理が来ると 1巡目:値がfalseなのでif文に入らない=何もしない 2巡目:値がtrueなのでif文に入り、アラート発生しreturn falseで何も返さない    =何もしない 3巡目:3巡目の処理に入り、値がfalseなのでif文に入らない=何もしない ↓ そのまま処理を続ける=処理が③に入り、画面遷移がされる といった挙動になります。 一方、for...ofは 1巡目:falseなのでif文に入らない=何もしない 2巡目:trueなのでif文に入り、アラート発生しreturn falseで何も返さない    =何もしない 3巡目:2巡目で処理は止まるので、何も起きない といった挙動になります。 この違いはforEach()とfor...ofの処理の直後にconsole.logを置いて、出力されるか否かを見るとわかりやすいのでは、と思います。 おわりに PHPとJavaScriptで同じforeachでも処理の仕方が違うのかと驚きました。 JavaScriptはループ処理が充実していてfor...inやeveryが用意されているので、選択肢が広がったな、と実感しました。 最後までお読みいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Siemaをスマホ対応 メモ

今回Siemaをスマホ対応させてみたメモになります。 前提として、 Siemaとは JavaScriptによる、画像スライダー https://pawelgrzybek.github.io/siema/ 詳しくは、ほかの人のこれらがわかりやすかったです。 【JavaScript】単体で使える超軽量スライダー[Siema]は初心者の方にオススメです。 https://on-ze.com/archives/6194 超軽量スライダーSiemaが最高なのでお勧めしたい https://suzumenote.com/tech/395 やりたいことは、 表示される画像をPCでは2枚、スマホでは1枚にしたい。 HTMLまたは、CSSまた、公式Siemaのオプションではできない?わからなかったので、Siemaのsiema.min.jsファイルをいじりました。 ただ、JavaScript言語が全く分からない、なので取り敢えず弄ってみましした。 (あまり時間がなかったので、JavaScriptを学ぶのはまたの機会に) 本題の追加コードですが、こちら if(document.body.scrollWidth<=750) this.perPage=1; になります。 意味は ビューポートの幅 (スクロールバーの幅を除く) が750以下なら画像の表示枚数を1にする、です。 ビューポートの幅とは、簡単にブラウザの(スクロールバーの幅を除く)横幅サイズになります。これらのサイトを参考にしました。 Element.clientWidth https://developer.mozilla.org/ja/docs/Web/API/Element/clientWidth JavaScriptでウインドウサイズを取得 https://web-designer.cman.jp/javascript_ref/window/size/ 追加コードを siema.min.jsファイル  { key:"buildSliderFrame", value:function() { /*追加コード*/ if(document.body.scrollWidth<=750) this.perPage=1; ここに入れます。 場所を探すのはkey:"buildSliderFrame"を目印にすると良いと思います。 問題のなぜここの場所に入れるかが、自分でもよくわかってないです。 ここの場所に入れた経緯を書いていきます。 まず、一行で公式コードが書かれているので、{} や ; や , で改行しました。 そしてperPageの変数、が画像の表示数であることが、公式オプションから読み取れました。 なので、この変数を探しました。 (jsファイルを開くのにMery を使いましたが 検索機能 便利でした) その最中にkey:~~があり key:"Next" や key:"prev" など見たことあるものがあり、そこからkey:~~は処理内容にそった文字列だと予測、またfunctionがJavaScriptの関数であることが調べてわかりました。 なので、perPage変数がある関数の最初の行に追加コードを書いてみて、webブラウザで動作確認してみたら、key:"buildSliderFrame",の関数にのみ追加すれば、問題なく動くことがわかりました。 というのが、一連の流れになります。 ちょっとした、わかったこと。 超軽量スライダーSiemaが最高なのでお勧めしたい https://suzumenote.com/tech/395 にて multipleDrag 謎です。とのことが書いてありましたが、 表示枚数を3枚にすると、わかりやすいですが、 multipleDragをfalseにすると三枚をすべて新しい画像にスライドできなくなります。 multipleDragをtrueにすると三枚をすべて新しい画像にスライドできます。 ということです。 ちょっとした、コード。 HTML <script> var mySiema = new Siema({ selector: '.siema', duration: 500, easing: 'ease-out', perPage: 2, startIndex: 0, draggable: true, threshold: 20, loop: true, onInit: () => {}, onChange: () => {}, }); document.querySelector('.js-prev1').addEventListener('click', function() {mySiema.prev()}); document.querySelector('.js-next1').addEventListener('click', function() {mySiema.next()}); setInterval(() => mySiema.next(), 5500); </script><!-- スクロール自動化 --> CSS @media (max-width:767px){ .siema-btn ul{ display:grid; grid-template-columns: 100%; grid-template-rows: 50% 50%; } .prev{ grid-row:2/3; } .next{ grid-row:1/2; } } こちらのCSSはnext,prevボタンの表示順序がPCでは横並び、スマホなら縦並びなら、縦並びの時、prevボタンが上に来てしまうので、nextボタンを上に持ってくるCSSです。 参考にしたもの 「display:grid」の使い方について【備忘録】 https://freemas.org/making/css/display-grid PCでは横並び スマホなら縦並び nextを上に持ってきたのが右。  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DeepL API Freeを使用して翻訳してみる

はじめに 新年早々、Netflexに加入しまして、以前からネットとかで海外ドラマのフレンズが英語の勉強にいいという噂は耳にしていたので、今年は素直にやってみようと思った次第です。 フレンズは1話あたり22分でシーズン1〜10まであり、全部で236話あるそうです。1日1話ずつ観ても8ヶ月くらいかかります。 フレンズの脚本のスクリプトも見ることができます。ただし、日本語訳はついていません。 DeepL API DeepLは、口語に近い文章に訳してくれるということで、フレンズを訳すのにちょうど良いかなと思って、ちょっとしたアプリケーションを作る上でDeepL APIを試してみたくなりました。 準備 1.開発者向けのアカウントを登録 DeepLAPIを使うためにDeepLProアカウントを作ります。 https://www.deepl.com/pro#developer 姓名メールアドレスやパスワード、住所などを入力し、登録します。 このサイトから「DeepL API Free」を選択して手順に沿って登録します。 ※クレジットカードを入力する項目がありますが、Freeプランであれば課金はされません。 ※DeepL API Freeだと50万文字/月が上限のようです。(執筆時点) 2.登録後のAPI認証キーをコピーしておく 登録完了後に認証キーが表示されるのでコピーしておきます。 「XXX~XXXX:fx」というキーです。 マイページ->プランでも見ることができます。 Python Google Colaboratoryで簡単に使用してみます。 API KEYを入力のところは、API認証キーに書き換えてください。 import requests # NOTE: put API KEY API_KEY:str = 'API KEYを入力' txt = 'This is a pen.' params = { "auth_key": API_KEY, "text": txt, "source_lang": 'EN', "target_lang": 'JA' } request = requests.post("https://api-free.deepl.com/v2/translate", data=params) result = request.json() print(result["translations"][0]["text"]) 結果 これはペンです。 JavaScript API KEYを入力のところは、API認証キーに書き換えてください。 <!DOCTYPE html> <html lang = "ja"> <head> <meta charset = "utf-8"> <title>JavaScript</title> <style> textarea { width: 500px; height:250px; } </style> </head> <body> <script> const API_KEY = 'API KEYを入力'; const API_URL = 'https://api-free.deepl.com/v2/translate'; function output() { const entext = document.getElementById("entext").value; let content = encodeURI('auth_key=' + API_KEY + '&text=' + entext + '&source_lang=EN&target_lang=JA'); let url = API_URL + '?' + content; fetch(url) .then(function(response) { if (response.ok) { return response.json(); } else { throw new Error("Could not reach the API: " + response.statusText); } }).then(function(data) { document.getElementById("jatext").value = data["translations"][0]["text"]; }).catch(function(error) { document.getElementById("jatext").value = error.message; }); }; </script> <textarea id="entext" placeholder="英語を入力してください"></textarea> <br> <input type="button" value="翻 訳" onclick="output()" /> <br> <textarea id="jatext"></textarea> </body> </html> 結果 試しにフレンズの一部を翻訳してみました。 English C’mon Daddy, listen to me! It’s like, it’s like, all of my life, everyone has always told me, ‘You’re a shoe! You’re a shoe, you’re a shoe, you’re a shoe!’. And today I just stopped and I said, ‘What if I don’t wanna be a shoe? What if I wanna be a- a purse, y’know? Or a- or a hat! No, I’m not saying I want you to buy me a hat, I’m saying I am a ha- It’s a metaphor, Daddy! Japanese パパ、聞いてよ!これまでの人生で、誰もが私に言ってきたのは、『お前は靴だ!』ということでした。あなたは靴だ!あなたは靴だ!あなたは靴だ!』ってね。そして今日、私はただ立ち止まり、こう言ったのです「もし私が靴になりたくないとしたら?もし私が靴になりたくなかったら、どうしよう?あるいは......あるいは帽子!』と。帽子を買って欲しいと言っているのではなくて、私は帽子だと言っているの......それは比喩よ、パパ!」 注意点 まとめて翻訳させようとすると、翻訳できなかったりします。 また、話者のところが「モニカです。」みたいになるので、本文のみを訳させるようにした方がいい。 嵌った点 最初、Python同様にPOSTでやろうとしたのですが、403エラー(type:cors)となり解決できず、諦めました。 サーバー側(node.js)ならPOSTできると思われますが、クライアント側はダメなのかな感じました。 Google App Script(GAS) API KEYを入力のところは、API認証キーに書き換えてください。 セルに例として、=DeepL(A2)とします。 function DeepL(text, ) { const API_KEY = 'API KEYを入力'; const API_URL = 'https://api-free.deepl.com/v2/translate'; let content = encodeURI('auth_key=' + API_KEY + '&text=' + entext + '&source_lang=EN&target_lang=JA'); let url = API_URL + '?' + content; let response = UrlFetchApp.fetch(url); let json = response.getContentText(); let data = JSON.parse(json); return data["translations"][0]["text"]; } 結果 注意点 Google App Scriptの仕様で、スプレッドシートを読み込むたびに関数が実行されてしまうようなので、同じファイル内で大量に関数を使用すると月額上限に引っかかりやすくなるので注意しましょう。 参照 DeeplをGoogleスプレッドシートと統合しますか? GoogleスプレッドシートでDeepL翻訳関数を作る 最後に フレンズのスクリプトを訳す専用のツールを作ろうと思ってます。 あと、Chrome拡張の「Language Reactor」を使うとフレンズを英語字幕・日本語字幕を同時表示して視聴できるので便利です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スムーススクロールさせる方法

今回は、jQueryを利用したスムーススクロールさせる方法を紹介していきます。 スムーススロールとは ヘッダーのメニューやトップへ戻るボタンなどをクリックした際に、ページ内のリンク先へ瞬時に飛ぶのではなく、ゆったり飛んでいく挙動です。 jQueryに記述 javascript // #から始まるURLがクリックされた時 jQuery('a[href^="#"]').click(function() { // 移動速度を指定(ミリ秒) let speed = 300; // hrefで指定されたidを取得 let id = jQuery(this).attr("href"); // idの値が#のみだったらターゲットをhtmlタグにしてトップへ戻るようにする let target = jQuery("#" == id ? "html" : id); // ページのトップを基準にターゲットの位置を取得 let position = jQuery(target).offset().top; // ターゲットの位置までspeedの速度で移動 jQuery("html, body").animate( { scrollTop: position - $( '#js-header' ).outerHeight() //htmlのheaderにid="js-header" }, speed ); return false; }); //wow new WOW().init() 上記の記述によって、スムーススクロールを実現できます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webview.hostObjects が async で失敗するのをなんとかする ver.2

message を汚染せず匿名関数でやりくりするタイプ JavaScript addEventListener('ExeLoaded',e=>{ var url = `https://script.google.com/macros/s/SampleDeployId/exec`; var text = `SampleText: Fixed newly exposed boat not showing up in the boat yard menu Changed contraptions to drop resources and inventory objects (crates) to drop resources even when killed with fire. Previously anything killed by fire would drop nothing.` chrome.webview.hostObjects.exe.PostError(url,text).then(r=>{ console.log(r)// null、async失敗 }) _exe.post(url,text).then(r=>{ console.log(r)// async成功 }) }) var _exe = new class { constructor(){ addEventListener('HostObjectAdded',e=>{ this.Promise.data = {} this.Promise.id = 0 e.value.methods.forEach(s=>this.addMethod(e.value.name,...s.split(','))) dispatchEvent(new Event('ExeLoaded')) }) } addMethod(h,m,attr){ var s = m[0].toLowerCase() + m.slice(1); if(this[s]) return if(attr=='async'){ this[s] = (...a)=>new this.Promise.Script(h,m,...a) }else{ this[s] = (...a)=>new this.Promise.Json(h,m,...a) } } } _exe.Promise = class{ constructor(){this.hook = ()=>{}} then(resolve){ this.hook = resolve return this } } _exe.Promise.Script = class extends _exe.Promise{ constructor(h,s,...a){super() var id = ++_exe.Promise.id _exe.Promise.data[id] = this chrome.webview.hostObjects[h][s](` (function(v,i){ v[i].hook(/*value*/) delete v[i] })(_exe.Promise.data,${id}) `,...a) } } _exe.Promise.Json = class extends _exe.Promise{ constructor(h,s,...a){super() chrome.webview.hostObjects[h][s](...a).then(r=>{ try{this.hook(JSON.parse(r))} catch(e){this.hook(r)} }) } } HostObject.cs public class HostObject { protected CoreWebView2 core = null!; public HostObject(WebView2 view) { view.NavigationCompleted+=(s,e) => { core = view.CoreWebView2; core.AddHostObjectToScript("exe",this); IIFE(@$" var e = new Event('HostObjectAdded') e.value = {{ name:'exe', methods:{JsonSerializer.Serialize(GetMethods())}, }} dispatchEvent(e) "); }; } //nullが返る public async Task<string> PostError(string url,string data) { var content = new StringContent(data,Encoding.UTF8); using(var client = new HttpClient()) { var response = await client.PostAsync(url,content); var s = await response.Content.ReadAsStringAsync(); return s; } } //手動でResolve public async void Post(string script,string url,string data) { var content = new StringContent(data, Encoding.UTF8); using (var client = new HttpClient()){ var response = await client.PostAsync(url, content); var s = await response.Content.ReadAsStringAsync(); Resolve(script,s); } } protected async void Resolve(string script,object v) { var s = script.Replace("/*value*/",JsonSerializer.Serialize(v)); var r = await core.ExecuteScriptAsync(s); } protected async void IIFE(string script) {//即時実行関数 var r = await core.ExecuteScriptAsync($"(function(){{{script}}})()"); } public string[] GetMethods(){ var objectMethods = typeof(Object).GetMethods(); var methods = new List<string>(); Type async = typeof(AsyncStateMachineAttribute); foreach(MethodInfo m in GetType().GetMethods()) { if(!Array.Exists(objectMethods,v => { return v.Name==m.Name; })){ if(m.GetCustomAttribute(async)==null) { methods.Add(m.Name); } else { methods.Add($"{m.Name},async"); } } } return methods.ToArray(); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】ES5とES6において、親クラス継承の違い

ES5においての継承 <script> //親 function Phone(brand, price) { this.brand = brand; this.price = price; } Phone.prototype.call = function () { console.log(" give XXX a call") } //子 function smartPhone(brand, price, color, size) { Phone.call(this, brand, price); this.color = color; this.size = size; } smartPhone.prototype = new Phone; //親を呼び出す smartPhone.prototype.constructor = smartPhone; //子 メソッドの追加 smartPhone.prototype.photo = function () { console.log("take photo") } smartPhone.prototype.mail = function () { console.log("receive email") } let iphone = new smartPhone('iphone', 100, 'white', '6.7inch'); console.log(iphone); //smartPhone{brand: 'iphone', price: 100, color: 'black',size: '6.7inch'} </script> ES6での継承 <script> //親 class Phone { constructor(brand, price) { this.brand = brand; this.price = price; } call() { console.log(" give XXX a call") } } //子 *** extendsを使えば継承できる *** class smartPhone extends Phone { constructor(brand, price, color, size) { supre(brand, price); //イコール Phone.call(this,brand,price) this.color = color; this.size = size; } photo() { console.log("take photo") } mail() { console.log("receive email") } } let iphone = new smartPhone('iphone13', 200, 'pink', '6.1inch') console.log(iphone) //smartPhone{brand: 'iphone13', price: 200, color: 'pink',size: '6.1inch'} iphone.call()//" give XXX a call" iphone.photo() //"take photo" iphone.mail() //"receive email" </script> 違い ES6とES5と比べて、親クラスを継承するときのコードが明らかに簡潔ですね。 classとextendsを使えば、親クラスを継承できるので、 PHP、Javaなどの構造と似ているように思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む