20200107のJavaScriptに関する記事は29件です。

ブラウザのユーザーエージェントを表示するブックマークレット

ブックマークレット

ダイアログで表示

window.promptメソッドを利用してダイアログで表示する。

javascript:(function() { window.prompt('userAgent', window.navigator.userAgent); })();

コンソールに表示

console.logメソッドを利用して Web コンソールに表示する。

javascript:(function() { console.log(window.navigator.userAgent); })();

window.navigator.userAgent

window.navigator.userAgentプロパティは、ブラウザのユーザーエージェントを表す文字列を返す。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webpackで複数のエントリーとアウトプットの設定で引っかかったこと

ElectronとTypeScriptを使うためにwebpackを使った。

設定は以下ファイル。

webpack.config.js
const path = require('path');

module.exports = {
    mode: 'development',
    target: 'electron-main',
    entry: './src/ts/index.ts',
    output: {
        path: path.join(__dirname, "dist"),
        filename: "main.js"
    },
    module: {
        rules: [{
        test: /\.ts$/,
        use: 'ts-loader'
        }]
    },
    resolve: {
        modules: [
        "node_modules",
        ],
        extensions: [
        '.ts',
        '.js'
        ]
    },
    node: {
        __dirname: false,
        __filename: false
    },
};

module.exports = {
    mode: 'development',
    target: 'electron-renderer',
    entry: path.join(__dirname, 'src', 'ts', 'renderer', 'index'),
    output: {
      filename: 'index.js',
      path: path.resolve(__dirname, 'dist', 'scripts')
    },
    resolve: {
      extensions: ['.json', '.js', '.ts']
    },
    module: {
      rules: [{
        test: /\.(tsx|ts)$/,
        use: [
          'ts-loader'
        ],
        include: [
          path.resolve(__dirname, 'src'),
          path.resolve(__dirname, 'node_modules'),
        ],
      }]
    },
  };

この状態で、

webpack --watch

を実行しても、2つ目のmodule.exportsしか実行されていない問題に遭遇。

解決策

webpack.config.js
const path = require('path');

const main = {
    mode: 'development',
    target: 'electron-main',
    entry: './src/ts/index.ts',
    output: {
        path: path.join(__dirname, "dist"),
        filename: "main.js"
    },
    module: {
        rules: [{
        test: /\.ts$/,
        use: 'ts-loader'
        }]
    },
    resolve: {
        modules: [
        "node_modules",
        ],
        extensions: [
        '.ts',
        '.js'
        ]
    },
    node: {
        __dirname: false,
        __filename: false
    },
};

const renderer = {
    mode: 'development',
    target: 'electron-renderer',
    entry: path.join(__dirname, 'src', 'ts', 'renderer', 'index'),
    output: {
      filename: 'index.js',
      path: path.resolve(__dirname, 'dist', 'scripts')
    },
    resolve: {
      extensions: ['.json', '.js', '.ts']
    },
    module: {
      rules: [{
        test: /\.(tsx|ts)$/,
        use: [
          'ts-loader'
        ],
        include: [
          path.resolve(__dirname, 'src'),
          path.resolve(__dirname, 'node_modules'),
        ],
      }]
    },
  };

  module.exports = [
      main, renderer
  }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VueとFlaskでITイベント情報を地図表示するウェブアプリを作った�

はじめに

ネットサーフィンをしていると、ITイベントポータルサイトのconnpassがイベント情報を提供するWebAPIを配信している事を知り、開催地などを地図上で表示するアプリがあれば便利なのではないかと思い制作、年始の3日間を生贄に捧げ完成。

成果物

Signpost for connpass
GitHub
スクリーンショット 2020-01-07 21.57.23.png

いわゆるSPAアプリケーションで、非同期通信で得たレスポンスを動的に地図表示します。

フレームワーク・ライブラリと選定理由

  • Flask:後述
  • Vue.js (Vue CLI):勉強中であるため、Reactよりしっくり来るため
  • Mapbox GL JS (+VueMapbox):多数の地物を表示するため動作パフォーマンスを重視した
  • BootstrapVue:神
  • OpenStreetMap(背景地図):国土地理院の基本地図よりも建物名などが見やすいから

各種ポイント

CORS制限

CORSとは、オリジン間リソース共有(Cross-Origin Resource Sharing)のこと。
簡単には、「許可を得ていない他人のウェブサイトのデータを非同期通信で持ってくるのはダメよ」、と理解している。
今回のケースでは、connpassAPIのレスポンスは、CORS制限のためフロント側で取得する事が出来ない。一度自らのサーバで受けておけばCORS制限には引っかからない。という訳で、当初はfirebaseのhostingやfunctionで対応しようと考えていたが、connpassAPIにアクセスしレスポンスを受け取るサーバをFlaskで構築する事とした。Flaskはとてもシンプルなので、今回のようになんでも良いからサーバが必要な場合に最適だと思うし、個人的に好きだから採用。

FlaskとVue.jsの環境構築

Vue.jsはnode.jsと組み合わせて使う事が多いと思います。色々調べた結果、Flaskと共存して開発出来る構成に至りました。
スクリーンショット 2020-01-07 21.51.25.png

ルートにPythonスクリプトやherokuサーバ用ファイルを配置し、フロント側はvueディレクトリに納めてあります。以下のとおり設定する事で、Flaskは、vue/distをtemplateフォルダとして参照するようになります。

from flask import Flask, render_template, request, jsonify, make_response, send_file, redirect, url_for

app = Flask(__name__, static_folder='./vue/dist/static', template_folder='./vue/dist')


#以下ルーティング…

APIサーバの構築

connpassAPIの仕様に沿ってアクセスし、得たレスポンスをフロントへ返すAPIサーバを以下のようにつくりました。

import urllib.request, urllib.parse
import json
@app.route('/api/')
def getApi():
    keyword_or = request.args.get('keyword_or')
    ym = request.args.get('ym')
    ymd = request.args.get('ymd')
    owner_nickname = request.args.get('owner_nickname')
    start = request.args.get('start')
    order = request.args.get('order')
    count = request.args.get('count')
    all_params = {
        "keyword_or": keyword_or,
        "ym":ym,
        "ymd":ymd,
        "owner_nickname":owner_nickname,
        "start":start,
        "order":order,
        "count":100
    }

    params = {}
    #値がNoneとなっている要素はパラメータから削除する
    for key in all_params:
        if all_params[key] != None:
            params[key] = all_params[key]


    p = urllib.parse.urlencode(params)
    url = "https://connpass.com/api/v1/event/?" + p

    with urllib.request.urlopen(url) as res:
        html = res.read().decode().replace(r"\n","")
        jsonData = json.loads(html)
        return jsonify(jsonData)

connpassAPIと同じパラメータを受け、そのままconnpassAPIに投げ、レスポンスをデコードし、jsonとして返しています。完全なる中継サーバという事です。

参考サイト

Vue.js(vue-cli)とFlaskを使って簡易アプリを作成する【後半 - サーバーサイド編】
BootstrapVue
VueMapbox

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript: オブジェクトのプロパティを削除する方法(イミュータブル)

JavaScriptでオブジェクトのプロパティをイミュータブルなやりかたで削除する方法です。

イミュータブルな削除というのは、あるオブジェクトのプロパティを削除するとき、そのオブジェクトを変更するのではなく、削除対象のプロパティが取り除かれたオブジェクトを新たに作ることです。

やりかた

destructuring assignment構文を使います。

例えば、a,b,cの3つのプロパティを持ったオブジェクトから、aを削除したい場合、

const obj1 = { a: 1, b: 2, c: 3 }

destructuring assignment構文に、取り除くaプロパティと、残すそれ以外のプロパティを入れる変数obj2...を伴って書きます:

const {a, ...obj2} = obj1

すると、obj1は変更されず、obj1からaが取り除かれたobj2ができます:

console.log(obj1, obj2)
//=> { a: 1, b: 2, c: 3 } { b: 2, c: 3 } ?✅
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Switch構文とWhile構文を学習しました

前回のあらすじ

・if構文を使って、変数がうるう年か判定するプログラムを組んだぞ

今日やったこと

・Switch構文について学んだ
Switch構文とは、条件となる変数の値によって、最適な場所へぴょんぴょんジャンプして上から下へと処理を行うもの。

case n:
 total +=n;
/*ここにbreak;を挿入すれば下の処理に移動しない*/
case m:
 total +=m;

・While構文について学んだ
While構文とは、条件をチェックして、その条件が成立する間は、繰り返しぐるぐるっと処理を行う構文
(今回の教科書『新人プログラマのためのjQuery Webアプリケーション開発講座』)

困ったこと

・難しいことをやっているからか、プログラミングを知らない人に説明できるほどの理解はできなかった
でも、自分では何が行われているのか理解できた。

感想

構文の勉強は地道だなと感じた。今の学習がものづくりに活かせる日は来るのだろうか…

次回の目標

次はfor構文の使い方を理解するぞい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

まだ相対パスで消耗してるの?

タイトル、煽りでスイマセン。

今回は↓これ↓の話です。

hoge.js
import SomeComponent from ../../../components/hoge/fuga

相対パスのimportです。
"../../"の数がいちいちわからなくなるのって私だけですかね?
また、ファイルを別のフォルダに打ちしたときなんかにエラーが出ることもあったりして、
相対パス、書き換えるの面倒くさいですよね。

今回はそんな悩みを絶対パスにして解決してしまおうという記事です。
題して、「絶対パスでReactを書こう!」
(よく考えたら題してないな)

内容に間違い等がございましたら、ご意見・ご指摘のほど宜しくお願いいたします。

前提:やり方は意外に簡単だよ!という話

調べたら色んなやり方が出てきましたが、一番手っ取り早い方法をご紹介します。
以下は概要。

  • webpack.config.jsonは使いません。
  • .babelrcは作りません。
  • ts.config.jsonを使います。
  • typescriptじゃなくてもOKです。

素のjavascirpt(typescriptじゃないという意味で素)でもts.config.jsonが使えるというのは、私も今回初めて知りました。
以下の記事が大変参考になったので貼っておきます。感謝!
https://qiita.com/terrierscript/items/a9826bc58d550d1b2764
また公式ドキュメントも先に貼っておきますのでお急ぎの方はこちらをご参照ください。
https://create-react-app.dev/docs/importing-a-component/#absolute-imports

では早速、方法をご紹介していきます。

step① tsconfig.jsonを作る

プロジェクトのroot(package.jsonなどがある場所)にts.config.jsonを作成します。
npx typescript --initして下記のように設定してください。
(下記はhttps://create-react-app.dev/より引用。)

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

自分は下記のように設定を加えています。

tsconfig.json
{
  "compilerOptions": {
    "incremental": true,
    "target": "es2018",
    "module": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "jsx": "react",
    "isolatedModules": true,
    "strict": true,
    "baseUrl": "src",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noEmit": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "build", "scripts", "functions"]
}

なおts.config.jsonの書き方については、下記を参考にさせていただきました。
https://usefuledge.com/pb_00009_tscconfig_json.html
自分はこの辺りの設定にあまり詳しくないので、「ここがおかしい」とか「こうした方がいいよ」というのがございましたら、ぜひぜひコメント欄にお願いいたします。

step② TypeScriptを入れる

素のJSでtsconfig.jsonを作成すると、TypeScriptじゃないのにtsconfigがあるよ!
というような趣旨のエラーがでるので、その案内に従ってTypeScriptを入れてください。
しかし、拡張子をtsxにしたり、全部anyにして無理やりTypeScript化する必要はありません。
(ts入れて使わないならjsconfig.jsonで良くね?という話は冒頭でご紹介した記事を御覧ください。)

結果:絶対パスで書けるようになる

hoge.js
import AbsoluteImportComponent from components/hoge/fuga
import RelativeImportComponent from ../components/hoge/hoge

絶対パスで書けるようになります!意外と簡単でしたね。
絶対パスでしか書けなくなるのではなく、どうやら両方いけるみたいです。

まとめ

絶対パスを使うとimportがむっちゃすっきりするよ、という話でした。
最後まで読んでいただいてありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

画像がないときにJavaScriptでかんたんな単色なアイコンを作る

スクリーンショット 2020-01-07 19.22.19.png

なんか画像が欲しいってなったとき

  1. 白い画面にアクセス

    about:blankにChromeでアクセスをして綺麗なDOMにアクセスします。

  2. スクリプトを貼り付けます

document.getElementsByTagName("body")[0].insertAdjacentElement("afterbegin", document.createElement("canvas"))


const width = 50;
const height = 50;
const canvas = document.getElementsByTagName("canvas")[0]

// 色を指定して
canvas.width = width
canvas.height = height
const icon = new ImageData(width, height)
icon.data.fill(33)

ctx = canvas.getContext("2d")
ctx.putImageData(icon, 0, 0)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptのquerySelectorAllで取得したNodelistをforEachでループしたときに、インデックスも取得する方法

querySelectorAllで配列に似た?NodeListというオブジェクトを取得できます

NodeListは、forEachループが使えます

ループするときに要素だけでなく、インデックス、それとNodeList自身も取得できます

ループ時のインデックス取得はよくあるので、簡単に取得できてよかった

See the Pen BayJXdw by adamof (@adamof) on CodePen.

こちらを参考にしました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reduxの学習コストに躊躇う初学者の方に伝えたいこと

Reduxつらい問題ってありますよね。
ちょっとした実装がしたいだけなのに、Reduxだとやたら量を書かないといけなかったりする。
「action? dispatch? reducer? 色々と学ぶことが多くて大変そう」と思う方もいらっしゃるでしょう。
今回はそんなReduxに尻込みしている初学者の方に送るReduxのメリットについての話です。

メリット① redux-devtools-extention便利すぎ

Reactを書くときにいろんなところにconsole.logをしかけて、いちいち挙動を確認しているとしたら、redux-devtools-extentionはそんなあなたの手間を劇的に削減することでしょう。
redux-devtools-extentionを使うためにReduxを書いているといっても過言ではありません。
いやさすがにそれは言い過ぎました。(どっちやねん!)
何はともあれ、redux-devtoolsは非常に便利ですのでReduxを使ってみましょう
下記の記事をご参照ください。
https://qiita.com/elzup/items/fc24588b2c6bae0834a6

メリット② 大きめのプロジェクトを管理しやすい

あなたはこう言うでしょう。
「簡単なことがしたいだけなのに!10行で書けることがReduxだと30行にも40行にもなる!」
その気持ちぐぅわかるよ。。。
Reduxのメリットは、大きめのプログラムでReduxを使ったことがある人にしかわからないものです。

そうReduxは大きくなればなるほどその力を発揮するものであって、
個人で開発するWebアプリにとってはほぼ例外なくオーバーキルです。
しかし、いざ大規模の開発となるとReduxは輝きます。
そのときに向けて個人開発で予行演習しておくことを強く推します。

メリット③ 綺麗なコードについて考えるようになる

②とも共通することですが、Reduxで書くとそうでないときより読みやすくなることが多いです。
なぜなら、Reduxというのは単に状態管理のライブラリではなく、コードを体系的に書くための1個の考え方でもあるからです。(あくまで個人的な意見ですが。)
Reduxを勉強する過程で、綺麗なコードを書くための先人たちの知恵や思想に数多く出会うことでしょう。
それはducksかもしれないし、re-ducksかもしれないし、presentational-componentsとcontainer-componentsを分離させることかもしれません。
それが何であれ、その考え方自体は、いつかReduxが別のライブラリにとって代わられたとしても、一個に思想として残り続ける価値のあるものであるだと思います。

もし「immutableってどういうことだ?」「ディレクトリ構成ってどうやったらいんだ?」などと考えたことがあるなら、それはReduxを学ぶための最良のチャンスですよ!

余談:HooksもいいけどReduxもいいよ

最後に余談。
Reduxに代わるものとしてReact界隈だと昨今はHooksに注目が集まっています。
「useContextなら関数も渡せるしReduxみたいな面倒なこともない」という意見もあります。
それでもReduxを推してみたい私です。
Reduxは必ずしもスマートではないかもしれませんが、やはり数の暴力といいますか、ちゃんとした情報がいろいろ落ちていたり、Reduxを強力にサポートするツールやライブラリが盛りだくさんだからです。
筆者は個人的にreact-redux-firebaseというreduxとfirebaseを良い感じに併用可能にしてくれるツールが好きで愛用しています。

「大変だけどHooksもReduxも両方学ぶのがベストだよ!」というのを今回の結論とさせてください!

Let's get Started!!

幸いにしてQiitaにはReduxのを学ぶための良記事があるので、まずはそれから始めてはいかがでしょう。
下記にリンクを貼っておきます。
https://qiita.com/mpyw/items/a816c6380219b1d5a3bf

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Riot.js で純粋なコンポーネント(riot.pure)を使用する

riot.pure

Riot v4.8.0でriot.pureが追加されたので使ってみます。

コンポーネントのレンダリングを自分で制御したい場合に、riot.pure を使うことで Riot.js の内部ロジックをスキップすることができるようです。
内部ロジックがスキップされる為、HTMLとCSSは使えません。

基本動作

riot.pureを使ってmountupdateunmountのメソッドを含んだオブジェクトを返すモジュールを登録します。

pure.riot
<my-pure>
  <script>
  import { pure } from 'riot'

  export default pure(({ slots, attributes, props }) => {
    console.log(slots, attributes, props);
    return {
      el: null,
      mount(el, context) {
        this.el = el;
        console.log("mount", el, context);
        this.el.innerHTML = 'Hello There';
      },
      update(context) {
        console.log("update", context);
      },
      unmount(preserveRoot) {
        console.log("unmount", preserveRoot);
        this.el.parentNode.removeChild(this.el);
      }
    };
  });
  </script>
</my-pure>

pureで受け取れる内容は以下の3つ
1. slots - コンポーネント内で見つかった slot リスト
2. attributes - コンテキストからコンポーネントプロパティを推測するために評価可能なコンポーネントの属性式
3. props - riot.component 呼び出しによってのみ設定できる初期コンポーネントユーザープロパティ

pureの対象となるエレメントはマウント時にしかもらえないため、this.elに保持しています。

pureコンポーネントの使用はこんな感じで。

app.riot
<my-app>
  <p>{ props.message }</p>
  <my-pure></my-pure>
  <input type="button" value="update" onclick="{ pre_update }">
  <input type="button" value="unmount" onclick="{ pre_unmount }">

  <script>
    import { unmount } from 'riot'

    export default {
      pre_update(e) {
        this.update();
      },

      pre_unmount(e) {
        unmount('my-pure');
      }
    }
  </script>
</my-app>
index.js
import { component,register } from 'riot'
import App from './app.riot'
import Pure from './pure.riot'

register('my-pure', Pure);

component(App)(document.getElementById('root'), {
  message: 'Hello World'
});

試してみる…がunmount時に反応しない?

image.png

どうやら、unmountの動きとしてriot-componentを持っていないとunmountを呼び出してくれないようです。

・動作確認用
https://plnkr.co/edit/sPjH1JE9zeLPErMBysx8?p=preview

というわけで、初めてのプルリクとやらをしてみました。1発ミスったのは内緒。
https://github.com/riot/riot/pull/2801

修正内容はいたって簡単で、持っていないなら持たせればいいじゃない、の精神。

riot/src/core/component.js
  return createCoreAPIMethods(method => (...args) => {
    component[method](...args)
    args[0][IS_PURE_SYMBOL] = component // ここ追加
    return component
  })
riot/src/riot.js
export function unmount(selector, keepRootElement) {
  return $(selector).map(element => {
    if (element[DOM_COMPONENT_INSTANCE_PROPERTY]) {
      element[DOM_COMPONENT_INSTANCE_PROPERTY].unmount(keepRootElement)
    // ↓ ここ追加
    } else if (element[IS_PURE_SYMBOL]) {
      element[IS_PURE_SYMBOL].unmount(keepRootElement)
    }
    return element
  })
}

まとめ

これを使えば複数のコンポーネントで使いまわせる共通の処理とか書けそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

{(name)}ってなんやねん!分割代入とは

Javascript(Vue.js)を書くなかで分割代入なんやねん!となったので、記事に残します。
※今回はVue.jsでコード書いてます。Javascriptの機能なので生Javascriptでも同じことができます。

変数に仮引数ではなく実引数を代入している・・・?

分割代入は関数の変数の部分に({実引数})という書き方をする。
以下のmyFuncNewに注目すると、nameというプロパティをもつオブジェクトをmyFuncNewに渡すという意味になる。

{{myFunc(items.name)}}//りんご
{{myFuncNew(items)}}//りんご

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  //通常の書き方
  myFunc(name){
    return name;
  },
 //分割代入での書き方
  myFuncNew({name}){
    return name;
  },
}

分割代入は関数の変数の部分に({実引数})という書き方をすると書いた通り、
呼び出し側で{}内を変えると動かなくなる。
nameからnewnameに変えてみた。

{{myFunc(newname)}}//りんご
{{myFuncNew(newname)}}//※表示されない※

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  myFunc(name){
    return name;
  },
  myFuncNew({name}){
    return name;
  },
}

表示されなくなった。

今回はオブジェクトを対象とした分割代入について簡単に記しましたが、もっと奥が深い内容なので勉強することにします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

({実引数})ってなんやねん!分割代入とは

Javascript(Vue.js)を書くなかで分割代入なんやねん!となったので、記事に残します。
※今回はVue.jsでコード書いてます。Javascriptの機能なので生Javascriptでも同じことができます。

()の中に{}があるぞ・・・?しかも{}の中に仮引数ではなく実引数が書かれている・・・?

分割代入は関数に渡す変数の部分に({実引数})という書き方をする。
以下のmyFuncNewに注目すると、nameというプロパティをもつオブジェクトをmyFuncNewに渡すという意味になる。

{{myFunc(items.name)}}//りんご
{{myFuncNew(items)}}//りんご

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  //通常の書き方
  myFunc(name){
    return name;
  },
 //分割代入での書き方
  myFuncNew({name}){
    return name;
  },
}

分割代入は関数の変数の部分に({実引数})という書き方をすると書いた通り、
呼び出し側で{}内を変えると動かなくなる。
nameからnewnameに変えてみた。

{{myFunc(newname)}}//りんご
{{myFuncNew(newname)}}//※表示されない※

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  myFunc(name){
    return name;
  },
  myFuncNew({name}){
    return name;
  },
}

表示されなくなった。

まとめ

呼び出される時に呼び出し元のオブジェクトが渡されるというところがポイントです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

分割代入

Javascript(Vue.js)を書くなかで分割代入なんやねん!となったので、記事に残します。
※今回はVue.jsでコード書いてます。Javascriptの機能なので生Javascriptでも同じことができます。

変数に仮引数ではなく実引数を代入している・・・?

分割代入は関数の変数の部分に({実引数})という書き方をする。
以下のmyFuncNewに注目すると、nameというプロパティをもつオブジェクトをmyFuncNewの引数とするという意味になる。

{{myFunc(items.name)}}//りんご
{{myFuncNew(items)}}//りんご

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  //通常の書き方
  myFunc(name){
    return name;
  },
 //分割代入での書き方
  myFuncNew({name}){
    return name;
  },
}

分割代入は関数の変数の部分に({実引数})という書き方をすると書いた通り、
呼び出し側で{}内を変えると動かなくなる。
nameからnewnameに変えてみた。

{{myFunc(newname)}}//りんご
{{myFuncNew(newname)}}//※表示されない※

省略

data(){
  return{
    items:{
      name:'りんご',
      price:'100',
    },
  }
},
methods:{
  myFunc(name){
    return name;
  },
  myFuncNew({name}){
    return name;
  },
}

表示されなくなった。

今回はオブジェクトを対象とした分割代入について簡単に記しましたが、もっと奥が深い内容なので勉強することにします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでDOM操作する方法[JavaScript本格入門]__WebデザイナーのJS学習備忘録

DOM

DOMとは、Document Object Modelのことで、HTMLやXMLなどマークアップ言語で書かれたドキュメントの特徴のことである。
div要素やアンカータグなどのかたまりをオブジェクトとしてJavaScriptでHTMLを直接編集することができる。

DOMはHTMLなどで書かれたドキュメントを文書ツリー(ツリー構造)として扱う。

DOMでは、ドキュメントに含まれる要素や属性、テキストをそれぞれオブジェクトと見なし、「オブジェクトの集合(階層関係)がドキュメントである」と考える。

ドキュメントを構成する要素や属性、テキストといったオブジェクトのことをノードと呼び、オブジェクトの種類に応じて要素ノード、属性ノード、テキストノードなどと呼ぶ。

DOMは、これらノードを抽出、追加、置換、削除するための汎用的な手段を提供するAPI(Application Programming Interface)である。

それぞれのノードはツリー構造上での上下関係によって呼称がある。

ルートノード

ツリーの最上位に位置するノードで、最上位ノードとも呼ぶ。

親ノード/子ノード

上下関係にあるノード。直接繋がっているノードで、ルートノードに近いノードを親ノード、遠いノードを子ノードと呼ぶ。
※上下関係にあるが、直接の親子でないものを祖先ノードと子孫ノードと呼ぶ場合もある。

兄弟ノード

同じ親ノードを持つノード同士のことである。先に書かれているものを兄ノード、後に書かれているものを弟ノードとして区別することもある。

DOMを操作する方法

DOM操作には要素ノードを取得する以外にも、ノードウォーキングイベントドリブンモデルがあるが、内容が多くなってしまうので、今回はDOM操作の最も基本となる要素ノードの取得に内容を絞っている。

getElementByIdメソッド

Idをキーとして、そのIdを持つ要素(Element)を取得(get)するということである。

▼ 構文 - getElementByIdメソッド

document.getElementById(id)
id:取得したい要素のid値

▼ サンプルコード

index.html
現在時刻: <span id="result"></span>
script.js
var current = new Date();                          //デフォルトのDateオブジェクトを生成
var result = document.getElementById("result");
result.textContent = current.toLocaleString();

まずデフォルトのDateオブジェクトを生成して、変数currentを定義する。
Dateオブジェクトはデフォルトで生成された時点でのシステムの日付をセットする。
次にid値resultを要素の持つドキュメントを取得して変数resultを定義する。
ここでのドキュメントとは、<span id="result"></span>
toLocaleString()とは、Dateオブジェクトのローカル時を取得する。
取得した日時をtextContentプロパティでspan要素に現在時刻を埋め込み、ページに反映させる。

取得した要素(Elementオブジェクト)に対してテキストを埋め込むためには、textContentプロパティを利用する。

getElementsByTagNameメソッド

タグ名をキーとして、そのタグ名を持つ要素(Elements)を取得(get)するということである。

タグとは、h1~h6やpなどの要素のことである。

id値は原則としてページ内で1つしか指定することができないのに対して、タグ名はページ内で何度も使用して良いため、取得する要素が複数になる可能性もある。
そのためgetElementByIdメソッドではElement(単数)であったのに対し、getElementsByTagNameメソッドではElements(複数形)となっていることに注意!

▼ 構文 getElementByTagNameメソッド

document.getElementByTagName(name)
name: タグ名

※ 引数nameに「*」と指定することですべての要素を取得することもできる。

例えば、ページのリスト要素をすべて取得したい場合

▼ サンプルコード

index.html
<ul>
  <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript">JavaScript \| MDN</a></li>
  <li><a href="http://semooh.jp/jquery/">JQuery日本語リファレンス</a></li>
  <li><a href="https://jp.vuejs.org/">Vue.js</a></li>
  <li><a href="https://ja.reactjs.org/">React</a></li>
</ul>
script.js
//ページ内のすべてのアンカータグを取得
var list = document.getElementByTagName("a");
//リスト(HTMLCollectionオブジェクト)から順にアンカータグを取り出し、
//そのhref属性をログに出力
for(var i = 0, len = list.length; i < len; i++) {
  console.log(list.item(i).href);
} 

↓ ログ出力結果

https://developer.mozilla.org/ja/docs/Web/JavaScript
http://semooh.jp/jquery/
https://jp.vuejs.org/
https://ja.reactjs.org/

getElemetnByTagNameメソッドの戻り値は、HTMLCollectionオブジェクトである。
HTMLCollectionオブジェクトから利用できるメンバーは以下

length
リストに含まれる要素数

item(i)
i番目の要素を取得

namedItem(name)
idまたはname属性が一致する要素を取得

getElementsByNameメソッド

name属性をキーに要素(Elements)を取得(get)するということである。
一般的には「input」や「select」などフォーム要素へのアクセスで利用する。
用途はラジオボタンやチェックボックスなど、name属性が等しい要素群を取得するような場合に限られる。

単一の要素を取得するなら、getElementByIdメソッドを利用する方がコードがシンプルになる。

▼ 構文 getElementsByNameメソッド

document.getElementByName(name)
name: name属性の値

例えば、テキストボックスに初期値として現在時刻をセットする場合

▼ サンプルコード

index.html
<form>
  <label for="time">時刻: </label>
  <input id="time" name="time" type="text" size="10">
</form>
script.js
var current = new Date();

//<input name="time">要素を取得
var nam = document.getElementByName("time");

//そのvalue属性を設定
nam[0].value = current.toLocaleTimeString();


テキストボックスに現在時刻を設定

nam[0]としているのは、getElementByNameメソッドの戻り値がノードの集合(NodeListオブジェクト)であるから。
一致する要素が1つであることがわかっているため、決め打ちで0番目の要素を取り出している。
要素(テキストボックス)の値を設定するには、valueプロパティを利用する。

単一の要素を取得する場合は、getElementByIdメソッドを利用する方がいい。
getElementByIdメソッドを利用した場合のjsファイルのコードは

script.js
var nam = document.getElementById("time");
nam.value = current.toLocaleTimeString();

getElementsByClassNameメソッド

class属性をキーとして、要素群(HTMLCollectionオブジェクト)を取得することができる。

特定の役割を持った要素に対して、共通のクラス名を付与しておくと、getElementsByClassNameメソッドで目的の要素をまとめて取り出すことができる。

▼ 構文 getElementsByClassName

document.getElementsByClassName(clazz)
clazz: class属性の値

例えば、class属性がframeworkである要素だけを取得し、そのリンク先を表示する場合

▼ サンプルコード

index.html
<ul>
  <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript">JavaScript \| MDN</a></li>
  <li><a href="http://semooh.jp/jquery/">JQuery日本語リファレンス</a></li>
  <li><a href="https://jp.vuejs.org/" class="framework">Vue.js</a></li>
  <li><a href="https://ja.reactjs.org/" class="framework">React</a></li>
</ul>
script.js
//ページ内のすべてのアンカータグを取得
var list = document.getElementsByClassName("framework");

//リストからアンカータグを取り出し、そのhref属性をログに出力
for(var i = 0, len = list.length; i < len; i++) {
  console.log(list.item(i).href);
}

↓ ログ出力結果

https://jp.vuejs.org/
https://ja.reactjs.org/

getElementsByClassNameメソッドの引数には、「clazz1 clazz2」のように空白区切で複数のクラス名を列記することもできる。
この場合、class属性としてclazz1 clazz2双方が指定された要素だけを検索する。

querySelector/querySelectorAllメソッド

これらのメソッドを使用することで、セレクター式を文書で検索して、合致した要素を取得することができる。
getXxxxメソッドでは特定の名前/属性値をキーに要素を取得していたのに対して、querySelector/querySelectorAllメソッドではより複雑な条件での検索を可能とする。

▼ 構文 querySelector/querySelectorAllメソッド

document.querySelector(selector)
document.querySelectorAll(selector)
selector: セレクタ一式

例えば、id="list"である要素の配下から、class="framework"であるアンカータグを取り出す場合

▼ サンプルコード

index.html
<ul id="list">
  <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript">JavaScript \| MDN</a></li>
  <li><a href="http://semooh.jp/jquery/">JQuery日本語リファレンス</a></li>
  <li><a href="https://jp.vuejs.org/" class="framework">Vue.js</a></li>
  <li><a href="https://ja.reactjs.org/" class="framework">React</a></li>
</ul>
script.js
var list = document.querySelectorAll("#list .framework");

for(var i = 0, len = list.length; i < len; i++) {
  console.log(list.item(i).href);
}

↓ ログ出力結果

https://jp.vuejs.org/
https://ja.reactjs.org/

querySelectorAllメソッドの戻り値は、getElementsByNameメソッドと同じく、条件に合致した要素を全て含んだNodeListオブジェクトである。
最初から取得すべき要素が1つとわかっている場合、あるいは要素群の最初の一つだけ取り出したい場合にはquerySelectorメソッドを利用するとよい。
この場合の戻り値は、Elementオブジェクト(単一の要素)となる。

getXxxxメソッドとqueryXxxxメソッドの使い分け方

特定のid値、class属性などで要素を検索可能な場合 => getXxxxメソッド
より複雑な検索条件で検索したい場合 => queryXxxxメソッド

querySelector/querySelectorAllメソッドは柔軟で高機能なメソッドである一方、getXxxxメソッドよりも低速である点がデメリット。
getElementByIdメソッドは高速なので、id値を指定して利用できる場合はid値で検索することがおすすめ!

最後に

今回はJavaScriptでのDOM操作で「ノード要素を取得する方法」についてまとめてきた。
今後、ノード要素の取得以外で、ノードウォーキング、イベントドリブンモデルについてもQiitaにまとめていこうと思う。

学習初学者ほどアウトプットを重視すべきだとQiitaに記事を投稿するためにまとめる度に感じている。

本記事において追記すべき内容な不備があればコメントいただけると嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者による ES6 class構文

ES6によるclass構文の概要

javascriptにおけるclass構文は、主に「クラスの拡張」に重きを置いたものであるため、構文自体は他の言語を模倣した形となっている。つまり、「インスタンス」という概念はあるものの、いわゆる「クラス」がなく、「プロトタイプという概念」だけが存在するということである。ES6でのclassキーワードでのクラスは、関数として呼び出しは出来ないが、内部的には関数である。

sample.js
class User {
 static age = 100
 constructor(name = '') {
  this.name = name
 }
 get() {
  /*コード内容*/
 }
 set() {
  /*コード内容*/
 }
}

//インスタンスの作成
const sample_user = new User('Tanaka')

特徴的なのは、constructor(Cではコンストラクタ)である。constructorはインスタンスが作成されたときに呼び出されるため、constructor内のthisはそのインスタンスを指している。constructorメソッドはオプションであり、クラスのインスタンスに対して、初期設定を行う。

またインスタンスの作成方法は、newキーワードを使う。
newキーワードで生成したインスタンスに関して後からインスタンスを追加することもでき、このことから他言語のクラスとは違うことがわかる。

classにおけるthisとbind

thisキーワードは、コンストラクターによって生成されるインスタンスを表すものである。thisキーワードはどこからでも参照できる特別な変数であるがよえに、呼び出す場所、呼び出しの方法によって中身が変わってきてしまう。
そして、class内のメソッドは、自動的にバインドがされない。バインドされないとは、あるメソッドを呼び出したときにそれがどこのclassで定義されているメソッドなのかがわからないという仕様である。例えば、

sample.js
class app {
 get() {
  const nameList = /*urlからnameのリストをダウンロードする*/
  return nameList.map(function(name) {
    return this.set(name)
  })
 }
 set(name) {
  this.name = name + ''
 }
}

一見よさそうに見えるが、getメソッドが呼び出したsetメソッド内のthisはもうそのインスタンスを指していないため、nameは正しく保存されない。
このことを解決する手段は何種類かある。
一つ目は、constructorでバインドしてしまうことだ。

sample.js
class app {
 constructor(name = '') {
  this.name = name
  this.set = this.set.bind(this)
 }
 get() {
  const nameList = /*urlからnameのリストをダウンロードする*/
  return nameList.map(function(name) {
    return this.set(name)
  })
 }
 set(name) {
  this.name = name + ''
 }
}

constructorでbind(this)を用いることで、setを呼び出したインスタンスとset内で用いられているthisを同じインスタンスにしている。C言語でいうところのポインタを同じにするということだ。
二つ目はthatをかませることだ。

sample.js
class app {
 constructor(name = ''){
  this.name = name
 }
 get() {
  const that = this
  const nameList = /*urlからnameのリストをダウンロードする*/
  return nameList.map(function(name) {
    return that.set(name)
  })
 }
 set(name) {
  this.name = name + ''
 }
}

もしくは

sample.js
 get() {
  const nameList = /*urlからnameのリストをダウンロードする*/
  return nameList.map(function(name) {
    return this.set(name)
  }, this)
 }

thatをいちいち作るとかっこ悪いのでmapの二つ目の変数にオプションとして付け加えるという方法もある。

しかし、一番使われているのはアロー関数を用いる解決策ではないだろうか。
アロー関数のthisはアロー関数が定義されたthisにバインドされる。

sample.js
 get() {
  const nameList = /*urlからnameのリストをダウンロードする*/
  return nameList.map((name) => this.set(name))
 }

このthisはそれぞれのnameに対して適切に行われる。

classの拡張

javascriptでclassを継承するときに覚えておくべきことは、やはりclassは親のプロパティを継承することである

sample.js
class Student extends Person {
 constructor(name, age, score) {
  super(name, age)
  this.score = score
 }
}

javascriptではentendsキーワードを用いて、継承を行う。superキーワードを使うことで、いちいち親のコンストラクタをもう一度呼び出さなくてもよい。また、superキーワードは親のメソッドを呼び出したいときにも使われる。

参考資料

山田祥寛様 「javascript本格入門」
JD Isaacks様 「入門javaScriptプログラミング」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【A-Frame】VRコンテンツの作り方

はじめに

社内制作でVRコンテンツを作成しました。
細かいコンテンツ作成などは置いておき、VRっぽいものを作るまでの流れが簡単だったので紹介します。

環境

OS : Windows 10 Pro
エディター : VSCode

実装

1. A-Frameの読み込み

<html>
  <head>
    <script src="https://aframe.io/releases/1.0.3/aframe.min.js"></script>
  </head>
  <body>
  </body>
</html>

2. オブジェクトの配置

<html>
  <head>
    <script src="https://aframe.io/releases/1.0.3/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-box position="0 1.17 -3.9" rotation="0 0 0" color="#ff0000"></a-box>
    </a-scene>
  </body>
</html>

この時点で下記のように出力されます。
※PCで確認しているので、スマホなどで確認するとすでにジャイロセンサーなど取得して、空間上にオブジェを配置します。
image.png

画像にテクスチャーを張ったり、3Dオブジェクトを配置したりできるので、試してみてください。
詳細は↓↓公式サイト↓↓
https://aframe.io/

3. A-Frame Inspector
値を変更して毎度、確認するのが面倒なので、A-Frame Inspectorを開いて(win:ctrl + alt + i)直接オブジェクトの配置やサイズ感を決めるとやりやすいかと思います。

image.png

※端末のOSのバージョンによって、センサーの取得状況が違うため、要実機検証が必要です。

実際にA-Frameで作ったサイトは↓こちら↓
http://www.hol-on.co.jp/greetingsite/newyear/2020/??utm_source=qiita

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML要素をアレする処理をVanillaJSとjQueryで比較

jQueryの時代は終わりなんていわれて久しいですが、まだまだ業務ではバリバリ使っている、止むを得ず使っているという方がおおいかと思います。VanillaJS(素のJavaScript)ではこう書くけどjQueryではどう書くんだっけ?という場面がしばしば。いまからわざわざいちからJQueryを覚えたくないという人向けにサイト制作時によく使う処理をまとめていこうかと思います。主に自分のメモ用。

スタイルを変更する

JSで動的にスタイルを指定する方法です。ここでは文字を大きくします。

vanilla.js
const target = document.querySelector('.hoge');

target.style.fontSize = '20px';

jquery.js
const target = $('.hoge');

target.css('fontSize', '20px');

jQueryで同時に複数のスタイルを変更したいとき

jquery.js
const target = $('.hoge');

target.css({'fontSize': '20px', 'color': 'red'});

スタイルを変更する上での注意点

プロパティ名はキャメルケース(大文字で単語を繋げる)です。cssのときのようなケバブケース(ハイフンで単語を繋げる)ではないので注意しましょう。

// この例はVanillaJSをもとにしています
// NG
target.style.font-size = '20px';
// OK
target.style.fontSize = '20px';

クラスを追加

あるクラスを追加します。openというクラスを追加してみましょう

vanilla.js
const target = document.querySelector('.hoge');

target.classList.add('open');

// 複数クラスを追加する場合
target.classList.add('open', 'active'); // コンマ区切り

jquery.js
const target = $('.hoge');

target.addClass('open');
// 複数クラスを追加する場合
target.addClass('open acitve'); // 半角スペース区切り

クラスを追加する時の注意点

追加したいクラスがすでに要素のclass属性に存在した場合は、無視されます。クラス名に「.(ドット)」は不要です。

// この例はVanillaJSをもとにしています
// NG
target.classList.add('.open');
// OK
target.classList.add('open');

クラスを削除

あるクラスを削除します。openというクラスを削除してみましょう

vanilla.js
const target = document.querySelector('.hoge');

target.classList.remove('open');

// 複数クラスを削除する場合
target.classList.remove('open', 'active'); // コンマ区切り

jquery.js
const target = $('.hoge');

target.removeClass('open'); 
// 複数クラスを削除する場合
target.removeClass('open acitve'); // 半角スペース区切り

クラスを削除するときの注意点

存在しないクラスを削除しようとしてもエラーはスローされません。

クラスを切り替え

あるクラスを切り替えます。切り替えるとは、

  • 切り替えたいクラスがなかったら追加
  • 切り替えたいクラスがすでにあれば削除

という動作のことをいいます。

openというクラスを切り替えてみましょう

vanilla.js
const target = document.querySelector('.hoge');

target.classList.toggle('open');

jquery.js
const target = $('.hoge');

target.toggleClass('open'); 

クラスがあるかどうか判定

openというクラスがあるかどうか調べます。

vanilla.js
const target = document.querySelector('.hoge');

console.log(target.classList.contains('open')); // true

jquery.js
const target = $('.hoge');

console.log(target.hasClass('open')); // true

複数要素に対して同一の処理をする

hogeというクラスがついた要素に対してactiveというクラスを追加してみましょう。

vanilla.js
const target = document.querySelectorAll('.hoge');

target.forEach((index) => {
    index.classList.add('active');
});

jquery.js
const target = $('.hoge');

target.each((index, elment) => {
    $(elment).addClass('active');
});

複数要素に対して同一の処理をするうえでの注意点

VanillaJSのforEachとjQueryのeachでは引数の中身がそれぞれ違うので注意です。

forEach.js
// 第一引数が要素
// 第二引数がインデックス
target.forEach((element, index) => {
  // 処理
});
each.js
// 第一引数がインデックス
// 第二引数が要素
target.each((index, element) => {
  // 処理
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.jsアプリでページ遷移時にプログレスバー(ページ上部に出るやつ)を出す

progress7.gif

なかなかにミニマルなフレームワーク「Next.js」ではデフォルトではプログレスバーを表示しません。
Nuxt.jsなら勝手にやってくれるんだけどな…と思いつつ、ここではNext.jsでの実現方法を書いておきます。超簡単です!

必要なもの

nextjs-progressbar

以上!

実装方法

まず、当然ですけどnextjs-progressbarをインストールします。

$ yarn add nextjs-progressbar

/pages/_app.js(ない場合は作る)(_app.jsって何、という方はこちらをどうぞ)を編集します。

/pages/_app.js
import React from 'react'
import App from 'next/app'
import NextNprogress from 'nextjs-progressbar' // ここ!

class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props
    return (
      <>
        <NextNprogress /> { /* ここ! */ }
        <Component {...pageProps} />
      </>
    )
  }
}

export default MyApp

ここで一旦宣伝です。

宣伝

先日、@svfreerider さんと共にRemote Club|海外移住や海外フリーランスの情報サイトというサービスを出しました!
既に海外移住した人と、これからしたいと考えている人のつながりを作り、より多くの方々が好きなところで生きていけるようになればいいという思いからRemote Clubを作りました。

少しでも海外移住に興味のある方、既に移住している方は是非Remote Clubを覗いていっていただけると嬉しいです!
フィードバックも待ってます!
よろしくおねがいします!

まとめ

Next.jsでも特に困ることなくプログレスバーを実装できました。
Nuxt.jsだとデフォルトで表示されるし便利、と思ったけど、まぁ必要な人は追加する、というスタンスのほうが自然な気もしました。
遷移先でAPIなどを使って外部からデータを取得する場合は、どうしても遷移に時間がかかり不自然に感じてしまいます(自分は)。
ということで、クライアントでの遷移にはプログレスバー表示したい!というかたは是非どうぞ!

ちなみにnextjs-progressbar作者の方の記事はこちらです。詳しく知りたい方は覗いてみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[トリビアの種] TaskQueueはオーバーフローするのか?結果は…ォアフン

この記事は、私が素朴な疑問を抱いたところから検証してみたまでの一部始終です。
勘違い等がありましたら、ご指摘いただけますと幸いです。

結果だけを知りたい方はこちら。

エラーは発生しなかった。

今回のお便りはこちら

ある日、JavaScriptの非同期処理について勉強していた時にスタックとキューの存在を知りました。1
これが「スタックにタスクが積まれすぎてオーバーフローになるのか!」と膝を打ちました。
それと同時に、キューオーバーフローって聞いたことないなと疑問に思いました。
そこで、キューオーバーフローになるようにあえてコードを実行するとどうなるのでしょうか?
これって種になりませんか?よろしくお願いします。

このトリビアの種つまりこういうことになります

キューオーバーフローになるようにあえてコードを実行すると結果は、、、「ォアフン」。

スタックオーバーフローとは

wikipedia先生お願いします。

スタックオーバーフロー (英: stack overflow) は、コンピュータプログラムにおいて、コールスタック領域の限界を超えたデータプッシュにより発生する、バッファオーバーフローの一種である。2

専門家に聞いてみた(現場の先輩)

Q. どのように確認すれば良い?
A. エラーの発生を確認する。若しくはブラウザ上で操作に影響があればそれがオーバーフローしていると判断して良いでしょう。

Q. 実行環境は?
A. JavaScriptは一般的にブラウザで実行されるので、最もメジャーであるChromeで確認すれば良いでしょう。

検証方法

  • Chrome(バージョン:78.0.3904.108)
  • 実行時エラーが発生するか
  • ブラウザの挙動に変化はないか

まずはスタックオーバーフローを発生させてみた

今回検証に使用するコードはこちら

function push() {
  try {
    return push();
  } catch (e) {
    console.log(e);
  }
}
push();

結果

エラーが発生した。
RangeError: Maximum call stack size exceeded
スクリーンショット 2020-01-07 10.00.11.png

キューオーバーフローが発生するか検証してみた

今回検証に使用するコードはこちら

enqueue()を抜け無ければ、延々とキューにタスクを詰め込む。

※無限ループに注意。

function enqueue() {
  while (1) {
    try {
      setTimeout(() => {console.log('詰めまくり!')}, 0);
    } catch (e) {
      console.log(e);
    }
  }
}
enqueue();

結果

エラーは発生しなかった。

無限ループで固まってしまったため強制終了。
スクリーンショット 2020-01-07 10.01.43.png

ここに新たなトリビアが誕生した

キューオーバーフローになるようにあえてコードを実行すると、、、エラーは発生しない。

番外編

キューに積みまくった状態でスタックへぶち込むとどうなるのか検証してみた。

まずはこちらで最大値を計測する。

let n = 0;
function push() {
  try {
    ++n;
    return push();
  } catch (e) {
    console.log(`${n}個目のタスクでエラーでした。`);
  }
}
push();

結果

image.png

続いてこちらを実行する

ループ処理はこちらを参考にさせていただいた。3
想定では、enqueue()を抜けた後にキューに詰められたタスクがスタックに積まれるはずだが。。

function enqueue() {
  for (let i = 0; i < 10452; i++) {
    try {
      setTimeout(() => {() => {console.log('詰めまくり!')}}, 0);
    } catch (e) {
      console.log(e);
    }
  }
  // キューに積み切るまでの時間稼ぎ
  const ms = new Date();
  while (new Date() - ms < 100000);
}
enqueue();

結果

エラーは発生しなかった。

スクリーンショット 2020-01-07 9.38.31.png

感想

あくまで予想だが、オーバーフローしないようにある程度分割してスタックに積んでいるのだろうか...?
はたまた、webapisが上手いことやってくれている...?
検証は以上となります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【 Laravel 】Laravel DB.comで『 CRUD簡単作成 』

Laravle DB.com を使って 「 ER図 → CRUD生成 」を試す!

以前にも書きましたが、Laravle DB.comでは「CRUD」のコードまで生成するようになりました。\(^o^)/

前提条件

  • Laravelで環境を作ったことがある人(とりあえず動作するまで)
  • Laravelの基本を理解している人(つもりの人)
  • 確認環境:Laravel5.5/PHP7.2.1/MySQL5.6.38 (Chromeで確認)

メリット

  • CRUDを自動生成!
  • 超便利! ER図を保存できるので、1度作ったものを別のアプリにも転用しやすい!
  • どのPCからでも同じデータを操作できる(ブラウザアプリだから)!

さあ、はじめて行きましょう!


【 LaravelDB.comへ ログイン 】

LaravelDB.com
Googleアカウントのみログイン可能です。
※現在はGoogleアカウントを持っていない人は少ないですからね〜
LaravelDB_com.jpg


【 LaravelDB.comの POINT!! 】

ER図を作成していきますが重要事項があります!

<<重要>>
主キーのAutoIncrementは どのテーブルも『 id 』 と固定すること!!

image.png

LaravelのEloquentModel を便利に使うためには、どのテーブルも 『 id 』 と固定するのが吉です。
理由は「 モデル名::find($id); 」と便利に使う場合は 主キーのAutoIncrement 『 id 』 名にしておかないと動作しないからです( Laravelを勉強してる人はなんとなく知ってることでしょうか )。
※ laravelDB.comのCRUDでも生成されるファイルは「 ○○○::find($id); 」を使ってるためそ!
※ laravelに最初から入っているテーブル「user」「password_resets」は作成しないこと(migrate時に上書きしてしまいます)
※慣れれば簡単ですが、少しだけ慣れる時間は必要ですね。


【 CRUDを生成してみる! 】

まずは、ER図を簡単に作ってみましょう!!
その後に右メニュー「 ER図のLoad/Save 」をクリックすると以下画面が表示されます。
image.png
CRUDを生成するボタンが新しく出来ています!!!
※まだデバッグが全て終わってないため「BETA」と書いてますが、クリックです!!!


【 CRUD: 新しいプロジェクトを用意 】

最低限Laravelがインストールした状態 を準備します!
image.png


【 CRUD: ダウンロードファイルを確認しましょう 】

Zip圧縮ファイルがダウンロードされるので、Zipを展開(解凍)して中を見てみましょう!
CRUDに必要なファイルが一式入っています。
image.png
ダウンロードしたファイル( 各フォルダ内のファイル )を移動しましょう!
※welcome.blade.phpはダウンロードしたファイルへのリンクが生成されているので、上書きしてください。


【 CRUD: 配置しおわったら「 Migrate 」しましょう 】

DBや.env設定など最低限の設定が終わっていたら

php artisan migrate

で、テーブルを作成してください。


【 完成: ブラウザで確認しましょう! 】

http://localhost/ (URLは開発環境によって異なります) で確認しましょう!
画面の中央に今までなかったリンクが出来ています。あなたが作ったテーブルのCRUD画面へ遷移できますよ!!

image.png


画面が古いままの場合(Cacheかも)

以下コマンドでCacheをクリアしてみましょう

php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

動作した後はコードを見ると良いでしょう

  • Routing
  • Controller
  • Views
  • Model 後は、自分で変更をいれていくだけですね。

こちらのイベントで操作方法等の勘所など解説する予定!

【G'sCreativeCollection #3】GEEKs_Output_LT
Outline】
・日時:1月11日(土)17:00~19:30
・会場:ShibuyaQWS(渋谷スクランブルスクエア15F)内 
・参加費:無料
・定員:30名


日本から海外へ向けたアウトプット

laravelDB.com は日本人が作り、海外でも使われるプロダクトになることを願っております。

Twitterアカウント: LaravelDB.com

m(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【 Laravel 】Laravel DB.comで『 CRUD簡単作成 』を解説

Laravle DB.com を使って 「 ER図 → CRUD生成 」を試す!

以前にも書きましたが、Laravle DB.comでは「CRUD」のコードまで生成するようになりました。\(^o^)/

私の開発環境

  • Laravel5.5/PHP7.2.1/MySQL5.6.38 (Chromeで確認)

メリット

  • CRUDを自動生成!
  • 超便利! ER図を保存できるので、1度作ったものを別のアプリにも転用しやすい!
  • どのPCからでも同じデータを操作できる(ブラウザアプリだから)!

さあ、はじめて行きましょう!


【 LaravelDB.comへ ログイン 】

LaravelDB.com
Googleアカウントのみログイン可能です。
※現在はGoogleアカウントを持っていない人は少ないですからね〜
LaravelDB_com.jpg


【 LaravelDB.comの POINT!! 】

ER図を作成していきますが重要事項があります!

<<重要>>
主キーのAutoIncrementは どのテーブルも『 id 』 と固定すること!!

image.png

LaravelのEloquentModel を便利に使うためには、どのテーブルも 『 id 』 と固定するのが吉です。
理由は「 モデル名::find($id); 」と便利に使う場合は 主キーのAutoIncrement 『 id 』 名にしておかないと動作しないからです( Laravelを勉強してる人はなんとなく知ってることでしょうか )。
※ laravelDB.comのCRUDでも生成されるファイルは「 ○○○::find($id); 」を使ってるためそ!
※ laravelに最初から入っているテーブル「user」「password_resets」は作成しないこと(migrate時に上書きしてしまいます)
※慣れれば簡単ですが、少しだけ慣れる時間は必要ですね。


【 CRUDを生成してみる! 】

まずは、ER図を簡単に作ってみましょう!!
その後に右メニュー「 ER図のLoad/Save 」をクリックすると以下画面が表示されます。
image.png
CRUDを生成するボタンが新しく出来ています!!!
※まだデバッグが全て終わってないため「BETA」と書いてますが、クリックです!!!


【 CRUD: 新しいプロジェクトを用意 】

最低限Laravelがインストールした状態 を準備します!
image.png
この時点ではまだLOGINは作らないように!


【 CRUD: ダウンロードファイルを確認しましょう 】

Zip圧縮ファイルがダウンロードされるので、Zipを展開(解凍)して中を見てみましょう!
CRUDに必要なファイルが一式入っています。
image.png
ダウンロードしたファイル( 各フォルダ内のファイル )を移動しましょう!
※welcome.blade.phpはダウンロードしたファイルへのリンクが生成されているので、上書きしてください。


【 CRUD: 配置しおわったら「 Migrate 」しましょう 】

DBや.env設定など最低限の設定が終わっていたら

php artisan migrate

で、テーブルを作成してください。


【 完成: ブラウザで確認しましょう! 】

http://localhost/ (URLは開発環境によって異なります) で確認しましょう!
画面の中央に今までなかったリンクが出来ています。あなたが作ったテーブルのCRUD画面へ遷移できますよ!!

welcome.blade.php
image.png

作成されたindex/show/editの画面
基本的な処理が最初から出来てます!捗りそうですね!
image.png


画面が古いままの場合(Cacheかも)

以下コマンドでCacheをクリアしてみましょう

php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

動作した後はコードを見ると良いでしょう

  • Routing
  • Controller
  • Views
  • Model 後は、自分で変更をいれていくだけですね。

こちらのイベントで操作方法等の勘所など解説する予定!

【G'sCreativeCollection #3】GEEKs_Output_LT
Outline】
・日時:1月11日(土)17:00~19:30
・会場:ShibuyaQWS(渋谷スクランブルスクエア15F)内 
・参加費:無料
・定員:30名


日本から海外へ向けたアウトプット

laravelDB.com は日本人が作り、海外でも使われるプロダクトになることを願っております。

Twitterアカウント: LaravelDB.com

m(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

高階関数、カリー化、部分適用

背景・動機

業務でReduxを利用していますが、公式サイトに以下のようなコードが現れビビったので調べました。

const middleware = store => next => action => {
  next(action);
}

高階関数とは

関数を引数にとったり、関数を戻り値とする関数のこと。

カリー化とは

高階関数を使うと、以下のようなadd関数を定義することができます。

const add = function(a) {
  return function(b) {
    return a + b;
  }
}
add(1)(2); // 3

addのように、引数を1つずつとって関数を返し、関数がつながっている状態にすることをカリー化と言います。
上記は簡略化して以下のように記述できます。

const add = a => b => a + b;

部分適用とは

カリー化された関数は、任意の引数を固定した別の関数を作成することができます。
これを部分適用と言います。

const add = a => b => a + b;
const add2 = add(2);
add2(3); // 5

どういったメリットがあるの?

関数を再利用する際に、毎回気にしなくて良い引数を固定化できます。
例えばAPI Requestを行う関数を考えてみます。
まずは部分適用を用いないパターン。

const apiRequest = (method, path, data) => {
  return fetch(path, { method: method, body: JSON.stringify(data) });
}
apiRequest('GET', 'http://example.com', { param1: 'hogehoge' });
apiRequest('GET', 'http://example.com', { param2: 'hogehoge' });
apiRequest('GET', 'http://example.com', { param3: 'hogehoge' });

上記は部分適用を用いることで、以下のように記述できます。

const apiRequest = method => path => data => {
  return fetch(path, { method: method, body: JSON.stringify(data) });
}
const get = apiRequest('GET');
const getExample = get('http://example.com');

getExample({ param1: 'hogehoge' });
getExample({ param2: 'hogehoge' });
getExample({ param3: 'hogehoge' });

引数を固定化することで、引数の数を減らしシンプルにできました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npm run serveの速度改善

npm run serveが遅い

Vue.jsで書いたプログラムを確認しようとした時に、
npm run serveをしますが、動作が遅いし、他のブラウジングも同時に遅くなってしまう現象が発生しました。

原因

ESETなどのセキュリティソフトが影響しているみたい。

回避方法

Vue.jsのプロジェクトルートに、vue.config.jsというファイルを作成し、配置する。中身はこんな感じ。

module.exports = {
  devServer: {
    port: 8009,
  },
};

これで
npm run serveすると、
指定した通り、8009ポートで立ち上げている。

 2020-01-07 8.47.20.jpg

参考サイト

https://blog.kobasato.net/entry/vue-cli-yarn-serve-npm-run-serve-issue/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript の最新技術情報 (ECMAScript) を確認する方法

はじめに

three.js で簡単な星空を描画するプログラムを作成して以降も、何かと javascript を使ってきましたが、ふと、javascript で実装される新機能などはどのようにして決定するのかという疑問を持ちました。

調べた結果、Ecma International による正式仕様となるまでの過程が公開されているようなので、備忘のために投稿しました。

実装する機能を決定する組織等について

Ecma International という情報通信システムの分野における国際的な標準化団体の元で、ECMAScript (エクマスクリプト) という javascript の標準仕様として、手続きが行われています。

ECMAScript は、ECMA-262 という規格番号で標準化されており、Edition 番号で更新が行われています。
2015年までは 単純に 1, 2, 3, ... 6 といったような形でしたが、2016年以降はその年を Edition 番号とするようになりました。

今年は 2020年なので、Edition は「ECMAScript 2020」となります。

新仕様が確認できるページ

正式仕様になるまでのプロセスとスケジュール

プロセスについては、The TC39 Process (https://tc39.es/process-document/) のページに記載されています。
0 から 4 のステージがあり、stage4 になってかつ EMCA 総会で承認されると、正式仕様となります。

スケジュールについてですが、The TC39 Process の「Spec revisions and scheduling」に記載されている以下のスケジュールを経て、毎年 7 月に承認されて正式仕様となります。

  •   ~ 1月:新仕様の候補が決定されます。
  • 2月 ~ 3月:ロイヤリティーフリーで使用できます。
  •     3月:TC39 によって、stage4 のものが新仕様として組み込まれます。
  • 4月 ~ 6月:ECMA CC と ECMA GA によるレビューが行われます。
  •     7月:ECMA 総会にて、正式仕様として承認されます。

新仕様の候補(本投稿公開時点:1/7)

新仕様の候補を確認したい場合は、emca262 の Github の README.md にある Finished Proposals で見ることができます。
具体的には、「Expected Publication Year」列が 2020 のものとなります。

ただ、stage4 になったけれども諸事情により stage3 になっている仕様 (例えば、「for-in mechanics」) があるため、実際に Finished Proposals の一覧から各 proposal の詳細を見た方がよいと思われます。

次の 8 つが新仕様の候補となっていました。

なお、各詳細ページに使用例が記載されていますので、非常に参考になります。

最後に

各新仕様の候補の具体的な使い方についてはこれから確認していくことになりますが、いったんは javascript の最新の仕様情報をどのように確認するのかを纏めてみました。

皆様の情報収集の一助になれば幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript の最新技術情報 (ECMAScript2020) を確認する方法

はじめに

three.js で簡単な星空を描画するプログラムを作成して以降も、何かと javascript を使ってきましたが、ふと、javascript で実装される新機能などはどのようにして決定するのかという疑問を持ちました。

調べた結果、Ecma International による正式仕様となるまでの過程が公開されているようなので、備忘のために投稿しました。

実装する機能を決定する組織等について

Ecma International という情報通信システムの分野における国際的な標準化団体の元で、ECMAScript (エクマスクリプト) という javascript の標準仕様として、手続きが行われています。

ECMAScript は、ECMA-262 という規格番号で標準化されており、Edition 番号で更新が行われています。
2015年までは 単純に 1, 2, 3, ... 6 といったような形でしたが、2016年以降はその年を Edition 番号とするようになりました。

今年は 2020年なので、Edition は「ECMAScript 2020」となります。

新仕様が確認できるページ

正式仕様になるまでのプロセスとスケジュール

プロセスについては、The TC39 Process (https://tc39.es/process-document/) のページに記載されています。
0 から 4 のステージがあり、stage4 になってかつ EMCA 総会で承認されると、正式仕様となります。

スケジュールについてですが、The TC39 Process の「Spec revisions and scheduling」に記載されている以下のスケジュールを経て、毎年 7 月に承認されて正式仕様となります。

  •   ~ 1月:新仕様の候補が決定されます。
  • 2月 ~ 3月:ロイヤリティーフリーで使用できます。
  •     3月:TC39 によって、stage4 のものが新仕様として組み込まれます。
  • 4月 ~ 6月:ECMA CC と ECMA GA によるレビューが行われます。
  •     7月:ECMA 総会にて、正式仕様として承認されます。

新仕様の候補(本投稿公開時点:1/7)

新仕様の候補を確認したい場合は、emca262 の Github の README.md にある Finished Proposals で見ることができます。
具体的には、「Expected Publication Year」列が 2020 のものとなります。

ただ、stage4 になったけれども諸事情により stage3 になっている仕様 (例えば、「for-in mechanics」) があるため、実際に Finished Proposals の一覧から各 proposal の詳細を見た方がよいと思われます。

次の 8 つが新仕様の候補となっていました。

なお、各詳細ページに使用例が記載されていますので、非常に参考になります。

最後に

各新仕様の候補の具体的な使い方についてはこれから確認していくことになりますが、いったんは javascript の最新の仕様情報をどのように確認するのかを纏めてみました。

皆様の情報収集の一助になれば幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非Javascriptエンジニアのための「Javascriptの関数」

前回 - 非Javascriptエンジニアのための「非Javascriptエンジニアのための「Javascriptの配列」」

ITエンジニアのアクマちゃん(@akumachanit)デモ!
この記事はJavaScript 「再」入門をベースに書いているデモ。

あけましておめでとうデモ。年末は体調を崩してしまってAdvent Calendarを書ききれなかったデモけど、引き続き「非Javascriptエンジニアのための」記事は書いていきたいと思いますのでよろしくお願いしますデモ!

関数の基本

今回はJavascriptの関数について書いていくデモ!
関数の基本の書き方はこんな感じデモ。

function add(x, y) {
  const total = x + y;
  return total;
}

これが一番シンプルな書き方デモね。引数は0以上の任意の数を設定することができるデモ。
returnでいつでも返り値を返すことができるデモ。returnを指定しなかったり、空の値をreturnした場合はundefinedを返すデモ。

関数の引数

関数は、設定された引数に値を渡さなくても呼び出すことができるデモ。
値を渡されなかった引数にはundefinedが設定されるデモ。

add(1); // 1 + undefined でNaNが返る
add(); // undefined + undefined でNaNが返る 

逆に設定された引数よりも多い引数が渡された場合は溢れた引数の値は無視されるデモ。

add(1,4,7); // 1 + 4 で5が返る 7は無視される

設定された引数よりも多くの引数を渡すのは無意味なように見えるデモけど、それらにアクセスする方法があるデモ。

arguments

設定された引数の数を超えて渡された値はargumentsという変数からアクセスできるデモ。arguments配列様(Array-like)のオブジェクトデモ。
配列様オブジェクトArrayの組み込みメソッドは持たないものの、lengthプロパティと0から始まる添字を持つデモ。

argumentsを使用する関数を書いてみるデモ。

function add() {
  let total = 0;
  for (const num of arguments) {
    total += num;
  }
  return sum;
}

add(1,2,3); // 6

argumentsには設定された引数に関係なく渡された値がすべて格納されているデモ。次に説明するrest parametersとはそこが違うので注意デモ。

rest parameters

rest parametersは任意の数の引数を配列として受け取れる構文デモ。

function add(x, ...args) {
  console.log(x);
  let total = 0;
  for (const num of args) {
    total += num;
  )
  return total;
}

add(1,2,3,4,5); // 2 + 3 + 4 + 5 で14

関数の最後の引数に...を付けることで任意の数の引数を配列として受け取るデモ。
...のついた変数より前の値(上記コードではxに渡された1)はrest parametersには含まれないデモ。

argumentsとrest parametersの違い

argumentsとrest parametersはほとんど同じように使えそうだけど、どう違いがあるデモかね?

  • 前述の通り、argumentsは渡された全ての値が含まれるが、rest parametersは...のついた変数以前の変数に渡された値は含まない。
  • argumentsは配列様オブジェクトだが、rest parametersは配列である。

そもそもrest parametersは、argumentsで実装した関数内で配列様オブジェクトであるargumentsを配列に直す手間を省くためにつくられたらしいデモ。

関数式

関数式(functionキーワード)を使えば式の中で関数を定義することができるデモ。

無名関数

関数式には名前付き無名があるデモ。
無名関数は以下の様に作るデモ。

let add = function() {
  let total = 0;
  for (const num of arguments) {
    total += num;
  }
  return total;
}

add(1,2,3); // 6

出来上がった関数の機能は前述のadd関数と同じデモ。

名前付き関数

名前付き関数式は以下のように定義するデモ。

let add = function add() {
  let total = 0;
  for (const num of arguments) {
    total += num;
  }
  return total;
}

add(1,2,3); // 6

名前を付けるメリットは
- つけた名前を用いて、関数内で再帰呼び出しをすることができる
- エラーが発生した際に、スタックトレースに関数名が表示されるためデバッグがしやすくなる
ことデモ。

参考

JavaScript 「再」入門
関数

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クリックでテキストをコピー【JavaScript】

コード

<h2>
  <span>コピーしないテキスト</span>
  <span class="text">コピーするテキスト</span>
  <span>コピーしないテキスト</span>
  <span class="copy-text-btn">【ここをクリックでテキストをコピー】</span>
</h2>
textCopy.js
window.addEventListener('load', function(){
  $('.copy-text-btn').on('click',function(){
    var textElem = $(this).parent().find('.text');
    window.getSelection().selectAllChildren(textElem[0]);
    document.execCommand("copy");
    window.alert("テキストをコピーしました!");
  });
});

動作実演GIF

2020-01-07 01.05.07.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

async/await完全に理解した(JavaScript)

きっかけ

最近Node.jsを触る機会があるが、言語を体系的に勉強してきたわけではないので、必要な機能から調べながら使っている。
あるとき一定時間待ってから次の処理を行うシーケンス的なことをしたいなと思って調べたところ、やりたいことは「非同期処理」と言われるようだ。次のようなサンプルコードが出てきた。

コールバックを使う方法
const t0 = Date.now(); // 開始時刻を取得
console.log('開始: ' + (Date.now() - t0)); // 経過時刻をあわせて出力
setTimeout(function() {
    console.log('1秒後に実行されるはず: '+ (Date.now() - t0)); // 経過時刻をあわせて出力
}, 1000);
出力
開始: 0
1秒後に実行される: 1004

あーそーゆーことね完全に理解した(画像は脳内補完願います)
よし、ちゃんと1秒後に実行された。
じゃあ処理を2段階にしてみよう。

コールバックを使う方法2
const t0 = Date.now();
console.log('開始: ' + (Date.now() - t0));
setTimeout(function() {
    console.log('1秒後に実行されるはず: '+ (Date.now() - t0));
    setTimeout(function() {
        console.log('2秒後に実行されるはず: '+ (Date.now() - t0));
    }, 1000);
}, 1000);
出力
開始: 0
1秒後に実行されるはず: 1003
2秒後に実行されるはず: 2004

あーそーゆーことね完全に理解した(画像は脳内補完願います)
じゃあ10個に増やしてみよう。

コールバックを使う方法4
const t0 = Date.now();
console.log('開始: ' + (Date.now() - t0));
setTimeout(function() {
    console.log('1秒後に実行されるはず: '+ (Date.now() - t0));
    setTimeout(function() {
        console.log('2秒後に実行されるはず: '+ (Date.now() - t0));
        setTimeout(function() {
            console.log('3秒後に実行されるはず: '+ (Date.now() - t0));
            setTimeout(function() {
                console.log('4秒後に実行されるはず: '+ (Date.now() - t0));
                setTimeout(function() {
                    console.log('5秒後に実行されるはず: '+ (Date.now() - t0));
                    setTimeout(function() {
                        console.log('6秒後に実行されるはず: '+ (Date.now() - t0));
                        setTimeout(function() {
                            console.log('7秒後に実行されるはず: '+ (Date.now() - t0));
                            setTimeout(function() {
                                console.log('8秒後に実行されるはず: '+ (Date.now() - t0));
                                setTimeout(function() {
                                    console.log('9秒後に実行されるはず: '+ (Date.now() - t0));
                                    setTimeout(function() {
                                        console.log('10秒後に実行されるはず: '+ (Date.now() - t0));
                                    }, 1000);
                                }, 1000);
                            }, 1000);
                        }, 1000);
                    }, 1000);
                }, 1000);
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);

いやよく見たらクソむかつく(画像は脳内補完願います)

なんだこのネストの嵐は。
調べてみると、これはJavaScript界隈で言われているコールバック地獄というやつらしい。
更に調べてみると、イマドキのJavaScriptにはコールバック地獄を解消する方法が用意されているということがわかった。ですよねー。

Promise と async/await

コールバック地獄を解消する方法は2種類ほどあるらしい。
1. Promiseを使う方法
2. async/awaitを使う方法
後者のasync/awaitの方が新しくてイマドキらしい。ということで前者のPromiseは飛ばしても大丈夫かな。
というわけで、async/awaitの概要を調べてみた。

引用:async/awaitを使ったモダンな非同期処理 - Qiita

リピートミー。「async/awaitはPromiseで作られている」。

!!??

async関数はPromiseを返します。
この関数を呼び出すときにawaitを付けると、このコードはPromiseがresolvedかrejectedを返すまで停止します。

async/awaitを完全に理解するには、結局Promiseを理解しないといけないらしい

async/awaitの完全理解

この辺をじっくり読んでいろいろコードを書いて試した。

コードと解説

先程の10回待つ処理をasync/awaitを使って書くとこうなる。

async/awaitを使ったコード
const t0 = Date.now();
console.log('開始: ' + (Date.now() - t0));
asyncCall();

function wait1sec() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, 1000);
    });
}

async function asyncCall() {
    await wait1sec();
    console.log('1秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('2秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('3秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('4秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('5秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('6秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('7秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('8秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('9秒後に実行されるはず: '+ (Date.now() - t0));
    await wait1sec();
    console.log('10秒後に実行されるはず: '+ (Date.now() - t0));
}
出力
1秒後に実行されるはず: 1009
2秒後に実行されるはず: 2013
3秒後に実行されるはず: 3015
4秒後に実行されるはず: 4019
5秒後に実行されるはず: 5022
6秒後に実行されるはず: 6028
7秒後に実行されるはず: 7029
8秒後に実行されるはず: 8036
9秒後に実行されるはず: 9042
10秒後に実行されるはず: 10046

おまけ

async は「エイシンク」と読むらしい。「アシンク」だと思ってた。
await は「アウェイト」で良いらしい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでToDoアプリを作成してみました

はじめに

この記事ではJavaScriptのライブラリであるReactを使用して簡単なToDoアプリの実装を行います。
クライアント側のみの実装になります。
Reactのドキュメントチュートリアル(三目並べ)を一通り行った後の練習になるように書きたいと思います。
環境構築に関しては、create-react-appを使用して作成しています。環境構築に関しては以前書いた記事があります。
もちろん、オレオレな環境でもokです。
Reactについては初心者なので認識の齟齬や到らない点も多々あると思いますが、よろしくお願いします。

目次

  1. 環境準備
  2. コンポーネントの確認
  3. ファイル構成
  4. Reactで保持するデータについて
  5. 各コンポーネントの解説
  6. まとめ

1. 環境準備

以下からソースコードを引っ張ってきます。
ソースコード
gitでクローンした場合は、ブランチはtodo-app-pure-cssです。(何かダサい名前なのは目を瞑っておいてください。。。)

1-1. DockerImageのビルド

docker build --rm -f "react-tutorial/Dockerfile" -t react-tutorial:latest "react-tutorial"

1-2. DockerContainerの起動

$ docker run --rm -it -v ${PWD}/app:/home/react-tutorial  -p 3000:3000/tcp react-tutorial:latest /bin/bash
root@03887209ce2d:/home# 

1-3. 追加のパッケージをインストール(コンテナの内部で操作してます)

root@03887209ce2d:/home# cd react-tutorial
root@03887209ce2d:/home/react-tutorial# yarn install

1-4. Reactアプリケーションの起動

root@03887209ce2d:/home/react-tutorial# yarn start

1-5. ブラウザで確認

Reactアプリケーションの起動に成功すると、以下のような表示がされます。

Compiled successfully!

You can now view react-tutorial in the browser.

  Local:            http://localhost:3000/
  On Your Network:  http://172.17.0.3:3000/

Note that the development build is not optimized.
To create a production build, use yarn build.

ブラウザでhttp://localhost:3000/を入力して開いてみましょう。
そうすると、下記の様な画面が表示されるはずです。
今回は、この画面をReactで作成していきたいと思います。

SampleReactToDoApp.png

2. コンポーネントの確認

ReactはUIのパーツをコンポーネントという独立した一つの部品とみなして構成していきます。
今回の例では下記の様に分割しました。

ReactToDoApp.png

ToDoアプリケーションを構成するコンポーネントは全部で4つあります。

  • ToDo
    ToDoアプリケーションの全体を表します

  • TaskAdd
    新しいタスクの追加を行います

  • TaskList
    追加されたタスクをリストにして表示します

  • TaskItem
    一つのタスクを表します

3. ファイル構成

ファイル構成を確認しましょう。

 フォルダ構成
.
├── Dockerfile
├── README.md
└── app
    ├── README.md
    ├── package.json
    ├── public
    │   ├── favicon.ico
    │   ├── index.html
    │   ├── logo192.png
    │   ├── logo512.png
    │   ├── manifest.json
    │   └── robots.txt
    ├── src
    │   ├── App.js
    │   ├── App.scss
    │   ├── components
    │   │   ├── Header.js
    │   │   ├── Task.js
    │   │   ├── TaskAdd.js
    │   │   ├── TaskList.js
    │   │   └── ToDo.js
    │   └── index.js
    ├── yarn-error.log
    └── yarn.lock

沢山のファイルがありますが、今回の記事で注目するのはapp/src以下のファイルのみです。

フォルダ構成
src
├── App.js
├── App.scss
├── components
│   ├── Header.js
│   ├── Task.js
│   ├── TaskAdd.js
│   ├── TaskList.js
│   └── ToDo.js
└── index.js

ファイルの解説(ToDo Appのコンポーネントを除く)

components配下のファイルが先ほど確認した各コンポーネントに対応しています。
後ほど、詳しく見ていきます。

Header.js

Header.jsはアプリのタイトルのToDoを表しているのみです。
公式ドキュメントで出てくるHelloWorldと同じですね。

Header.js
import React from "react";

const Header = () => {
  return (
    <header>
      <h1>ToDo</h1>
    </header>
  );
};

export default Header;

App.scss

App.scssはレイアウトの部分を書いてあります。
デフォルトの状態では見にくいので、見やすい様に少しレイアウトを調整しています。
今回の内容とはあまり関係無いので割愛します。

index.js

index.jsAppコンポーネントをレンダリンしています。
では、Appコンポーネントを見てみましょう。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

App.js

App.jsHeaderコンポーネントToDoコンポーネントをレンダリングしています。

App.js
import React from 'react';
import 'reset-css'
import Header from './components/Header';
import ToDo from './components/ToDo'
import './App.scss';

function App() {
  return (
    <div className="App">
      <Header />
      <ToDo />
    </div>

  );
}

export default App;

4. Reactで保持するデータについて

各コンポーネントの説明に入る前にReactで保持して、各コンポーネントにどんなデータを渡すのかを説明します。
今回のToDoアプリはタスクの作成、更新、削除ができます。
では、ToDo AddコンポーネントTask Listコンポーネントで共通で使用したいデータは何でしょうか??
言い換えると、ToDoコンポーネントで持っておいた方が楽なデータは何でしょうか??
タスクの新規作成時には、既に同じ名前のタスクが登録されていないか確認したいのでタスクのリストTask Listコンポーネントで保持するより
ToDoコンポーネントに持つ方が、ToDo Addコンポーネントが参照しやすく無いでしょうか??
また、今回はDBなどの保存する機能はありませんが後々のことを考慮するとなれば各タスクの識別子(id)が必要になってくると考えれます。
なので、ToDoコンポーネントにはタスクのIDを持たせる様にします。
ここまでを、再度コンポーネントのイメージと一緒に確認すると以下の様になります。

ReactToDoApp-state (2).png

本当は更新した際のステータスもToDoコンポーネントで保持するべきだと思いますが省略。

5. 各コンポーネントの解説

各コンポーネントの関係をイメージにすると以下のようになります。
一つずつ確認しながら、見てみてください。

5-1.全体図

ReactToDoStateFlow (2).png

5-2. ToDo.js

ToDo.js
import React from "react";
import TaskAdd from './TaskAdd';
import TaskList from './TaskList';

class ToDo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      TaskList: [],
      TaskId: 0
    };
    this.deleteTask = this.deleteTask.bind(this);
    this.addTask = this.addTask.bind(this);
  }

  deleteTask(TaskId) {
    var NewTaskList = this.state.TaskList;
    let TaskIndex = 0;
    for (var i = 0; i < NewTaskList.length; i++) {
      if (NewTaskList[i].key.toString() === TaskId.toString()) {
        TaskIndex = i;
      }
    }
    NewTaskList.splice(TaskIndex, 1);
    this.setState({ TaskList: NewTaskList });
  }

  addTask(newTask) {
    let TaskList = this.state.TaskList;
    TaskList.push(newTask);
    this.setState({ TaskList: TaskList });
  }

  render() {

    return (
      <main className='todo-component'>
        <TaskAdd id={this.state.TaskId} addTask={this.addTask} TaskList={this.state.TaskList} deleteTask={this.deleteTask} />
        <TaskList TaskList={this.state.TaskList} />
      </main>
    );
  }
}

export default ToDo; 

とりあえず、使用するものをインポートします。

import
import React from "react";
import TaskAdd from './TaskAdd';
import TaskList from './TaskList';

先ほど書いた通り
stateに

  • タスクのリストとして TaskList
  • タスクの識別子(id)として TaskId

をそれぞれ定義しています。
deleteTaskはタスクの削除
addTaskはタスクの登録
を示しています。

ToDoコンポーネントのconstructor
constructor(props) {
  super(props);
  this.state = {
    TaskList: [],
    TaskId: 0
  };
  this.deleteTask = this.deleteTask.bind(this);
  this.addTask = this.addTask.bind(this);
}

タスクの削除はタスクIDを元にタスクリストから削除対象のリストのインデックスを探します。
そして、インデックスが見つかればタスクリストに対してspliceで対象のタスクの削除を実行します。
削除が完了したら、this.setStateでタスクリストを更新します。

ToDoコンポーネントのdeleteTask
deleteTask(TaskId) {
  var NewTaskList = this.state.TaskList;
  let TaskIndex = 0;
  for (var i = 0; i < NewTaskList.length; i++) {
    if (NewTaskList[i].key.toString() === TaskId.toString()) {
      TaskIndex = i;
    }
  }
  NewTaskList.splice(TaskIndex, 1);
  this.setState({ TaskList: NewTaskList });
}

新しいタスクの情報をタスクリストにpushして更新しています。
後ほど、説明しますがnewTaskTaskコンポーネントになっています。

ToDoコンポーネントのaddTask
addTask(newTask) {
  let TaskList = this.state.TaskList;
  TaskList.push(newTask);
  this.setState({ TaskList: TaskList });
}

TaskAddには、以下のpropが送られています。

  • タスクID
  • タスクリスト
  • addTask関数
  • deleteTask関数

TaskListにはタスクリストのみがpropとして送られています。

なぜTaskAddの方に削除する関数を持たせているのか疑問に思う方も居ると思いますが
後に解決すると思うので、ここでは送っているということだけ覚えておいてください。

ToDoコンポーネントのrender
render() {
  return (
    <main className='todo-component'>
      <TaskAdd id={this.state.TaskId} addTask={this.addTask} TaskList={this.state.TaskList} deleteTask={this.deleteTask} />
      <TaskList TaskList={this.state.TaskList} />
    </main>
  );
}

5-3. TaskAdd.js

TaskAdd
import React from "react";
import Task from './Task'

class TaskAdd extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            NewTask: '',
            TaskId: this.props.id,
            ErrorMessage: '',
        };
        this.handleClick = this.handleClick.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({ NewTask: event.target.value });
    }
    handleClick() {
        // 空白チェック
        if (this.state.NewTask === '') {
            this.setState({ ErrorMessage: '入力が空です。' })
            return 0
        }
        // 重複チェック
        for (var i = 0; i < this.props.TaskList.length; i++) {
            if (this.props.TaskList[i].props.name === this.state.NewTask) {
                this.setState({ ErrorMessage: 'タスク名が重複しています。' })
                return 0
            }
        }
        let TaskId = this.state.TaskId;
        this.props.addTask(<Task key={TaskId} id={TaskId} name={this.state.NewTask} deleteTask={this.props.deleteTask} />);
        this.setState({ TaskId: TaskId + 1 })
        this.setState({ NewTask: '' })
        this.setState({ ErrorMessage: '' })
    }

    render() {
        return (
            <section className='task-creator'>
                <h2>Task Add</h2>
                <input className='task-item-text' type="text" placeholder="Task" value={this.state.NewTask} onChange={this.handleChange} />
                <button className='task-add-btn' type="button" onClick={this.handleClick}>Add</button>
                <p className='error-msg'>{this.state.ErrorMessage}</p>
            </section>
        );
    }
}

export default TaskAdd; 

とりあえず、使用するものをインポートします。
タスクを新規に作成するのでTaskコンポーネントをインポートしています。

import
import React from "react";
import Task from './Task'

stateとして

  • 新規タスク名
  • タスクID(ToDoコンポーネントから受け取る)
  • エラーメッセージ

handleClickはAddボタン押下時の関数
handleChangeはテキストボックスの変更を検知してstateの新規タスク名を書き換えています

TaskAddコンポーネントのconstructor
constructor(props) {
    super(props);
    this.state = {
        NewTask: '',
        TaskId: this.props.id,
        ErrorMessage: '',
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleChange = this.handleChange.bind(this);

シンプルにsetStateで値を書き換えています。
renderの箇所でonChange で指定するとできます。
JavaScriptを書いてた人なら、平常運転的な感じですかね?

TaskAddコンポーネントのhandleChange
handleChange(event) {
    this.setState({ NewTask: event.target.value });
}

Addボタン押下時の関数です。
空白チェックや重複チェックは特に言うことは無いと思います。
propsで受け取ったTaskListをここで使用して、重複の確認をしています。
処理の途中で抜けるのにreturn 0にしてるけど、あまりよろしく無いかも。。。。

諸々のチェックが完了すると、ToDoコンポーネントからpropsとして受け取ったaddTask関数を使用してリストに新しいタスクを登録しています。
タスク自体はTaskとしてコンポーネント化しているので、addTaskの引数はTaskになります。

さてdeleteTaskをTaskAddコンポーネントに持たせている理由ですが、タスクIDと削除機能がセットである方が削除処理が書きやすいからです。
そして、削除処理を持たせることができるタイミングがタスクの新規作成時だからです。
何か別の方法も考えられそうですが。。。(状態管理のフレームワークを使わない方向で。。)

最後のsetStateは新しいタスクIDを振ったり、タスクのテキストボックスやエラーメッセージを空白にしているのみです。

TaskAddコンポーネントのhandleChange
handleClick() {
    // 空白チェック
    if (this.state.NewTask === '') {
        this.setState({ ErrorMessage: '入力が空です。' })
        return 0
    }
    // 重複チェック
    for (var i = 0; i < this.props.TaskList.length; i++) {
        if (this.props.TaskList[i].props.name === this.state.NewTask) {
            this.setState({ ErrorMessage: 'タスク名が重複しています。' })
            return 0
        }
    }
    let TaskId = this.state.TaskId;
    this.props.addTask(<Task key={TaskId} id={TaskId} name={this.state.NewTask} deleteTask={this.props.deleteTask} />);
    this.setState({ TaskId: TaskId + 1 })
    this.setState({ NewTask: '' })
    this.setState({ ErrorMessage: '' })
}

特に説明はいらないと思います。普通のhtmlタグを並べているだけです。

TaskAddコンポーネントのrender
render() {
    return (
        <section className='task-creator'>
            <h2>Task Add</h2>
            <input className='task-item-text' type="text" placeholder="Task" value={this.state.NewTask} onChange={this.handleChange} />
            <button className='task-add-btn' type="button" onClick={this.handleClick}>Add</button>
            <p className='error-msg'>{this.state.ErrorMessage}</p>
        </section>
    );

5-4. Task.js

Task.js
import React from "react";

class Task extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isDone: this.props.isDone,
        }
        this.handleChange = this.handleChange.bind(this);
    }

    checkTaskStatus(isDone) {
        if (isDone) {
            return 'isDone task-item-label'
        } else {
            return 'WorkInProgress task-item-label'
        }
    }

    handleChange() {
        if (this.state.isDone) {
            this.setState({ isDone: false })
        } else {
            this.setState({ isDone: true })
        }
    }

    render() {
        return (
            <li className='task-item-row'>
                <input id={'task-id-' + this.props.id.toString()} className='task-item-checkbox' type='checkbox' onChange={this.handleChange}></input>
                <label htmlFor={'task-id-' + this.props.id.toString()} className={this.checkTaskStatus(this.state.isDone)}>{this.props.name}</label>
                <i className="material-icons icon" onClick={() => this.props.deleteTask(this.props.id)}>delete</i>
            </li>
        );
    }
}
export default Task; 

stateにはタスクが完了したかどうかを判定するis_Doneフラグ
handleChangeはタスクのチェックボックスのイベント処理に使用しています。

Taskコンポーネントのconstructor
constructor(props) {
    super(props);
    this.state = {
        isDone: this.props.isDone,
    }
    this.handleChange = this.handleChange.bind(this);
}

タスクのラベルにcssのクラスを適応させています。
ちょっと不格好。。

TaskコンポーネントのcheckTaskStatus
checkTaskStatus(isDone) {
    if (isDone) {
        return 'isDone task-item-label'
    } else {
        return 'WorkInProgress task-item-label'
    }

stateのis_Doneを見て判定しているだけです。

TaskコンポーネントのhandleChange
handleChange() {
    if (this.state.isDone) {
        this.setState({ isDone: false })
    } else {
        this.setState({ isDone: true })
    }
}

普通のhtmlタグにOnChangeやOnClickのイベント処理を追加しているだけです。
onClick={() => this.props.deleteTask(this.props.id)}
こう書けば、handleChangeみたに定義して書かなくていいから楽ですよね。

Taskコンポーネントのrender
render() {
    return (
        <li className='task-item-row'>
            <input id={'task-id-' + this.props.id.toString()} className='task-item-checkbox' type='checkbox' onChange={this.handleChange}></input>
            <label htmlFor={'task-id-' + this.props.id.toString()} className={this.checkTaskStatus(this.state.isDone)}>{this.props.name}</label>
            <i className="material-icons icon" onClick={() => this.props.deleteTask(this.props.id)}>delete</i>
        </li>
    );
}

5-5. TaskList.js

renderでToDoコンポーネントから受け取ったTaskListを表示しているだけです。

TaskList
import React from "react";

class TaskList extends React.Component {
    render() {
        return (
            <section className='task-list'>
                <h2>Task List</h2>
                <ul>
                    {this.props.TaskList}
                </ul>
            </section>
        );
    }
}

export default TaskList; 

6. まとめ

今回は結構なボリュームになってしまいましたが、うまく説明できたでしょうか?(ちょっと心配・・・)
ほんの数日前にReactを改めて勉強しなおして、それっぽいものは作れたかなと思っていたりします。
私は公式のドキュメントやチュートリアルだけでは、なかなか手が進まなくて四苦八苦したので
今回の記事作成の過程でReactと少しは仲良くなれた気がします。
まだまだ、HookやReduxなど関門が立ちはだかっているのが見えますが、地道に取り組んで行きたいと思っています。
また、最後までお読みくださりありがとうございます。
もしかしたら、有識者の方から見たらデタラメな書き方をしているかもしれませんがご容赦ください。
質問、指摘、コメントは大歓迎ですので、よろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む