20210907のJavaScriptに関する記事は22件です。

JavaScriptのvar,let,constの違いを理解する。ついでにスコープも理解する。

概要 変数宣言について、 「変数はvarかlet、定数はconst。」 「varはもう使うな。」 とか聞いたことあるけど、詳細を理解していないので調べてみました。 違いを理解するための前提知識としてスコープの理解が必要だったので、それも調べてみました。 変数宣言 変数の宣言とは、変数名を登録し値を格納するための領域をメモリに確保することです。 JavaScriptでは変数を使う際、変数の宣言が推奨されています。(※宣言しなくても動くが注意が必要。詳細はおまけ1で) 変数宣言をするための命令として以下3つがあります。 letとconst はES2015から登場した新しい命令です。 var let const //宣言 var test; console.log(test); //->代入していないのでundefined //代入 test = 1; //宣言時に代入してもOK(初期化という) var test2 = 2; //宣言せず代入することもできる test3 = 3; 3つの違いは? まとめるとこんなかんじです。 var let const 再代入 〇 〇 × 再宣言 〇 × × ブロック内で変数を宣言した場合のスコープ ブロックではない ブロック ブロック 変数の巻き上げ undefined ReferenceError ReferenceError それぞれ詳しくみていきましょう。 再代入 再代入とは、宣言した変数名に対して別の値を代入することです。 var,letは再代入できますが、constはできないです。 「定数の宣言ではconstを使う」とよく聞きますよね。これはconstが再代入を許可しないからです。 //OK var a = 'hoge'; a = 'fuga'; //OK let b = 'hoge'; b = 'fuga'; //NG const c = 'hoge'; c = 'fuga'; // ->TypeError: Assignment to constant variable. 再宣言 再宣言とは、既に宣言している変数名で再度変数の宣言を行うことです。 varは再宣言ができますが、let,constはできないです。 再宣言できてしまうと、意図せず同じ変数名で宣言してしまったことで不具合につながる可能性があります。 varを使う場合要注意ですね。 //OK var a = 'hoge'; var a = 'fuga'; //NG let b = 'hoge'; let b = 'fuga'; ->Identifier 'b' has already been declared //NG const c = 'hoge'; const c = 'fuga'; ->Identifier 'c' has already been declared ブロック内で変数を宣言した場合のスコープ ブロック内で変数の宣言を行った場合、var,let,constのどれを使ったかで変数のスコープが変わります。 varはブロックの外側のスコープになり、(関数内で宣言したならローカルスコープ、トップレベルで宣言したならグローバルスコープ)、 let,constはブロックスコープになります。 詳細にいく前に、スコープについて整理します。 スコープとは? スコープとは、変数が参照できる範囲を決めるの概念のことです。 JavaScriptにおいてスコープには以下があります。 グローバルスコープ ローカルスコープ ブロックスコープ グローバルスコープ スクリプト全体から参照が可能。グローバルスコープを持つ変数をグローバル変数という。 ローカルスコープ(関数スコープ) 宣言された関数内でのみ参照できる。(本によっては関数スコープ書いてる場合もあり。)ローカルスコープを持つ変数をローカル変数という。 function test(arg) { // ローカル変数を宣言 var a = 'local'; console.log(a); // -> local // 関数の仮引数もローカルスコープをもつ console.log(arg); // -> hoge } test('hoge'); // 関数の外側からはローカル変数や仮引数を参照できない console.log(a); // -> Uncaught ReferenceError: a is not defined console.log(arg); // -> Uncaught ReferenceError: arg is not defined ブロックスコープ ES2015から追加されたスコープです。ブロック({})の範囲のみ参照できます。 例)if文やループ function test() { if (true){ // ブロック内で変数を宣言(ブロックスコープ) let a = 'test'; } // ブロックスコープの外側からは参照できない。 console.log(a); // ->ReferenceError: a is not defined } test(); var,let,constでブロックスコープの扱いが違う スコープについて整理したところで、var,let,constの違いに話を戻します。 ブロックスコープはES2015から追加された概念なので、let,constは対応していますが、ES2015以前からあったvarは対応していないです。 function test() { if (true){ // ブロック内で変数を宣言 var var_test = 'var'; // ローカルスコープ let let_test = 'let'; // ブロックスコープ const const_test = 'const'; // ブロックスコープ } console.log(var_test); // ローカルスコープなので関数内から参照できる console.log(let_test); // ->ReferenceError: let_test is not defined ブロックの外から参照できない console.log(const_test); // ->ReferenceError: const_test is not defined ブロックの外から参照できない } test(); 変数の巻き上げ ローカルスコープの説明の中で以下のように記述しました。 宣言された関数内でのみ参照できる。 つまり、宣言された関数内であれば、宣言より前の行から参照できる ということです。どゆこと?? function test() { console.log(a); // -> ??? var a = 'test'; } test(); console.log(a); の結果はどうなるでしょうか?まだ宣言されてないのでReferenceErrorが出そうに思えますが... . . . 正解はundefinedです。 変数aはローカルスコープをもっており、関数内全体で参照可能です。2行目console.log(a)の時点では変数aの宣言だけがされており、値の代入はされていないため、undefinedとなります。 以下のように書いた場合と同じになります。 function test() { var a; console.log(a); // -> undefined a = 'test'; } test(); このように、変数の宣言がスコープの先頭で行われたようになる挙動を「巻き上げ」または「ホイスティング」といいます。 ここまでの例では宣言にvarを使ってきましたが、let const だとどうなるでしょうか? function test() { console.log(a); // -> ReferenceError: Cannot access 'a' before initialization let a = 'test'; } test(); エラーになります。constでも同様にエラーになります。 let,constでも巻き上げは起こっていますが、値が代入される前に参照しようとするとReferenceErrorになります。 varを使うときは要注意です。varを使うなら、変数の宣言はスコープの先頭で行うべきですね。 まとめ 再度、それぞれの違いをまとめます。 var let const 再代入 〇 〇 × 再宣言 〇 × × ブロック内で変数を宣言した場合のスコープ ブロックではない ブロック ブロック 変数の巻き上げ undefined ReferenceError ReferenceError おまけ1 ~変数宣言しなかったらどうなる?~ 以下のコードでは、関数内の変数aは宣言せず、いきなり代入しています。 宣言しなくても動くので、これ自体は問題ないです。 では、console.log(a)の結果はどうなるでしょうか? var a = 'gloval'; function test(){ a = 'local'; } test(); console.log(a); . . . グローバル変数のaを参照しているので、glovalと言いたいところですが、結果はlocalです。 var let const を使って宣言しなかった変数はグローバル変数とみなされるからです。 var a = 'gloval'; function test(){ a = 'local'; // グローバル変数aに再代入 } test(); console.log(a); // 再代入後のaを参照しているので、結果はlocalになる 「宣言しなくても動くんだから、しなくてもいいや」とか思ってると、意図せずグローバル変数を上書きしてしまいかねません。 原則変数宣言は行いましょう。 おまけ2 ~switch文でletを使うときは気を付けよう~ ブロック内でletで変数宣言した場合、スコープはブロックスコープになる、という話をしました。 ここで注意したいのがswitch文をつかった条件分岐です。 まず、if文を使ってこのように書いた場合、エラーにはなりません。 変数bはブロックスコープを持つので、各条件のブロック内でのみ有効となるため、let b = 2; は変数の再宣言になりません。 let a = 1; // ここから if (a === 1){ let b = 1; } // ここまでで1つのブロック else if (a === 2){ let b = 2; } else{ let b = null; } これをswitch文を使って以下のように書くと、エラーになります。 switch文全体で1つのブロックになるので、let b = 2;は変数の再宣言になります。 let a = 1; // ここから switch(a){ case 1: let b = 1; break case 2: let b = 2; // -> SyntaxError: Identifier 'b' has already been declared break default: let b = null break } // ここまでで1つのブロック switch文を使うなら、以下のように、switch文のブロックの外で変数bを宣言すればOKです。 let a = 1; // ブロック外で宣言 let b; switch(a){ case 1: b = 1; break case 2: b = 2; break default: b = null break }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sliceとspliceの違いを簡単にまとめる。

はじめに 文字の並びが似ているけれど使い勝手は全く異なる、sliceとspliceの違いについてまとめます。 sliceとspliceの違い slice 元の配列の値を変更しない。 取得する値のstartとendの位置を指定するのみで値の挿入などはできない。 splice 元の配列の値を変更する。 取得する値の範囲を決めて、別の値を追加したり削除したりできる。 sliceが配列を参照のみするのに対して、spliceは積極的に配列の値を変化させたいときに使えます。 sliceの使用例 sliceはカッコ内に1つもしくは2つの数字を取ることができます。 最初の数字はスタート位置を、2つ目の数字は終了位置を示します。 仮に数字が1つの場合は指定したスタート位置から最後の配列まで取得します。 const numArray = [1, 2, 3, 4, 5, 6, 7, 8]; const list = numArray.slice(0, 2); //出力 [1, 2] console.log(list); //出力 [1, 2, 3, 4, 5, 6, 7, 8] //元の配列は変化していない。 console.log(numArray); spliceの使用例 spliceの場合は、1つ目がスタート位置、2つ目がスタート位置から何個目までを取り出すかを示します。 そして3つ目以降に値がセットされた場合、スタート位置の後にそれらの値を追加します。 このように元の配列の値を変化させるメソッドを破壊的メソッドと呼びます。 const numArray = [1, 2, 3, 4, 5, 6, 7, 8]; const list = numArray.splice(0, 3, "one", "two", "three"); //出力 [1, 2, 3] console.log(list); //出力 ["one", "two", "three", 4, 5, 6, 7, 8] //元の配列が変化している。 console.log(numArray); さいごに 使い勝手が良いのは元の配列を破壊せずに配列内の任意の値を持ってこれるsliceだと思います。 しかし、前から2つずつ塊として配列から取ってきたい場合などはspliceの方が便利な場合もあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.js超入門ノート10(SequelizeでのCRUD編)

レコードの新規作成 以下のファイルを追加します。 views/users/add.ejs <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="content-type" content="text/html"> <title><%= title %></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous"> <link rel="stylesheet" href="/stylesheets/style.css" /> </head> <body class="container"> <header> <h1 class="display-4"> <%= title %> </h1> </header> <div role="main"> <form action="/users/add" method="post"> <div class="form-group"> <label for="name">NAME</label> <input type="text" name="name" id="name" class="form-control"> </div> <div class="form-group"> <label for="pass">PASSWORD</label> <input type="password" name="pass" id="pass" class="form-control"> </div> <div class="form-group"> <label for="mail">MAIL</label> <td><input type="text" name="mail" id="mail" class="form-control"> </div> <div class="form-group"> <label for="age">AGE</label> <td><input type="number" name="age" id="age" class="form-control"> </div> <input type="submit" value="作成" class="btn btn-primary"> </form> </div> </body> </html> 以下の処理を追記します。 routes/users.js router.get('/add',(req, res, next) => { var data = { title: 'Users/Add' } res.render('users/add', data); }); router.post('/add',(req, res, next) => { db.sequelize.sync() .then(() => db.User.create({ name: req.body.name, pass: req.body.pass, mail: req.body.mail, age: req.body.age })) .then(usr => { res.redirect('/users'); }); }); レコードの更新 以下のファイルを追加します。 views/users/edit.ejs <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="content-type" content="text/html"> <title><%= title %></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous"> <link rel="stylesheet" href="/stylesheets/style.css" /> </head> <body class="container"> <header> <h1 class="display-4"> <%= title %> </h1> </header> <div role="main"> <form action="/users/edit" method="post"> <input type="hidden" name="id" value="<%= form.id %>"> <div class="form-group"> <label for="name">NAME</label> <input type="text" name="name" id="name" class="form-control" value="<%= form.name %>"> </div> <div class="form-group"> <label for="pass">PASSWORD</label> <input type="text" name="pass" id="pass" class="form-control" value="<%= form.pass %>"> </div> <div class="form-group"> <label for="mail">MAIL</label> <td><input type="text" name="mail" id="mail" class="form-control" value="<%= form.mail %>"> </div> <div class="form-group"> <label for="age">AGE</label> <td><input type="number" name="age" id="age" class="form-control" value="<%= form.age %>"> </div> <input type="submit" value="更新" class="btn btn-primary"> </form> </div> </body> </html> 以下の処理を追記します。 routes/users.js router.get('/edit',(req, res, next) => { db.User.findByPk(req.query.id) .then(usr => { var data = { title: 'Users/Edit', form: usr } res.render('users/edit', data); }); }); router.post('/edit',(req, res, next) => { db.sequelize.sync() .then(() => db.User.update({ name: req.body.name, pass: req.body.pass, mail: req.body.mail, age: req.body.age }, { where:{id:req.body.id} })) .then(usr => { res.redirect('/users'); }); }); モデルを書き換えて更新 以下のように修正します。 routes/users.js router.post('/edit',(req, res, next) => { db.User.findByPk(req.body.id) .then(usr => { usr.name = req.body.name; usr.pass = req.body.pass; usr.mail = req.body.mail; usr.age = req.body.age; usr.save().then(() => res.redirect('/users')); }); }); 処理の結果は同じです。 レコードの削除 以下のファイルを追加します。 views/users/delete.ejs <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="content-type" content="text/html"> <title><%= title %></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" crossorigin="anonymous"> <link rel="stylesheet" href="/stylesheets/style.css" /> </head> <body class="container"> <header> <h1 class="display-4"> <%= title %> </h1> </header> <div role="main"> <table class="table"> <tr> <th>NAME</th> <td><%= form.name %></td> </tr> <tr> <th>PASSWORD</th> <td><%= form.pass %></td> </tr> <tr> <th>MAIL</th> <td><%= form.mail %></td> </tr> <tr> <th>AGE</th> <td><%= form.age %></td> </tr> <tr> <th></th> <td></td> </tr> </table> <form action="/users/delete" method="post"> <input type="hidden" name="id" value="<%= form.id %>"> <input type="submit" value="削除" class="btn btn-primary"> </form> </div> </body> </html> 以下の処理を追記します。 routes/users.js router.get('/delete',(req, res, next) => { db.User.findByPk(req.query.id) .then(usr => { var data = { title: 'Users/Delete', form: usr } res.render('users/delete', data); }); }); router.post('/delete',(req, res, next) => { db.sequelize.sync() .then(() => db.User.destroy({ where:{id:req.body.id} })) .then(usr => { res.redirect('/users'); }); }); モデルを取り出して削除する 以下のように修正します。 routes/users.js router.post('/delete',(req, res, next) => { db.User.findByPk(req.body.id) .then(usr => { usr.destroy().then(() => res.redirect('/users')); }); }); 処理の結果は同じです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】オブジェクト(連想配列)をObject.fromEntries()でmapする

はじめに オブジェクト(連想配列)をmapしたい場面があって調べたところObject.fromEntries()に出会った。 実際に動作を確認しながら使ってみたのでそのメモ。 簡単なサンプル シンプルなオブジェクトをmapしてみる。 const obj = { a: 1, b: 2, c: 3 } const convertedObj = Object.fromEntries( Object.entries(obj).map(([key, value]) => { console.log([key, value * 2]) // コンソール出力 // 1回目: ["a", 2] // 2回目: ["b", 4] // 3回目: ["c", 6] return [key, value * 2] }), ) console.log(convertedObj) // コンソール出力 // {a: 2, b: 4, c: 6} なるほど、Object.fromEntries()の返り値がしっかり連想配列で返ってきてる。 今回実装した内容 想定場面 { a: 数値、 b: "数値文字列", c: "日時(文字列)" }見たいな感じの配列↓↓をAPIからレスポンスで受け取って const objArr = [ { a: 10, b: "10", c: "2021-09-01T06:38:19.274406Z" }, { a: 20, b: "20", c: "2021-09-02T06:38:19.274406Z" }, { a: 30, b: "30", c: "2021-09-03T06:38:19.274406Z" }, { a: 40, b: "40", c: "2021-09-04T06:38:19.274406Z" }, { a: 50, b: "50", c: "2021-09-05T06:38:19.274406Z" }, ]; a: 数値はそのままnumber型 b: "数値文字列"はnumber型にキャスト c: "日時"はそのままstring型 こんな感じに↓↓整形したい場面があった。(普通はフロント側でこんな苦しい実装することも滅多にないと思いますが、、) // 整形後(期待する結果) [ {a: 10, b: 10, c: "2021-09-01T06:38:19.274406Z"}, {a: 20, b: 20, c: "2021-09-02T06:38:19.274406Z"}, {a: 30, b: 30, c: "2021-09-03T06:38:19.274406Z"}, {a: 40, b: 40, c: "2021-09-04T06:38:19.274406Z"}, {a: 50, b: 50, c: "2021-09-05T06:38:19.274406Z"}, ] 実装 冗長になってしまいました。でも、実現したいことはできた。 const objArr = [ { a: 10, b: "10", c: "2021-09-01T06:38:19.274406Z" }, { a: 20, b: "20", c: "2021-09-02T06:38:19.274406Z" }, { a: 30, b: "30", c: "2021-09-03T06:38:19.274406Z" }, { a: 40, b: "40", c: "2021-09-04T06:38:19.274406Z" }, { a: 50, b: "50", c: "2021-09-05T06:38:19.274406Z" }, ]; const convertedObjArr = objArr.map((obj) => { return Object.fromEntries( Object.entries(obj).map(([key, value]) => { // value を number型にキャストする const castedValue = Number(value); if (Number.isFinite(castedValue)) { console.log([key, castedValue]); // キャストに成功していれば、キャストした値をvalueとして返す return [key, castedValue]; } console.log([key, value]); // キャストに失敗(NaN)すれば、そのままの値をvalueとして返す return [key, value]; }) ); }); console.log(convertedObjArr); コンソールを確認 map内の処理結果 // value を number型にキャストする const castedValue = Number(value); console.log(castedValue); if (Number.isFinite(castedValue)) { console.log([key, castedValue]); // キャストに成功していれば、キャストした値をvalueとして返す return [key, castedValue]; } console.log([key, value]); // キャストに失敗すれば、そのままの値をvalueとして返す return [key, value]; コンソール出力 ["a", 10] ["b", 10] ["c", "2021-09-01T06:38:19.274406Z"] ["a", 20] ["b", 20] ["c", "2021-09-02T06:38:19.274406Z"] ["a", 30] ["b", 30] ["c", "2021-09-03T06:38:19.274406Z"] ["a", 40] ["b", 40] ["c", "2021-09-04T06:38:19.274406Z"] ["a", 50] ["b", 50] ["c", "2021-09-05T06:38:19.274406Z"] 最終的な整形後の結果 console.log(convertedObjArr); コンソール出力 [ {a: 10, b: 10, c: "2021-09-01T06:38:19.274406Z"}, {a: 20, b: 20, c: "2021-09-02T06:38:19.274406Z"}, {a: 30, b: 30, c: "2021-09-03T06:38:19.274406Z"}, {a: 40, b: 40, c: "2021-09-04T06:38:19.274406Z"}, {a: 50, b: 50, c: "2021-09-05T06:38:19.274406Z"}, ] ちゃんと期待する結果が出力されました。 実際の実装ではreturn Object.fromEntries(以下を関数に切り出したものの、やはり冗長になってしまったので、もっと効率的な記述方法などあれば教えてください。 最後に わかりやすい記事に非常に助けられました。 ありがとうございました。 https://hfuji.hatenablog.jp/entry/2019/04/30/223722 https://qiita.com/taku-0728/items/329e0bee1c49b7ce7cd1 https://qiita.com/gp333/items/8bfa34979da64f15035c JavaScript 面白い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期処理を会社組織に例えて説明する。

単純なindex.html <!DOCTYPE html> <html lang="jp"> <head> <meta charset="UTF-8"> <title>タイトル</title> <script src="main.js"> </script> </head> <body> <input type="number" id="suuji" maxlength="4"/> <button onclick="bushoA()";>送信</button> </body> </html> そして、単純なmain.js function bushoA() { var suuji = document.getElementById("suuji").value; var suuji = Number(suuji) // 社員A suuji = suuji + 1; console.log( "shainA " ,suuji); // 社員B suuji = suuji + 2 console.log("shainB",suuji) } 部署Aでの流れです。 社員Aは入力された数値に1を足し 社員Bはそれに2を足すという、単純な流れ作業 部署Aの部長は言いました 「Aくん、これからネットワーク経由で数字を足してもらう事になるから、時間のかかる処理になってもいいように準備しておいてね!」 社員A「わかりました、処理に10秒かけるようにしておきます」 社員Aが以下のように準備しました。 setTimeout(() => { // 社員A suuji = suuji + 1; console.log( "shainA " ,suuji); }, 1000); ところが、社員Bからなにやら報告が・・・ 「なんかAの処理を変えてから結果がおかしいんです」 すると部長 「あ、そうだった。Aは時間のかかる処理をやっているから、その結果を返す前にBが処理しちゃったらおかしくなるわ」 社員B 「どうしましょうか?」 部長 「Aの処理を非同期処理にするわ」 社員B 「は?非同期処理??」 部長 「流れに乗れない処理の宣言の事だよ、Aは成功か失敗か返すから、成功した時にBの処理を行えばいいよ」 「Aくん、非同期にしておいてね」 社員A 「あっ、うっかり忘れていました、Bの処理はthenに記述しておくようにしておきます」 こうしてできたのが以下のコードになります。 function bushoA() { var suuji = document.getElementById("suuji").value; var suuji = Number(suuji) new Promise(resolve=>{ setTimeout( () => { // 社員A suuji = suuji + 1; console.log( "shainA " ,suuji); resolve() }, 1000); }) .then(()=>{ // 社員B suuji = suuji + 2 console.log("shainB",suuji) }) .catch(err => { console.log(err) }) } こうして無事に社員Aの後に、結果を変えずに社員Bの処理ができるようになりました。 次回は「async await」について説明しようと思いますが、なかなか例えが難しいですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React Native】ただ写真ライブラリから写真を取得するだけでなく、加工して表示させる方法

写真ライブラリから写真を取得し、自分の好きなサイズに加工して表示させる ReactNativeで写真ライブラリから写真を取得する方法はいくつかありますが、その中で加工や編集も行えるライブラリを紹介します。 ライブラリのGitHub https://github.com/ivpusic/react-native-image-crop-picker もともとreact-native-image-pickerというライブラリがあり、それを使えば「カメラを起動させるか」「写真ライブラリを開くか」の選択欄も出て、どちらも実装できる便利なものがありましたが、最近のアップデートで選択欄が出なくなったり何かと使いにくなりました。 image-pickerのGitHub https://github.com/react-native-image-picker/react-native-image-picker またこのライブラリで写真を取得してもそのまま表示されるため、実際にアプリに入れてみないと調整が難しいです。 例えば、アイコンを設定する際に写真全体を表示するのではなく、右下の一部分だけを表示したいとなった時に、従来のimage-pickerだと、まず写真ライブラリ使う写真の加工をして、それを保存してから使わないといけません。 そこがimage-crop-pickerを使うことでアプリ側で加工するためのviewを挟んでくれるため、その場で写真の加工を行えます 実際に実装し、両方を比べてみる 実際にimage-pickerとimage-crop-pickerの両方を実装し、比べてみます。 1、ライブラリをインストール react-native-image-crop-picker:yarn add react-native-image-crop-picker react-native-image-picker:yarn add react-native-image-picker このライブラリでは写真ライブラリにアクセスしたり、カメラを起動させるためinfo.plistに設定が必要です。 ios iOSディレクトリ内のinfo.plistに追記してください <key>NSPhotoLibraryUsageDescription</key> <string>${プロジェクト名など} would like to upload photos from your photo gallery</string> <key>NSCameraUsageDescription</key> <string>${プロジェクト名など} requires to access camera for uploading photos to your profile or posts</string> <key>NSPhotoLibraryAddUsageDescription</key> <string>${プロジェクト名など} would like to save photos to your photo gallery</string> <key>NSMicrophoneUsageDescription</key> <string>${プロジェクト名など}requires to access Audio recording to record and uplod videos</string> コマンドラインで (cd ios && pod install)を打ち込む ちなみに()で囲うことで現在のディレクトリから移動せずにpod installしてくれます。 あるあるなのがiosディレクトリにしたままyarn iosをしててエラーになるパターンです。 割と気づかず何でエラーなのか分からなくなってしまいます。 Android AndroidManifest.xmlに以下を追記してください。 <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 2、コーディング プロジェクトを作成し、今回用のファイルを用意します。 ライブラリをインポート import ImagePicker from 'react-native-image-crop-picker'; import { launchCamera, launchImageLibrary } from 'react-native-image-picker'; image-pickerでは写真ライブラリを開きたいときはlaunchImageLibrary、カメラを起動するときはlaunchCameraを使用します。 launchCameraもインポートしている状態ですが、今回は使わないので必要ないです。 余談ですが、アプリなどのアイコンを設定する際に大体カメラを起動するか写真ライブラリから取得するか選択できると思いますが、その場でカメラを起動して撮った写真をアイコンに設定する人なんているんですかね。 僕は一度もカメラを使ったことないし、友人のアイコンを見る限り全員既存の写真を取得して使ってると思います(加工したものをアイコンに使うから)。 image-crop-pickerを実装 このonCropImageをonPressしたタイミングで呼び出せば写真ライブラリから取得できます。 openPickerの中身には現在は画像サイズなど初期の設定しかしてませんが、たくさん種類があるため自分の好みに合わせて色々付け加えてみてください。 https://github.com/ivpusic/react-native-image-crop-picker 取得時にsetImagePath内に格納してますがこれはuseStateで管理している値なので、今はエラーのままで大丈夫です。 const onCropImage = () => { ImagePicker.openPicker({ width: 300, height: 300, cropping: true, }).then(image => { setImagePath(image.path) }); } image-pickerを実装 こちらもoptionsで色々設定できますが今回は説明を割愛します。 https://github.com/react-native-image-picker/react-native-image-picker 表示するためのuriはresponseのassetsの配列内にあるのでそこにある値をuseStateで管理します const options = { mediaType: 'photo', maxWidth: 1000, maxHeight: 1000, quality: 0.8, saveToPhotos: true, }; const choosePhoto = () => { launchImageLibrary(options, (response) => { if (response.didCancel) { console.log('User cancelled image picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else { console.log(response.assets[0].uri) setImagePath(response.assets[0].uri) } }); } 全体を実装 今回アイコンをタップした際に出てくる選択欄(アクションシート)はライブラリを使って実装してますが、別の記事で実装方法を取り上げてるのでそこを参照ください。 https://qiita.com/flutter_daisuki/items/949869756b3d10944b7f import React ,{useState}from 'react'; import {StyleSheet, TouchableOpacity, View,Image} from 'react-native'; import {ActionSheet} from 'react-native-cross-actionsheet'; import Icon from 'react-native-vector-icons/FontAwesome'; import ImagePicker from 'react-native-image-crop-picker'; import { launchCamera, launchImageLibrary } from 'react-native-image-picker'; export const ImageCropPicker = () => { const [imagePath ,setImagePath] = useState('') const onCropImage = () => { ImagePicker.openPicker({ width: 300, height: 300, cropping: true, }).then(image => { setImagePath(image.path) }); } const options = { mediaType: 'photo', maxWidth: 1000, maxHeight: 1000, quality: 0.8, saveToPhotos: true, }; const choosePhoto = () => { launchImageLibrary(options, (response) => { if (response.didCancel) { console.log('User cancelled image picker'); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else { console.log(response.assets[0].uri) setImagePath(response.assets[0].uri) } }); } const onPressAction = () => { return ActionSheet.options({ options: [ {text: 'image-picker', onPress:choosePhoto}, {text: 'image-crop-picker', onPress:onCropImage}, ], cancel: {text: 'キャンセル'}, }); }; return ( <View> <TouchableOpacity onPress={onPressAction}> <View style={styles.iconButton}> {imagePath ? ( <Image style={styles.Photo} source={{uri: imagePath}} /> ) : ( <Icon style={styles.icon} name="user-o" size={70} /> )} </View> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ iconButton: { borderWidth: 1, width: 100, height: 100, borderRadius: 100, marginLeft: 'auto', marginRight: 'auto', }, icon: { marginLeft: 'auto', marginRight: 'auto', marginTop: 'auto', marginBottom: 'auto', }, Photo: { width: 100, height: 100, borderRadius: 50, }, }); コードを解説するとアイコン全体をtouchableOpacityでタップできるようにし、タップした際にアクションシートを出すようにしています。 またアイコンはuseStateのimagePathがある場合はそのまま写真を表示するようにして、ない場合はIconを表示するようにしています。 三項演算子を使ってますが、めちゃくちゃ使う場面が多いのでぜひ覚えてください。 そして、アクションシート内で上をタップするとimage-pickerの処理が、下をタップするとimage-crop-pickerの処理が動くようになってます。 3、実際にどんな感じか確認してみる 初期画面 タップ時 image-pickerを選択時 ライブラリが開き、右上の滝をタップすると 表示されました! ただ、今回は綺麗に映りましたが、もし写真の一部分だけをアイコンに使いたいのにこうされると思うようにアイコンが設定できないです。 image-crop-pickerを選択時 ライブラリが開き、さっきと同じ滝の写真をタップすると。。。 写真編集用のviewが出てきました! 滝を拡大させて、アイコンいっぱい滝にしよう できました! ユーザーのことを考えるとこっちの方がいいですね✨ 皆さんもぜひ使ってみてください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】reduceメソッド

はじめに こんにちは。 JavaScriptのreduceメソッドについてアウトプットしていきます! reduceメソッドとは reduce() メソッドは、配列の各要素に対して (引数で与えられた) reducer 関数を実行して、単一の出力値を生成します。 参照:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce JavaScript const numbers = [1,2,3,4]; const result = numbers.reduce((accu,curr) => { return accu + curr; }) console.log(result); //10 上記の記述では、1回目の処理でaccuには配列の最初の要素の1が入る。currには2が入る。2回目の処理でaccuにはreturnで返された3が入り、currには配列の3が入る。 処理が完了すると、resultから配列の合計10が出力される。 mapメソッドとの違い reduceメソッドと同じように、配列に対して処理ができるmapメソッドについても簡単に説明していきます。 mapメソッドとは map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。 参照:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map JavaScript const numbers = [1,2,3,4]; const result = numbers.map((num) => { return num + 2; }) console.log(result); //[3, 4, 5, 6] 配列の要素全てに+2を実行して、新しい配列を生成できます。 最後に ここまでreduceメソッドについてまとめてみました。 できることが少しずつ増えてきてプログラミングの楽しさが増してきました笑 これからもコツコツ学習していきます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テキストファイルを出力する方法

function downLoadTXT(text, filename) { // TXTをダウンロードする let blob = new Blob([text], {type: "text/plain"}); let link = document.createElement("a"); // aタグのエレメントを作成 link.href = window.URL.createObjectURL(blob); link.download = filename; link.click(); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kintone hack の予選で披露したアプリ「メッセージヨマセール」の作り方

みなさァ~ん、kintone hack ・・・知ってますか??? サムネでやりすぎと言われていますがw こんな作品でkintone hack 予選会に参加してきました。 今回はそんな私が作った、「メッセージヨマセール」の作り方を紹介したいと思います。 アプリの準備 フィールド種類 フィールドコード 備考 ラジオボタン 確認 未,済 (デフォルトは「未」) ドロップダウン 緊急度 緊急, 普通 文字列(複数行) メッセージ 必須 ユーザー選択 宛先 必須 リンク URL Webサイトアドレス ユーザー選択 送り主 デフォルトはログインユーザー JavaScript こちらの記事をこっそりパクいや、参考にさせていただきました? @youtoy さん、ありがとうございます!!! JavaScript / CSSでカスタマイズ こちらを追加しておきます。 GSAP https://cdnjs.cloudflare.com/ajax/libs/gsap/3.5.1/gsap.min.js kintone REST API Client https://unpkg.com/@kintone/rest-api-client@latest/umd/KintoneRestAPIClient.min.js コード 突貫工事気味に作りましたので、動かないところがあるかも? これはメッセージヨマセールアプリ自体のコードなので、 ポータルやスペースで使用したいときはコピペして調整して使ってね。 (() => { "use strict"; // 読み上げ const readJpn = async (msg) => { const u = new SpeechSynthesisUtterance(); u.text = msg; u.lang = "jp-ja"; u.rate = 1.0; speechSynthesis.speak(u); }; // 表示 kintone.events.on(["app.record.index.show"], (event) => { // ポータルに設置するときはアプリIDベタ書きになる const appId = kintone.app.getId(); let count = 0; const sp = kintone.app.getHeaderMenuSpaceElement(); const btn = new Kuc.Button({ text: "レコード作成", type: "submit", }); const i = setInterval(async () => { const client = new KintoneRestAPIClient(); const resGet = await client.record.getRecords({ app: appId, query: `確認 in ("未") and 宛先 in ("${kintone.getLoginUser().code}")`, }); if (resGet.records.length > 0) { const randRange = Math.floor(Math.random() * resGet.records.length); createText(resGet.records[randRange]); } else { clearInterval(i); console.log("流れるメッセージ終了"); } }, 1000); const createText = async (record) => { const div_text = document.createElement("div"); div_text.id = "text" + count; count++; div_text.style.position = "fixed"; div_text.style.whiteSpace = "nowrap"; div_text.style.left = document.documentElement.clientWidth + "px"; const random = Math.round( Math.random() * document.documentElement.clientHeight ); div_text.style.top = random + "px"; div_text.addEventListener("click", async () => { const client = new KintoneRestAPIClient(); const rec = { 確認: { value: "済", }, }; const resPut = await client.record.updateRecord({ app: appId, id: record.$id.value, record: rec, }); }); const a = document.createElement("a"); a.href = record.URL.value; a.target = "_blank"; const str = document.createTextNode(record.メッセージ.value); if (record.緊急度.value === "緊急") { a.style = "color:red; font-weight:bold;font-size:x-large;"; btn.addEventListener("click", { handleEvent: readEvent, arg1: record.メッセージ.value, }); // this の扱い方がよくわからないので function function readEvent(event) { readJpn(this.arg1); event.currentTarget.removeEventListener("click", this); } btn.click(); } else { a.style = "color:black; font-size:x-large;"; } a.appendChild(str); div_text.appendChild(a); document.body.appendChild(div_text); await gsap.to("#" + div_text.id, { duration: 20, x: -1 * (document.documentElement.clientWidth + div_text.clientWidth), }); div_text.parentNode.removeChild(div_text); }; return event; }); })(); まとめというか感想 我ながらしょうもないhackでしたが、予選会で笑ってもらって嬉しかったです。 こんなショウモナイ内容でも怒られないのがいいですね?サイボウズさん心広い! kintone hack 毎年やっているみたいなので、どなたでも、是非、お気軽に挑戦してみてください?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数計測対応カウントダウンタイマー

複数の計測に対応したカウントダウンタイマーです。 動作デモ countdown_timer.html <!DOCTYPE html> <html lang='ja'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width,initial-scale=1'> <title>countdown timer</title> <style> .unit { background-color: #ccd; border-radius: 10px; padding: 8px; margin: 12px; display: inline-block; } .i, .v { display: none; } .p { text-align: center; font-size: 44px; width: 220px; margin-bottom: 8px; font-family: monospace; } .time { margin-bottom: 8px; } .reset, .start { width: 92px; margin: 0; font-size: 18px; } </style> </head> <body> <button class='add'>ADD UNIT</button> <div id='container'> </div> <script> 'use strict'; window.addEventListener('DOMContentLoaded', () => { addUnit(); // 初期unit表示 // containerに対するinputイベント document.getElementById('container').addEventListener('input', e => { const target = e.target, // container内で実際にクリックされた要素 unit = target.parentNode; // クリックされた要素の親unit // time入力操作時の計測時間設定 if(target.classList.contains('time')) { unit.querySelector('.v').value = 0; const t = unit.querySelector('input[type=time]'); t.style.backgroundColor = ''; const p = unit.querySelector('.p'); p.value = getFormat(t.valueAsNumber); p.style.backgroundColor = ''; } }); // containerに対するclickイベント document.getElementById('container').addEventListener('click', e => { const target = e.target, // container内で実際にクリックされた要素 cl = target.classList, // クリックされた要素のclassList unit = target.parentNode, // クリックされた要素の親unit // unit内の各要素 t = unit.querySelector('input[type=time]'), // 計測時間入力 p = unit.querySelector('.p'), // 残時間表示 id = unit.querySelector('.i'), // setTimeoutの戻り値保持 n = unit.querySelector('.v'), // start/stopボタン押下時の時間保持 resetBtn = unit.querySelector('.reset'); // resetボタン // resetボタン押下時 if(cl.contains('reset') && n.value <= 0) { p.value = getFormat(t.valueAsNumber); n.value = 0; p.style.backgroundColor = ''; } // startボタン押下時 else if(cl.contains('start')) { // 停止中の計測開始処理 if(n.value <= 0) { n.value -= -Date.now(); if(t.valueAsNumber === 0) return; countdown(id, n, t); target.textContent = 'STOP'; resetBtn.disabled = true; t.disabled = true; p.style.backgroundColor = 'lightgreen'; } // 計測中の停止処理 else { n.value -= Date.now(); clearTimeout(id.value); target.textContent = 'START'; resetBtn.disabled = false; t.disabled = false; p.style.backgroundColor = ''; } } // 削除ボタン押下時 else if(cl.contains('del')) { // 計測中であればTimeout解除 if(!isNaN(parseInt(id.value))) { clearTimeout(id.value); } // 対象unit削除 unit.remove(); chkUnits(); } }); // unit追加ボタン押下時 document.querySelector('.add').addEventListener('click', () => { addUnit(); }); }); // containerにunit追加 const addUnit = () => { document.getElementById('container').insertAdjacentHTML('beforeend',` <div class='unit'> <input type='text' class='i'> <input type='number' class='v' value='0'> <input type='text' class='p' value='00:10:00' readonly><br> <input type='time' class='time' value='00:10:00' step='1'> <br> <button class='reset'>RESET</button> <button class='start'>START</button> <button class='del'>×</button> </div> `); chkUnits(); } // unitの削除ボタン表示管理 const chkUnits = () => { const units = document.getElementById('container').querySelectorAll('.unit'); // 全unit数が1個の場合は削除ボタン非表示 units[0].querySelector('.del').style.display = units.length === 1 ? 'none' : 'inline'; }; // カウントダウン const countdown = (id, n, t) => { const d = Date.now() - n.value, num = t.valueAsNumber - Math.floor(d / 1000) * 1000 - 1000, p = t.parentNode.querySelector('.p'); // 表示更新 p.value = getFormat(num); // 残時間あり if(num >= 0) { // 残時間の秒の切り替わりタイミングを目標としてtimeout設定 id.value = setTimeout(countdown, 1000 - d % 1000, id, n, t); } // 0に到達 else { p.value = '00:00:00'; p.style.backgroundColor = 'lightcoral'; } }; // 時間表示フォーマット const getFormat = d => new Date(+d).toISOString().slice(11, 19); </script> </body> </html> 関連記事 複数計測対応ストップウォッチ カウントダウンタイマー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コンポーネントとprops

React入門 最近学習を始めたのですが、propsとコンポーネントという概念がReactを触るという点において重要だと感じたのでメモ代わりにまとめました。 コンポーネントとは ・見た目と機能を持つUI ・コンポーネントを組み合わせてページを作る ・大きく2種類のコンポーネントに分かれる  Class Component (クラスコンポーネント)  Functional Component (関数コンポーネント) //今はこっちで書くことが主流 なぜコンポーネントを使うのか ・再利用するため  ✔同じ記述を何度もする必要がない ・コードの見通しをよくするため  ✔1コンポーネント=1ファイル  ✔べつファイルに分けることでコードが読みやすくなる ・変更に強くするため  ✔修正は1箇所だけでOK コンポーネントの基本的な使い方 ・ファイルは大文字 ・子コンポーネントでexport ・親コンポーネントでimport コンポーネントの再利用 ・配列をmap()メソッドで処理することが一般的 ・同じコンポーネントをいくつも呼び出すことができる propsでデータを受け渡す(★) propsとは(★) 親から子にデータを受け渡す際の橋渡し役 Reactでよく使用する App.jsx(親) import Article from "./components/Article"; function App() { return( <div> <Article title={'これはタイトルです'} ←呼び出し側で値を指定する content={'これはコンテンツです'} </div> ); } export default App; components/Article.jsx(子) const Article = (props) => { return( <div> <h2>{props.title} <p>{props.content}</p> </div> ) } export default Article; propsで渡せるデータ ・文字列、数値、真偽値、配列、オブジェクト、日付などなんでもOK ・変数も可能 ・文字列は{}なしでもOK ・propsのデータは{}に記述
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript入門一覧

概要 JavaScriptを学習、理解を深めるため「JavaScript Primer 迷わないための入門書」を読み、 理解した内容等を記載した記事一覧となる。 記事一覧 JavaScript入門(変数と宣言) JavaScript入門(値の評価と表示)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript入門(値の評価と表示)

概要 JavaScriptを学習、理解を深めるため「JavaScript Primer 迷わないための入門書」を読み、 理解した内容等を記載していく。 JavaScript入門一覧に他の記事をまとめています。 値の評価 値の評価とは「入力した値を評価してその結果を返すこと」を示す。 評価とは(引用:Wikipedia) 数学や計算機科学において、変数に関連づけられた値などをもとに関数(関数 (数学)、関数 (プログラミング))などの式・表現が表す値を計算すること。 JavaScriptの実行を確認する 確認方法は以下の2つが挙げられている。 ①ブラウザの開発者コンソール ②HTMLファイルからJavaScriptファイルを読み込む ①開発者コンソールでの確認 今回はGoogle Chromeの開発者コンソールを使用し確認を行う。 JavaScript PrimerではFirefoxで確認する手順が示されている。 PCはMacを使用している前提としています。 開発者コンソールを開く REPLという「コードを評価し、その結果を表示する」機能を使用する。 開発者ツールをcommand + option + jもしくはfn + F12で立ち上げる 開かれたツールメニューから「console」タブを開く 「>」が表示されている箇所がコンソールとなり、こちらに入力していく ブラウザの開発者コンソール上でJavaScriptコードを評価する 例として下記パターンで行う。 数字を直接入力 => 入力した数字が評価され、表示される 文字列を入力 => 入力した文字列が評価され、表示される 変数を定義 => undefinedが表示される(変数宣言は変数名と値を関連づけるだけであるため) 定義した変数を入力 => 「3.」で宣言した変数が評価され、値が表示される ②HTMLファイルからJavaScriptファイルを読み込む 今回は任意場所にtestディレクトリを作成 testディレクトリ内にindex.htmlとindex.jsファイルを作成 ファイルの編集等に使用するエディタはVisual Studio Codeを使用(なんでもOKです) Console APIというデバッグ作業を実行できるようにする機能を使用する ■ファイル作成後の構成 ■各ファイルの内容 index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript入門</title> <script src="index.js"></script> </head> <body> </body> </html> index.js // console.log(出力したい内容)でコンソールに表示できる console.log(123); console.log("test"); const year = 2021; console.log(year); ブラウザの開発者コンソール上で確認する 先ほど作成したindex.htmlをFinderやフォルダから開き、ブラウザで表示する 何も表示されていないページが表示される 今回はコンソールでの表示のため、前項同様に開発者コンソールを開く すると以下のように開発者コンソール上で確認できる 赤枠部には、ファイル名(index.js)、console.logを指定した行数が表示されている
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

送信ボタン/登録ボタンを押した後、ローディングアイコンを表示させる(二重送信防止)

概要 Railsアプリを例として、ローディングアイコンのgifファイルとJSを使って実装します。 実装するのは、「送信ボタンをクリック後、送信ボタンを非表示にし、代わりにローディングアイコンを表示させる」というシンプルなものです。 事前準備 事前準備として、ローディングアイコンのgifファイルを用意し、loading.gifという名前でアプリ内に保存し使えるようにしておきます。 「ローディングアイコン ダウンロード」等でググればフリーのものが見つかるでしょう。 実装 このような一般的なフォームがあるとします。 <%= form_with url: "パス" do |form| %> <%= f.button '登録', class: 'btn' %> <% end %> div要素を以下のように追記します。 <%= form_with url: "パス" do |form| %> <%= f.button '登録', class: 'loading_btn' %> <div class="loading"> <%= image_tag 'loading.gif' %> </div> <% end %> 下記のようなJSを適用させます。 $(function() { $('form').on('submit', function() { $('.loading_btn').hide(); $('.loading').show(); }); }); 以上で完了です。 必要なら、CSSで適当にレイアウトを整えてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Axios】速度改善のため、毎度APIを呼ばないようにする(〇〇秒キャッシュ)

ページに遷移するたびにAPIを呼ぶのは効率が悪いため、ある程度の時間は再レンダリングしてもAPIを呼ばないようにする 独学の時はAPIを呼ぶのは基本一つしかなく、あまり速度が遅いとは感じませんでしたが、 本格的にアプリを作っていくと1ページに4種類のAPIを呼ばないといけないことになります。 その時に毎回全部のAPIを呼んでいると速度が遅くなる恐れがあります。 そこで、毎回呼ぶ必要のないAPIは次にAPIを呼ぶまでの時間を設定し、その間はレンダリングしてもAPIを呼ばないようにします。 実装していく 実際にコーディングしていきます。 1、APIを呼ぶ設定 まずはAPIの呼び出し共通処理を記述します import axios from 'axios'; export const api = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com/' }); api.interceptors.response.use( response => { return response; }, function (error) { switch (error.response.status) { case 400: case 401: console.log('認証エラーです'); case 404: console.log('URL先がないです'); default: } }, ); 次に実際にAPIを呼び出す処理を実装します。 jsonplaceholderの「posts」と「users」の二つを呼び出しています。 そしてここでは検証用に毎秒レンダリングするように処理しています。(setInterval) こうすることで、1秒経つ毎に毎回二つのAPIを呼び出しているようにしています import React, {useEffect, useState} from 'react'; import {StyleSheet, Text, FlatList, SafeAreaView} from 'react-native'; import {api} from '../api/api'; export const ApiResearch = () => { const [count, setCount] = useState(0); const [postsData, setPostsData] = useState([]); const [usersData, setUsersData] = useState([]); const getPostsData = async () => { const res = await api.get('posts'); setPostsData(res.data); }; const getUsersData = async () => { const res = await api.get('users'); setUsersData(res.data); }; useEffect(() => { getPostsData(); getUsersData(); const intervalId = setInterval(() => { setCount(prevCount => prevCount + 1); }, 1000); return () => clearInterval(intervalId); },[count]); return ( <SafeAreaView> <Text>{count}</Text> <Text style={styles.title} >ここからpostデータです</Text> <FlatList data={postsData} renderItem={({item}) => <Text>{item.title}</Text>} keyExtractor={item => item.id.toString()} /> <Text style={styles.title}>ここからuserデータです</Text> <FlatList data={usersData} renderItem={({item}) => <Text>{item.name}</Text>} keyExtractor={item => item.id.toString()} /> </SafeAreaView> ); }; const styles = StyleSheet.create({ title: { fontSize:20, fontWeight:'bold', marginBottom:10 } }); あとはこのApiResearce.jsをApp.jsで呼んでください。 では実際にReactNative Debbugerで確認してみましょう。 画像の通り、1秒毎にpostsとusersが呼び出されてるのがわかると思います。 2、axios cache adapterを使い、APIを呼ぶタイミングを設定する 必要なライブラリをインストール yarn add axios-cache-adapter APIの共通処理にcacheの設定をしていきます import axios from 'axios'; import {setupCache} from 'axios-cache-adapter'; const cache = setupCache({ maxAge: 10 * 1000, }); export const api = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com/', adapter:cache.adapter }); api.interceptors.response.use( response => { return response; }, function (error) { switch (error.response.status) { case 400: case 401: console.log('認証エラーです'); case 404: console.log('URL先がないです'); default: } }, ); setupCacheをインポートし、キャッシュする時間を設定します。 今回は10秒経ったら再度取得するようにしたいので10*1000としています(ミリ秒なので*1000する必要があります)。 Debuggerで確認してみましょう 今回は初期の一度のみ呼ばれているだけで、毎秒呼ばれてないですね。 10秒経ったので再度API通信がされました! 3、API毎にキャッシュする時間を設定する 最後にAPI毎に時間を設定していきます。 こうすることでリアルタイムにAPIを取得したいものと、何分、何時間に一回でいいもので分けることができます。 実装方法は簡単です。先程は共通処理に記述したので、APIを呼び出す処理の第二引数に加えるだけです。 先程の共通処理に記述したコードを削除し、ApiReseach.js(APIを呼び出しているところ)に追加で記述していきます import React, {useEffect, useState} from 'react'; import {StyleSheet, Text, FlatList, SafeAreaView} from 'react-native'; import {api} from '../api/api'; import {setupCache} from 'axios-cache-adapter'; const tenCache = setupCache({ maxAge: 10 * 1000, }); const thirtyCache = setupCache({ maxAge: 30 * 1000, }); export const ApiResearch = () => { const [count, setCount] = useState(0); const [postsData, setPostsData] = useState([]); const [usersData, setUsersData] = useState([]); const getPostsData = async () => { const res = await api.get('posts',{ adapter:tenCache.adapter }); setPostsData(res.data); }; const getUsersData = async () => { const res = await api.get('users',{ adapter:thirtyCache.adapter }); setUsersData(res.data); }; useEffect(() => { getPostsData(); getUsersData(); const intervalId = setInterval(() => { setCount(prevCount => prevCount + 1); }, 1000); return () => clearInterval(intervalId); },[count]); return ( <SafeAreaView> <Text>{count}</Text> <Text style={styles.title} >ここからpostデータです</Text> <FlatList data={postsData} renderItem={({item}) => <Text>{item.title}</Text>} keyExtractor={item => item.id.toString()} /> <Text style={styles.title}>ここからuserデータです</Text> <FlatList data={usersData} renderItem={({item}) => <Text>{item.name}</Text>} keyExtractor={item => item.id.toString()} /> </SafeAreaView> ); }; const styles = StyleSheet.create({ title: { fontSize:20, fontWeight:'bold', marginBottom:10 } }); 10秒毎に呼び出すAPIと30秒毎に呼び出すAPIで分けています。 では実際にDebuggerで確認してみましょう 画像の通りusersの方はpostsが3回呼ばれると1回呼ばれているのがわかると思います。 10秒、20秒のタイミングではusersは呼ばれていません! エンジニア初学者でエンジニア転職を目指されている方はここまで実装する必要はないかもしれないですが、 転職用のポートフォリオを作る際にこのようにAPI毎にキャッシュ時間を設定して速度改善をしていると他の人のポートフォリオと差別化が図れますし、かなりポイントが高いと思います 自分も初めて実装しましたが、想像以上に簡単だったので使ってみてください!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ReactNative】アプリのプロフィール画像を変えるときに下から出てくる選択欄(アクションシートというらしい)を作ってみる

画像のようなタップしたときに下から出てくる選択欄(アクションシート)を実装していきます。 この機能はよくアプリのアイコンや写真を設定する時に使われていると思います。 実際にはiPhoneのappleIDのアイコンやLINEのトップ画を設定するときに使われています プロジェクト作成、必要なライブラリをインストール まずはReact-Nativeプロジェクトを作成します。 ちなみにExpoではなく、素のReactNativeで作っていきます。 作成方法はドキュメントなどを参考に進めてください。 次に必要なライブラリをインストールします yarnの場合yarn add react-native-cross-actionsheet npmの場合npm install react-native-cross-actionsheet iOSだけの実装ならReactNativeの標準に入ってるのでインストールする必要はないです 実はReactNativeの公式にActionSheetIOSという同じようなUIを作れるものがありますが、それだとiOSにしか実装できません。 iOSだけならそれでもいいですが、Androidも作る予定の人はライブラリをインストールしてください。 作り方自体はほとんど一緒です。 実装 今回はアクションシート用のコンポーネントを作成して、それをApp.jsから呼び出しているかたちです import React from 'react'; import { StyleSheet, View, } from 'react-native'; import { ActionSheetPage } from './src/components/ActionSheet'; const App = () => { return ( <View style={styles.screen}> <ActionSheetPage/> </View> ); }; const styles = StyleSheet.create({ screen: { flex:1, justifyContent:'center', alignItems:'center' } }); export default App; 次にアクションシートを実装するコンポーネントを作成していきます。 import React from 'react'; import {TouchableOpacity, View} from 'react-native'; import Icon from 'react-native-vector-icons/FontAwesome'; export const ActionSheetPage = () => { return ( <View> <TouchableOpacity> <View> <Icon name="user-o" size={70} /> </View> </TouchableOpacity> </View> ); }; ベースを作り、TouchableOpacityでタップした時の挙動にアクションシートを使っていきます import React from 'react'; import {StyleSheet, TouchableOpacity, View} from 'react-native'; import {ActionSheet} from 'react-native-cross-actionsheet'; import Icon from 'react-native-vector-icons/FontAwesome'; export const ActionSheetPage = () => { const onPressAction = () => { return ActionSheet.options({ options: [ {text: '写真を撮る', onPress: () => console.log('create')}, {text: '写真を選択', onPress: () => console.log('update')}, ], cancel: {text: 'キャンセル'}, }); }; return ( <View> <TouchableOpacity onPress={onPressAction}> <View style={styles.iconButton}> <Icon style={styles.icon} name="user-o" size={70} /> </View> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ iconButton: { borderWidth: 1, width: 100, height: 100, borderRadius: 100, marginLeft: 'auto', marginRight: 'auto', }, icon: { marginLeft: 'auto', marginRight: 'auto', marginTop: 'auto', marginBottom: 'auto', }, }); options内のtextに入れたい文字を入れ、onPressにタップした時の挙動を実装すれば完成です ちなみにAndoroidだとこんな感じ 意外と簡単に実装できます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【TypeORM】typescriptの開発時「synchronize: true」はEntityにもとづいてテーブルを自動生成してくれる。「synchronize: true」の強さの紹介と「synchronize: false」にする重要さの紹介

記事概要 掲題の通り。 typeormが持つsynchronizeの機能を使えば、ヒトがテーブルを作成せずともスキーマさえあればテーブル定義を自動生成してくれる。 反面、テーブル定義変更にもとづく自動DROPが行われる場合もあるため、あくまでも開発最初期のスタートダッシュに留めるべき機能と考える。 こういう人向け 筆者自身。「こういうのができましたよ」と後で自他に確認する。 typeormを他の人はどんな感じに書いてるのか知りたい人 CREATE TABLEを最初っから書くのが面倒で、entityを書けば自動生成してくれないかなと思ってる人 実行例 出力Entity import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, Index } from "typeorm"; import { date_dictionary } from "./datadirectory" // コードマスタ定義。 @Entity({ name: "m_code" }) // インデックスも張れる。ここでは大分類codeGroupIdと小分類codeIdに分けているイメージ。 @Index("codeid-pair" ,["codeGroupId", "codeId"], { unique: true }) export class m_code extends BaseEntity { // primary keyやtype、サイズ、コメントも設定できる。 @PrimaryGeneratedColumn({ name: "id", type: "bigint", comment: '自動採番ID' }) id!: number; @Column({ nullable: false, type: "varchar", length: 7, comment: "グループコードID" }) codeGroupId!: string; @Column({ nullable: false, type: "varchar", length: 7, comment: "コードID" }) codeId!: string; @Column({ nullable: true, type: "varchar", length: 85, comment: "コード名" }) codeName!: string; @Column({ nullable: false, type: "int", comment: "表示順" }) hyoujiJyun!: number; // 共通カラム使用。デフォルトだとカラム名が連結される(dateTypeUpdatorみたいになる)ので、prefix:falseにしている @Column(() => date_dictionary, { prefix: false }) dateType!: date_dictionary } import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, UpdateDateColumn, CreateDateColumn } from "typeorm"; // @Entityが無い、テーブルとしては登録されないクラス。共通カラムを別々の場所で使う時に使える。 export class date_dictionary { @Column({ nullable: true, type: "varchar", length: 12,comment: "作成者" }) creator!: string; @CreateDateColumn({ nullable: true, type: "datetime",comment: "作成日時" }) createdAt!: Date; @Column({ nullable: true, type: "varchar", length: 12 ,comment: "更新者" }) updator!: string; @UpdateDateColumn({ nullable: true, type: "datetime",comment: "更新日時" }) updatedAt!: Date; } 接続設定 import dotenv from 'dotenv' import { ConnectionOptions } from "typeorm"; import {Log4jsLogger} from "./common/logger" // nuxt.js v14.12なので.envを有効活用している。 // 接続設定を環境変数process.env.~にすることで他環境へも適用できるようにする。 dotenv.config({ path: __dirname + '/.env' }); // https://typeorm.io/#/connection-options const config: ConnectionOptions = { type: "mysql", host: process.env.CONNECT_HOST, port: Number(process.env.CONNECT_PORT), username: process.env.CONNECT_USER, password: process.env.CONNECT_PASS, database: process.env.CONNECT_DATABASE, // 指定したフォルダ内の@Entityを認識してくれる。 // Nuxt.jsのservermiddlewareにて動かしてる扱いなので以下のようなフォルダ階層としている。models命名はsequelizeで開発してた時の名残 entities: [ "server/typeorm/models/*.*" ], // synchronize: trueの時、上記entityを正としてスキーマに接続、同期させる。 // デバッグや開発時に役立つ。 // 公式ドキュメントに書いてあるが、本番環境では絶対にfalseにしておくこと。 synchronize: true, logging: true, // https://typeorm.io/#/logging // log4jsを用いてガチャガチャカスタムしてるので割愛。 // Visual Code Studio + typescriptのクイックフィックスで編集必要なメソッドはわかる。 logger:new Log4jsLogger(), timezone:"+09:00" } export default { config: config } 接続時(なんとなく日時情報は抜いている) [DEBUG] debug - START TRANSACTION [DEBUG] debug - undefined [DEBUG] debug - SELECT DATABASE() AS `db_name` [DEBUG] debug - undefined [DEBUG] debug - SELECT `TABLE_SCHEMA`, `TABLE_NAME` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' [DEBUG] debug - undefined [DEBUG] debug - SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'typeorm_metadata' [DEBUG] debug - undefined [DEBUG] debug - SELECT SCHEMA() AS `schema_name` [DEBUG] debug - undefined [DEBUG] debug - CREATE TABLE `nuxt_typescript_training`.`m_code` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自動採番ID', `codeGroupId` varchar(7) NOT NULL COMMENT 'グループコードID', `codeId` varchar(7) NOT NULL COMMENT 'コードID', `codeName` varchar(85) NULL COMMENT 'コード名', `hyoujiJyun` int NOT NULL COMMENT '表示順', `creator` varchar(12) NULL COMMENT '作成者', `createdAt` datetime(6) NULL COMMENT '作成日時' DEFAULT CURRENT_TIMESTAMP(6), `updator` varchar(12) NULL COMMENT '更新者', `updatedAt` datetime(6) NULL COMMENT '更新日時' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), UNIQUE INDEX `codeid-pair` (`codeGroupId`, `codeId`), PRIMARY KEY (`id`)) ENGINE=InnoDB [DEBUG] debug - undefined [DEBUG] debug - COMMIT 出来上がったテーブル CREATE TABLE `m_code` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自動採番ID', `codeGroupId` varchar(7) NOT NULL COMMENT 'グループコードID', `codeId` varchar(7) NOT NULL COMMENT 'コードID', `codeName` varchar(85) DEFAULT NULL COMMENT 'コード名', `hyoujiJyun` int(11) NOT NULL COMMENT '表示順', `creator` varchar(12) DEFAULT NULL COMMENT '作成者', `createdAt` datetime(6) DEFAULT current_timestamp(6) COMMENT '作成日時', `updator` varchar(12) DEFAULT NULL COMMENT '更新者', `updatedAt` datetime(6) DEFAULT current_timestamp(6) ON UPDATE current_timestamp(6) COMMENT '更新日時', PRIMARY KEY (`id`), UNIQUE KEY `codeid-pair` (`codeGroupId`,`codeId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 自動生成されました! けど、ちょっとテーブル定義を変更する。 // lengthを7→10に伸ばす。 @Column({ nullable: false, type: "varchar", length: 10, comment: "グループコードID" }) codeGroupId!: string; @Column({ nullable: false, type: "varchar", length: 10, comment: "コードID" }) codeId!: string; 実行結果 [DEBUG] debug - START TRANSACTION [DEBUG] debug - undefined [DEBUG] debug - SELECT DATABASE() AS `db_name` [DEBUG] debug - undefined [DEBUG] debug - SELECT `TABLE_SCHEMA`, `TABLE_NAME` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' [DEBUG] debug - undefined [DEBUG] debug - SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' [DEBUG] debug - undefined [DEBUG] debug - SELECT * FROM ( SELECT * FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` `kcu` WHERE `kcu`.`TABLE_SCHEMA` = 'nuxt_typescript_training' AND `kcu`.`TABLE_NAME` = 'm_code' ) `kcu` WHERE `CONSTRAINT_NAME` = 'PRIMARY' [DEBUG] debug - undefined [DEBUG] debug - SELECT `SCHEMA_NAME`, `DEFAULT_CHARACTER_SET_NAME` as `CHARSET`, `DEFAULT_COLLATION_NAME` AS `COLLATION` FROM `INFORMATION_SCHEMA`.`SCHEMATA` [DEBUG] debug - undefined [DEBUG] debug - SELECT `s`.* FROM ( SELECT * FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' ) `s` LEFT JOIN ( SELECT * FROM `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` WHERE `CONSTRAINT_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' ) `rc` ON `s`.`INDEX_NAME` = `rc`.`CONSTRAINT_NAME` AND `s`.`TABLE_SCHEMA` = `rc`.`CONSTRAINT_SCHEMA` WHERE `s`.`INDEX_NAME` != 'PRIMARY' AND `rc`.`CONSTRAINT_NAME` IS NULL [DEBUG] debug - undefined [DEBUG] debug - SELECT `kcu`.`TABLE_SCHEMA`, `kcu`.`TABLE_NAME`, `kcu`.`CONSTRAINT_NAME`, `kcu`.`COLUMN_NAME`, `kcu`.`REFERENCED_TABLE_SCHEMA`, `kcu`.`REFERENCED_TABLE_NAME`, `kcu`.`REFERENCED_COLUMN_NAME`, `rc`.`DELETE_RULE` `ON_DELETE`, `rc`.`UPDATE_RULE` `ON_UPDATE` FROM ( SELECT * FROM `INFORMATION_SCHEMA`.`KEY_COLUMN_USAGE` `kcu` WHERE `kcu`.`TABLE_SCHEMA` = 'nuxt_typescript_training' AND `kcu`.`TABLE_NAME` = 'm_code' ) `kcu` INNER JOIN ( SELECT * FROM `INFORMATION_SCHEMA`.`REFERENTIAL_CONSTRAINTS` WHERE `CONSTRAINT_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'm_code' ) `rc` ON `rc`.`CONSTRAINT_SCHEMA` = `kcu`.`CONSTRAINT_SCHEMA` AND `rc`.`TABLE_NAME` = `kcu`.`TABLE_NAME` AND `rc`.`CONSTRAINT_NAME` = `kcu`.`CONSTRAINT_NAME` [DEBUG] debug - undefined [DEBUG] debug - SELECT VERSION() AS `version` [DEBUG] debug - undefined [DEBUG] debug - SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nuxt_typescript_training' AND `TABLE_NAME` = 'typeorm_metadata' [DEBUG] debug - undefined [DEBUG] debug - SELECT SCHEMA() AS `schema_name` [DEBUG] debug - undefined [DEBUG] debug - DROP INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeGroupId` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeGroupId` varchar(10) NOT NULL COMMENT 'グループコードID' [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeId` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeId` varchar(10) NOT NULL COMMENT 'コードID' [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `codeName` `codeName` varchar(85) NULL COMMENT 'コード名' [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `creator` `creator` varchar(12) NULL COMMENT '作成者' [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` CHANGE `updator` `updator` varchar(12) NULL COMMENT '更新者' [DEBUG] debug - undefined [DEBUG] debug - CREATE UNIQUE INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code` (`codeGroupId`, `codeId`) [DEBUG] debug - undefined [DEBUG] debug - COMMIT ALTERで書き換えてくれた、けど… [DEBUG] debug - DROP INDEX `codeid-pair` ON `nuxt_typescript_training`.`m_code` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeGroupId` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeGroupId` varchar(10) NOT NULL COMMENT 'グループコードID' [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` DROP COLUMN `codeId` [DEBUG] debug - undefined [DEBUG] debug - ALTER TABLE `nuxt_typescript_training`.`m_code` ADD `codeId` varchar(10) NOT NULL COMMENT 'コードID' [DEBUG] debug - undefined 「synchronize: true」だとテーブル定義変更時は(少なくとも0.2.37段階のmysql対象だと)DROPしたあとに入れ直してくる! 当然新規作成されたレコードはnullないし初期値になるので、十分に留意する必要がある。 あと順序を考慮していないので、新しい列は最下部にドシドシ追加されていく形になる。 動作環境 https://typeorm.io/#/ 上記公式ドキュメントの「Installation」を一読。 以下はあくまで2021年筆者の環境であるため、どこかの推奨というわけではない。 "express": "^4.17.1", "mysql2": "^2.3.0", "nuxt": "<2.15.0", "reflect-metadata": "^0.1.13", "typeorm": "^0.2.37", 上記仕様を知った上での個人的考察 開発初期に「synchronize: true」でガシガシ作る。この時にEntityをしっかり作り込む。 出来上がったCREATE TABLEをDDLとして設計書に起こす。この時に「synchronize: false」にする あとは必要に応じて人の手でALTER TABLEなりを行う。この時Migrationを使っても良いかもしれない。当然バックアップは取っておく。 上記の方のように、@Entity({name: 'users', synchronize: false})のようにsynchronizeを対処しても良いかもしれない。 終わりに Entityを出発点とした自動データベース構築は個人的に非常に好ましいと考える。 自然とEntityとテーブル定義の内容が合致しているため、ありがちなカラム抜けやEntityの手抜きが激減する。 DBの細かい設定にも対応したtypeormのentityは今後も期待している。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ES6】変数宣言const・let・var のまとめ

これは何? ES6記法で書く機会が増え、const, let, varの使い分けがよくわかっていないため、どのようなな違いがあるかまとめてみました 結論 基本的にはconstを使う。 forループの処理などのように再代入が必要なケースのみletを使用する。 varは、挙動が把握しづらく、バグの温床になる可能性があるため使わない。 用語 それぞれの特徴を理解する際に、出てくる用語のまとめ - 再代入 = 一度宣言した変数を更新すること - 再宣言 = 一度宣言した変数を、同じ変数名を使って再び宣言し直すこと - ブロックスコープ = ブロック{}ごとに囲まれた範囲のこと - 関数スコープ = 関数のブロック{}ごとに作られる範囲のこと それぞれの特徴 const 再代入不可 再宣言不可 ブロックスコープを持つ ブロックスコープ外からのアクセスを制限。つまり、ブロックスコープ外から参照ができない。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 let 再代入可能 再宣言不可能 ブロックスコープを持つ ブロックスコープ外からのアクセスを制限。つまり、ブロックスコープ外から参照ができない。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 var 再代入可能 再宣言可能 ブロックスコープを持たない ブロックスコープ外からのアクセスを許可する。つまり、ブロックスコープ外から参照ができる。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 再現 再代入 const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; // 再代入 constValue = 'Goodby!!'; letValue = 'Goodby!!'; varValue = 'Goodby!!'; console.log(constValue); console.log(letValue); console.log(varValue); // エラー内容 Uncaught TypeError: Assignment to constant variable. Goodby!! Goodby!! constのみ再代入しようとするとエラーが吐かれる。 再宣言 const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; // 再宣言 const constValue = 'Goodby!!'; let letValue = 'Goodby!!'; var varValue = 'Goodby!!'; console.log(constValue); console.log(letValue); console.log(varValue); // エラー内容 Uncaught SyntaxError: Identifier 'constValue' has already been declared Uncaught SyntaxError: Identifier 'letValue' has already been declared" Goodby!! constとletは再宣言しようとするとエラーが吐かれる。 ブロックスコープ const isTest = true; if(isTest) { const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; } console.log(constValue); console.log(letValue); console.log(varValue); // 出力 Uncaught ReferenceError: constValue is not defined Uncaught ReferenceError: letValue is not defined "Hello!!" constとletは、if文などのブロックスコープ外から参照できない 関数スコープ const setValuables = (text) => { const constValue = text; let letValue = text; var varValue = text; } setValuables('Hello!!'); console.log(constValue); console.log(letValue); console.log(varValue); // 出力 Uncaught ReferenceError: constValue is not defined Uncaught ReferenceError: letValue is not defined Uncaught ReferenceError: varValue is not defined const、letとvar全て、関数ブロック外から参照できない const, letの方が優れている varだと宣言したブロック以外にも影響をあたえてしまうため、意図しないところで値が変わってしまう。const, letだと宣言したブロック以内に影響が収まるため、意図しないところで値が変わってしまうことを防ぐことができます。 constは定数のように見えて、定数ではない constは再代入はできないが、オブジェクト型(Array, Objectなど)の中身を変更することができるようです。 例えば、配列に配列を再代入するとエラーになるが、配列の中身の一部を変更する時はエラーにらずに変更することができます。 そのため、constは定数のように見えて、定数ではないと言えます。 まとめ まとめて見て感じたことは、制限の少ないvarを使うのではなく、制限の多いconst、let を使う方が良さそうです。 また、基本的には変数はconstで定義してあげて、for文などの処理で、変数を数え上げたりする場合はletを使用するという使い分けで良さそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ES6】変数宣言const・let・var のまとめ

これは何? ES6記法で書く機会が増え、const, let, varの使い分けがよくわかっていないため、どのようなな違いがあるかまとめてみました 結論 基本的にはconstを使う。 forループの処理などのように再代入が必要なケースのみletを使用する。 varは、挙動が把握しづらく、バグの温床になる可能性があるため使わない。 用語 それぞれの特徴を理解する際に、出てくる用語のまとめ - 再代入 = 一度宣言した変数を更新すること - 再宣言 = 一度宣言した変数を、同じ変数名を使って再び宣言し直すこと - ブロックスコープ = ブロック{}ごとに囲まれた範囲のこと - 関数スコープ = 関数のブロック{}ごとに作られる範囲のこと それぞれの特徴 const 再代入不可 再宣言不可 ブロックスコープを持つ ブロックスコープ外からのアクセスを制限。つまり、ブロックスコープ外から参照ができない。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 let 再代入可能 再宣言不可能 ブロックスコープを持つ ブロックスコープ外からのアクセスを制限。つまり、ブロックスコープ外から参照ができない。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 var 再代入可能 再宣言可能 ブロックスコープを持たない ブロックスコープ外からのアクセスを許可する。つまり、ブロックスコープ外から参照ができる。 関数スコープを持つ 関数スコープ外からのアクセスを制限。つまり、関数スコープ外から参照ができない。 再現 再代入 const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; // 再代入 constValue = 'Goodby!!'; letValue = 'Goodby!!'; varValue = 'Goodby!!'; console.log(constValue); console.log(letValue); console.log(varValue); // エラー内容 Uncaught TypeError: Assignment to constant variable. Goodby!! Goodby!! constのみ再代入しようとするとエラーが吐かれる。 再宣言 const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; // 再宣言 const constValue = 'Goodby!!'; let letValue = 'Goodby!!'; var varValue = 'Goodby!!'; console.log(constValue); console.log(letValue); console.log(varValue); // エラー内容 Uncaught SyntaxError: Identifier 'constValue' has already been declared Uncaught SyntaxError: Identifier 'letValue' has already been declared" Goodby!! constとletは再宣言しようとするとエラーが吐かれる。 ブロックスコープ const isTest = true; if(isTest) { const constValue = 'Hello!!'; let letValue = 'Hello!!'; var varValue = 'Hello!!'; } console.log(constValue); console.log(letValue); console.log(varValue); // 出力 Uncaught ReferenceError: constValue is not defined Uncaught ReferenceError: letValue is not defined "Hello!!" constとletは、if文などのブロックスコープ外から参照できない 関数スコープ const setValuables = (text) => { const constValue = text; let letValue = text; var varValue = text; } setValuables('Hello!!'); console.log(constValue); console.log(letValue); console.log(varValue); // 出力 Uncaught ReferenceError: constValue is not defined Uncaught ReferenceError: letValue is not defined Uncaught ReferenceError: varValue is not defined const、letとvar全て、関数ブロック外から参照できない const, letの方が優れている varだと宣言したブロック以外にも影響をあたえてしまうため、意図しないところで値が変わってしまう。const, letだと宣言したブロック以内に影響が収まるため、意図しないところで値が変わってしまうことを防ぐことができます。 constは定数のように見えて、定数ではない constは再代入はできないが、オブジェクト型(Array, Objectなど)の中身を変更することができるようです。 例えば、配列に配列を再代入するとエラーになるが、配列の中身の一部を変更する時はエラーにらずに変更することができます。 そのため、constは定数のように見えて、定数ではないと言えます。 まとめ まとめて見て感じたことは、制限の少ないvarを使うのではなく、制限の多いconst、let を使う方が良さそうです。 また、基本的には変数はconstで定義してあげて、for文などの処理で、変数を数え上げたりする場合はletを使用するという使い分けで良さそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js: docxをhtmlにするMammoth

Mammoth .docx to HTML converter 導入 npm --save-dev install mammoth お試し npx mammoth sample.docx output2.html Before *テンプレート After まとめ Pandoc でない簡単な選択肢の1。 Pythonでも。 ということで、触ってみた記録でした。参考になればさいわいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Atcoder】JavaScriptでもC++のSTLが使いたい!

はじめに 競プロではC++の利用者が圧倒的に多い中、私はJavaScriptでAtCoderのコンテストに参加しています。理由は、競プロを始めようと思ったときに、新しくC++を勉強するよりも、使い慣れた言語で手っ取り早く競プロを始めたかったからです。JavaScriptはC++と比べると実行速度が遅い言語ではありますが、適切に実装すれば、大半の問題では制限時間を超えてしまうことはないと感じています。しかし、ときどきC++との大きな差を感じざるを得ない場面もあります。 C++のSTLが使いたくなるとき JavaScriptにはArray・Set・Mapなどのデータの集合を扱うためのクラスが用意されていますが、C++のSTLに比べると種類や機能が貧弱です。例えば、JavaScriptでキューを使いたいときはArrayで代替することになりますが、先頭の要素を取り出すArray#shiftは計算量がO(n)のためかなり低速です[1][2]。また、C++のstd::setのように要素が常にソートされているクラスも用意されていません。もし、STLにあってJavaScriptにはないクラスが必要になったら、自前で実装しなければなりませんが、それはかなり大変です。 tstlとは JavaScriptでやるのもそろそろ限界かと思いながら、いろいろと調べていると、AtCoderのTypeScript環境にはtstlというライブラリがインストールされていることに気が付きます[3]。tstlはC++のSTLをTypeScriptに移植したライブラリとのことで、これは非常にありがたいです。JavaScriptでSTLにあるものと同等のクラスを使うことができれば、上記の問題は解決できます。 【リポジトリ】https://github.com/samchon/tstl 【API一覧】https://tstl.dev/api/modules/std.html tstlを使うときの注意点 tstlはTypeScript環境にしかインストールされていないため、JavaScriptユーザーがtstlを使う場合は、提出言語をTypeScriptに指定する必要があります。また、リポジトリのREADMEではデフォルトインポートが使用されていますが、AtCoderがインストールしているtstlのバージョンではデフォルトインポートに対応していないようなので、名前付きインポートを使用します。 import * as std from 'tstl'; // すべての名前つきエクスポートをまとめてインポートする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoftさんの「ONNX Runtime Web(ORT Web)」のサンプル【Quick Start - Web (using script tag)】を動かしてみたり公式デモの情報を見て試してみたりする

この記事は、以下のニュースに書かれていた「ONNX Runtime Web(ORT Web)」の一部を試してみたり、関連情報を見てみたという話です。 ●マイクロソフト、WebAssemblyとWebGLで推論エンジンを実装した「ONNX Runtime Web」(ORT Web)をオープンソースで公開 - Publickey ブログ書きました: マイクロソフト、WebAssemblyとWebGLで推論エンジンを実装した「ONNX Runtime Web」(ORT Web)をオープンソースで公開 https://t.co/6y4XXjo1S1— Publickey (@publickey) September 5, 2021 情報をたどってサンプルを動かすまで 情報をたどる 上記の記事の最初に、以下のリポジトリへのリンクがはられていました。 ●onnxruntime/js/web at master · microsoft/onnxruntime  https://github.com/microsoft/onnxruntime/tree/master/js/web#readme そして、中を見ていくと以下の「Usage」の説明がありました。 そして、上記のONNX Runtime JavaScript examplesの先を見てみます。 そして、ONNX Runtime JavaScript examples の中にも「Usage」の項目があります。 その中の「Quick Start」は以下の 3種類があります。 Nodejs Binding Web (using script tag) Web (using bundler) 個人的には、TensorFlow.js等を HTML+JavaScript でという感じのことを好んでやっていたりするので、Web (using script tag) を深掘りしてみます。 Web (using script tag) のサンプルを試す 「Web (using script tag) のクイックスタート」は、ページ内はシンプルでした。 ちなみに、リポジトリ内のファイルも以下の 3つだけ。 HTMLファイルの中身を見ていってみます。 <!DOCTYPE html> <html> <header> <title>ONNX Runtime JavaScript examples: Quick Start - Web (using script tag)</title> </header> <body> <!-- import ONNXRuntime Web from CDN --> <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js"></script> <script> // use an async context to call onnxruntime functions. async function main() { try { // create a new session and load the specific model. // // the model in this example contains a single MatMul node // it has 2 inputs: 'a'(float32, 3x4) and 'b'(float32, 4x3) // it has 1 output: 'c'(float32, 3x3) const session = await ort.InferenceSession.create('./model.onnx'); // prepare inputs. a tensor need its corresponding TypedArray as data const dataA = Float32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); const dataB = Float32Array.from([10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]); const tensorA = new ort.Tensor('float32', dataA, [3, 4]); const tensorB = new ort.Tensor('float32', dataB, [4, 3]); // prepare feeds. use model input names as keys. const feeds = { a: tensorA, b: tensorB }; // feed inputs and run const results = await session.run(feeds); // read from results const dataC = results.c.data; document.write(`data of result tensor 'c': ${dataC}`); } catch (e) { document.write(`failed to inference ONNX model: ${e}.`); } } main(); </script> </body> </html> 内容を見た感じだと、先ほどのリポジトリ内にあった「model.onnx」を読み込んで使えば、この処理を実行できそうです。 ファイル読み込みを伴うので、HTMLファイルを直接ブラウザにドラッグ&ドロップして実行というわけにはいかないため、ローカルでサーバを実行します。 「Web (using script tag) のクイックスタート」では、 npx light-server -s . -p 8080 というコマンドで light-server を使ってサーバを動かしていましたが、自分の場合は Python のワンライナーWebサーバを使いました。 そして、ブラウザからローカルサーバのアドレスでアクセスし、処理を実行してみます。 そうすると、ブラウザ上に以下の結果が表示されました。 このプログラムの内容は、3行4列の行列と4行3列の行列の2つの行列の積を求めるもののようなので、結果が正しいか確認してみます。 適当にググってでてきた以下のサイトで、今回の行列の積の計算を行ってみます。  ●行列の積 - 高精度計算サイト   https://keisan.casio.jp/exec/system/1308269580 計算結果は以下のとおりで、先ほどブラウザ上に表示されたものと一致していそうです。 もう少し関連情報を見てみる とりあえずサンプルは簡単に動かせたのですが、冒頭の記事にあった以下の部分の話には触れられずでした。 どうやら、以下の記事にもう少し情報がありそうなので、少し見てみます。 ●ONNX Runtime Web—running your machine learning model in browser - Microsoft Open Source Blog  https://cloudblogs.microsoft.com/opensource/2021/09/02/onnx-runtime-web-running-your-machine-learning-model-in-browser/ モデルの準備 「Get Started」の中に書かれた内容を見ると、ONNX形式のモデルを準備すれば、もう少しいろいろなことができそうです。 TensorFlow・PyTorch等でモデルを作って変換するか、ONNX Model Zoo からモデルを持ってくるか、という感じのことが書いてあります。 ブラウザ上で推論を実行 さらに「Get Started」の中を読み進めていくと、ブラウザ上で推論を実行する話が書かれていそうでした。 さらに読み進めると、以下に「ONNX Runtime Web」というリンクがありました。 リンク先を見ると、画像分類や他のいくつかのサンプルがありそうでした。 とりあえず、一番上の画像分類が試せそうな「MobileNet, trained on ImageNet 」を開いてみます。 バックエンドを「GPU-WebGL」か「CPU-WebAssembly」のどちらかにして、画像分類を実行できそうな感じです。 画像は、既に用意されたものを選ぶか、自分でアップロードすることができるようでした。 とりあえず、既に用意されている画像を選んで動作確認をしてみました。 試しに「coffee」を選ぶと、以下のような推論の結果が表示されるのが確認できました。 上記の左上から開けるメニューを開いて、その中のリンクの 1つを見るとさらに情報が書かれていそうでしたが、今回の記事はここまでにしておこうと思います。  ●microsoft/onnxruntime-web-demo: demos to show the capabilities of ONNX Runtime Web   https://github.com/Microsoft/onnxruntime-web-demo まとめ 今回、情報を見かけて気になった「ONNX Runtime Web(ORT Web)」に関する内容を試してみました。 画像分類等のデモの実装の話はまだ見られてないので、別途、調べて記事にできればと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む