20200817のJavaScriptに関する記事は25件です。

JavaScriptを記述中に出たエラー Uncaught TypeError:cannot read property 'post' of null

表題の通り、本日カリキュラムを実施中に出たエラーメッセージ。
railsを使い、投稿文を既読にする機能をAjaxで表現しようとしていたらこのエラーが発生。原因は記述漏れだったんですが、そこに気づくまでの過程が、まだまだ「何となくここじゃない?」というレベルなので、今回のエラーが出た時の調べ方や考えた事などを記事にしてみます。(考えが浅すぎるとか言わないでね・・・)

今回実装した内容は、『投稿文をクリックすると、背景が灰色になり既読という事がわかる』といったものでした。
カリキュラムを見ながらその機能を実装していったのですが、ほぼ記述も終わり正常に動くか確認をしたところ、『投稿文をクリックしても背景色が変わらない』という状況に・・・。

とりあえず、今ある知識とgoogle先生の力を借りてエラーを調べることに。

まずコンソール開いて確認すると、『Uncaught TypeError: cannot read property 'post' for null』というエラーメッセージが表示されていました。
内容を読み解くべく英単語の意味を調べながら自分なりに解読すると、
『Uncaught TypeError => 記述が間違っている箇所がある! 』
『cannot read property 'post' for null => postっていうプロパティはnullだから読み込めないよ!』
と解釈。またコンソールのchecked.jsを開いて見ると

 XHR.onload = () => {
       const item = XHR.response.post;
//この記述でUncaught TypeErrorが発生している模様

ここから、『まず記述がどこかおかしい』 => 『イベントを発火させる為のid取得とか正常にできていない?』 => 『viewファイルとjsファイルの記述に間違いがないか(idの打ち間違いとか)確認してみよう』 => 『打ち間違えている箇所無し・・・』 => 『データが送られていないならコントローラーとか怪しくない?』 => 『怪しい箇所を見つけられず・・・』 => 『そういえばカリキュラムでルーティングをpathパラメーターで扱えるように修正していたな・・・』 => 『コロンが抜けてる!』といった具合で今回はエラーを特定できましたが、もっとしっかり原因を追求した上でエラーを発見できたらと・・・。

今回のエラー原因はコレ

 get 'posts/:id', to: 'posts#checked'
// 'posts/:id'の:が抜けていてエラーが発生していたようです。

恐らく同じようなエラーは今後も多々繰り返すと思うので、簡単に調べてみました。

まずこのエラーの内容ですが、
Uncaught TypeError:cannot read property 'プロパティ名' of null

エラー理由:nullのプロパティ名にアクセスしている
よくある原因:何かを探す関数でnullが返ってきている

ということで、これがよく起こるのが「何かを探す」といったメソッドが、何も見つからない時にnullを返す事があるからで、検索条件とかが間違っている場合に起こるようです。
こういった場合nullチェックをするのが対策として有効とのことです。

参考にさせていただいた記事
https://qiita.com/hirothings/items/fe51d0a79e83d65c02ed

https://sbfl.net/blog/2019/02/01/javascript-error-messages/

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

Javascriptを使って暗号化と復号化を学ぶ (準備編)

JSを使って暗号化と復号化

きっかけ

webシステムにおける、不正侵入の対策として暗号化と復号化の体験をしたいと考えたため。

「Javascript 暗号化」と調べると、どうやら作れそうなので作ろうと決意。

構想

メールアドレスとパスワードの入力フォームを作成
(新規登録を想定)

入力されたデータを登録し、暗号化して保存しておくプログラム。

暗号化されたデータを復号化してユーザーに返すプログラム。

今回作成するプログラム

1.HTML&CSS

アカウント作成のための入力フォームの作成
レスポンシブにも対応する。

2.javascript(ユーザー操作部分)

①エンターキーを押して次の入力欄に移動する機能
マウス操作の手間を省くために実装。

しかし、
この後に実装するクリックイベントで
エンター押したときとクリックで押したときの2種類のイベントで作る必要ができた。

②クリックイベント
2つのデータを入力し、送信ボタンでデータを送信する機能
データの取得などの主要な機能は次回の記事に。

3.javascript(正規表現)

①入力制限
メールアドレスとパスワードの書式を用意し、書式に当てはまらない場合はエラーを出す。

②アラートの表示
入力したデータの内容に応じた、詳細なフォーム検証を行う。

完成物

See the Pen qBZNNwx by ライム (@raimumk2) on CodePen.

サンプルコード

HTML
encryption.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <link rel="stylesheet" href="css/styles.css">

  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
  <div class="web-browser">
    <h1>webブラウザ</h1>
    <form action="#" method="POST" id="create-account" name="create_account">
      <div class="form-label">アカウント作成</div>
      <div class="item">
        <label for="user-id" class="label label1">メールアドレス:</label><input type="email" id="email" class="inputs" name="email">
      </div>
      <div class="item">
        <label for="password" class="label label2">パスワード:</label><input type="password" id="password" class="inputs" name="password"
        >
      </div>
      <div class="item">
        <label for="re-password" class="label label2">パスワード(確認用):</label><input type="password" id="re-password" class="inputs" name="re-password">
      </div>
      <input type="submit" id="new-create" value="新規登録">
    </form>
  </div>

  <script type="text/javascript" src="js/encryption.js"></script>
</body>
</html>

CSS
encryption.css
* {
  margin: 0;
  padding: 0;
}

body {
  color: #300;
}

.web-browser {
  background: #dbedf0;
  padding-bottom: 10px;
}

h1 {
  text-align: center;
  padding-top: 50px;
}

form {
  display: flex;
  flex-direction: column;
  background: #fff;
  width: 500px;
  margin: 40px auto;
  padding: 15px 20px;
}

.form-label {

  font-size: 22px;
  text-align: center;
  margin-bottom: 15px;
}

.item {
  overflow: hidden;
  margin-bottom: 15px;
  color: #300;
}

.label {
  float: left;
  height: 33.5px;
  padding-top: 5.5px;
  padding-left: 10px;
}

.label1 {
  margin-right: 65px;
  width: 135px;
  border-left: 3px solid #c00;
}

.label2 {
  margin-right: 20px;
  width: 180px;
  border-left: 3px solid #1760a0;
}

.inputs {
  float: left;
  width: 250px;
}

input[type="email"],
input[type="password"] {
  border: 1px solid #aaa;
  border-radius: 5px;
  padding: 10px;
  font-size: 15px;
}

input[type="submit"] {
  width: 250px;
  background: #80a491;
  border: none;
  color: #fff;
  font-size: 17px;
  font-weight: bold;
  padding: 10px 20px;
  margin: 0 auto;
}

input[type="submit"]:hover {
  opacity: 0.75;
}

@media (max-width: 600px) {
  form {
    padding: 10px 20px;;
  }

  .inputs {
    width: 225px;
  }
}

JavaScript
encryption.js
// エンターキーで次の項目に移行する
$('form').on('keydown', 'input', function(e) {
  if (e.keyCode == 13) {
      if ($(this).attr("type") == 'submit') return;

      var form = $(this).closest('form');
      var focusable = form.find('input, button[type="submit"]')
          .not('[readonly]').filter(':visible');

      if (e.shiftKey) {
          focusable.eq(focusable.index(this) - 1).focus();
      } else {
          var next = focusable.eq(focusable.index(this) + 1);
          if (next.length) {
              next.focus();
          } else {
              focusable.eq(0).focus();
          }
      }

      e.preventDefault();
  }
});

// 新規作成ボタンの処理
//入力が空だった場合
{
  const createBtn = document.getElementById('new-create');
  createBtn.addEventListener('keypress', onkeyPress);
  createBtn.addEventListener('click', onClick);

  const email = create_account.email;
  const password = create_account.password;

  function onClick() {
    if (email.value == "" && password.value == "") {
      alert('メールアドレスとパスワードを入力してください');
      return false;
    } else if (email.value == "") {
      alert('メールアドレスを入力してください');
      return false;
    } else if (password.value == "") {
      alert('パスワードを入力してください');
      return false;
    }
  }

  function onkeyPress(e) {
    if (e.keyCode == '13') {
      onClick();
    }
  }
}

//各正規表現について
{
  const createBtn = document.getElementById('new-create');

  //メールアドレス
  const form_email = document.getElementById('email');
  const email_regexp = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/

  // パスワード
  const form_password = document.getElementById('password');
  const password_regexp = /^(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,100}$/

  const form_re_password = document.getElementById('re-password');

  createBtn.addEventListener('click', regExp);
  createBtn.addEventListener('keypress', regExpKey);

  function regExp(e){
    if (form_email.value != "" && form_password.value != "") {
      if (form_email.value.match(email_regexp) === null) {
        alert('有効なメールアドレスを入力してください');
        return false;
      } else if (form_password.value.match(password_regexp) === null) {
        alert('有効なパスワードを入力してください');
        return false;
      } else if (form_password.value != form_re_password.value) {
        alert('パスワードの入力値が一致しません');
        return false;
      } else {
        alert('アカウントを作成しました');
        return e.preventDefault();
      }
    }
  };

  function regExpKey(e) {
    if (e.keyCode == '13') {
      regExp();
    }
  }
}

制作過程

1.HTML&CSS部分

メールアドレスとパスワードを入力してアカウントを登録することを想定したフォームを作成

スクリーンショット 2020-08-16 21.57.48.png

2.javascript(ユーザーの操作部分)

①エンターキーを押して次の入力欄に移動する機能

マウス操作の手間省くことができた。
また、KeyCodeの存在を知ることができた。

コードについては、下記のURLの内容から、必要なコードを抜粋。
WebフォームでEnterキーを押したときにフォーカスを移動させたい

②クリックイベント
2つのデータを入力し、送信ボタンでデータを送信する機能
前述したとおり、こちらは次回に。

3.javascript(正規表現)

①入力制限
メールアドレスの場合は、@を使った書式になっているかどうか
パスワードの場合は半角数字・半角英字のみ使える、文字数制限の設定

パスワードと確認用のパスワードに入力された値が同じ場合のみ
アカウント作成が行える。
という構想のもと以下のようにコードを書いた。

encryption.js
  //formの各要素を取得&正規表現
  //メールアドレス
  const form_email = document.getElementById('email');
  const email_regexp = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/

  // パスワード
  const form_password = document.getElementById('password');
  const password_regexp = /^(?=.*?[a-z])(?=.*?\d)[a-z\d]{8,100}$/

  const form_re_password = document.getElementById('re-password');

②アラート
新規作成ボタン押し、データの入力が正常の場合、成功アラート表示

失敗した場合は、エラーメッセージを出し、どこが間違っているかを指摘
1.フォームに空欄になっている箇所があるかどうか
2.メールアドレスは〇〇@〇〇.〇〇の書式に当てはまっているかどうか
3.パスワードは数字英字どちらも1文字以上使われていている、かつ、合計8文字以上になっているかどうか
4.確認用パスワードは1回目のパスワードと同様になっているかどうか

これらを人まとまりにすると、コードが複雑になったので
空欄の場合と入力されている状態とで、関数を別にした。

いずれも新規登録ボタンを押した際に呼び出している。

encryption.js
  //空欄の場合
  const createBtn = document.getElementById('new-create');
  createBtn.addEventListener('keypress', onkeyPress);
  createBtn.addEventListener('click', onClick);

  const email = create_account.email;
  const password = create_account.password;

  function onClick() {
    if (email.value == "" && password.value == "") {
      alert('メールアドレスとパスワードを入力してください');
      return false;
    } else if (email.value == "") {
      alert('メールアドレスを入力してください');
      return false;
    } else if (password.value == "") {
      alert('パスワードを入力してください');
      return false;
    }
  }

  function onkeyPress(e) {
    if (e.keyCode == '13') {
      onClick();
    }
  }
encryption.js
  createBtn.addEventListener('click', regExp);
  createBtn.addEventListener('keypress', regExpKey);

  //何らかの値が入力されている場合  
  function regExp(e){
    if (form_email.value != "" && form_password.value != "") {
      if (form_email.value.match(email_regexp) === null) {
        alert('有効なメールアドレスを入力してください');
        return false;
      } else if (form_password.value.match(password_regexp) === null) {
        alert('有効なパスワードを入力してください');
        return false;
      } else if (form_password.value != form_re_password.value) {
        alert('パスワードの入力値が一致しません');
        return false;
      } else {
        alert('アカウントを作成しました');
        return e.preventDefault();
      }
    }
  };

  function regExpKey(e) {
    if (e.keyCode == '13') {
      regExp();
    }
  }

参考サイト

JavaScriptで暗号化と復号化を行う方法を現役エンジニアが解説【初心者向け】

フォーム関連

【初心者向け】0からformがわかる|HTMLでのフォーム作成

WebフォームでEnterキーを押したときにフォーカスを移動させたい

JavaScriptで実現!入力フォームの値を取得してチェックする方法

正規表現

MDN:クライアント側のフォームデータ検証

JavaScriptの正規表現(RegExp)の書き方・使い方について解説

JavaScriptで空判定する方法

JavaScriptで同値チェックを実装してHTML5と同じデザインのエラーを出す方法

データ取得

テキストボックスに入力された値を取得する - JavaScript プログラミング

その他

qiita:input type=submit でページを更新"しない"方法。

MDN:Event.preventDefault()

次回

入力されたデータを取得し、保存しておく機能
(JSONやAjaxについて学習しておく)

暗号化:アカウント作成→webサーバーにデータを登録→APサーバーへ送信時にデータの暗号化を行う
サーバーを建てられる知識はまだないので、
webブラウザ上に、webサーバーとAPサーバーを見立て
webブラウザだけでデータのやり取りを行う。

復号化は次々回。

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

初心者のためのVue.jsの環境構築方法

初心者のためのVue.jsの環境構築方法

初心者向けのVue.jsの環境構築方法をまとめました。
Vue.jsをローカルで少し動かしてみたい方は以下の記事を参考にしてみてください。

今話題のJavaScriptフレームワーク「Vue.js」の簡単な始め方

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

Safariは危険なJavaScriptに対応しない

先日Appleが、Safariは幾つかのWebAPIに対応しないと公言しました
日本語記事も出ています。

しかし、どのサイトも対応しないAPIの一覧を並べてはいるのですが、それぞれのAPIが具体的にどのようなものなのかを記載した記事が見当たらなかったので、以下はそれらについて調べてみたものです。

対応しない理由

a threat to user privacy、すなわち、あくまでブラウザフィンガープリントなどの手段によって個人を特定・追跡できてしまうからという理由です。
セキュリティ的にも問題なAPIが並んでいるのですが、そちらは理由ではありません。

対応しないWeb API

Web Bluetooth

caniuse
RFC
非公式日本語訳

01.png

解説

ブラウザからBluetoothを通して接続先のデバイスにアクセスすることができます。
データ転送形式はGATTで、データを取得するだけではなく送り込むこともできます。

接続先の機器にもよりますが、接続可能なデバイスの例はデジカメだったり心拍計だったり血圧計だったりと、そりゃもう個人情報の塊ですから、物によってはひとつでも許可した時点で完全に個人を特定できるようになるでしょう。

Qiita内関連記事

Web Bluetooth API で BLE(Chrome と micro:bit をつなぐ)
micro:bitとWebBluetoothで通信してみました

Web MIDI API

caniuse
RFC

02.png

解説

ブラウザからMIDI機器にアクセスすることができます。

そもそもブラウザをMIDI機器に繋ぐなんてこと、普通の人はしません。
RFCにもFew systems will have significant numbers of MIDI devices attached『個体識別できなくなるほどたくさん接続されるMIDI機器はない』とか書かれるくらいですし。
機器のメーカーや機種名、バージョン、そしてその他のフィンガープリントを組み合わせれば、ほぼ完全に特定できるでしょう。
使ってる人が多いから識別できないGamepad APIとは対照的ですね。

また、音を鳴らしてそれを別のブラウザで聞き取ることができれば、PCとモバイルを紐付けることもできそうです。

Qiita内関連記事

WebAudio/WebMIDI API Advent Calendar 2017 Advent Calendar 2017
WebAudio Web MIDI API Advent Calendar 2016
Web MIDI APIでシンセサイザーの入力を受け取ってみた
Web MIDI API による Web アプリと DAW の連携

Magnetometer API

caniuse
RFC
非公式日本語訳

03.png

解説

磁力センサです。

そもそもこんなものをブラウザで扱う必要があるのかというのもありますが、RFCでも妙に具体的に問題となりそうな例を出しています
・建物などの磁場変動から、スマホの所有者の位置と移動を理論上特定することができる。
Geolocation APIの位置情報とMagnetometer APIのデータの差異から、ユーザがVPNを使っているかどうかがわかる。
・場合によってはキーストロークも取得可能。

そんなに懸念があるんだったら最初から作るなよ。

Qiita内関連記事

見当たらず。

Web NFC

caniuse
RFC

04.png

解説

ブラウザからNFCにアクセスできます。

今どきのスマホであれば、本体は最初から対応しています。
PCであればNFCリーダー/ライターが必要となります。

実装例は、【Web NFC】JavaScriptでNFCタグのデータを読み書きしてみた などで見ることができます。
思いきりNFCにデータを書き込んでいますね。
これにより個人の特定、複数ブラウザの紐付けが可能になります。

Qiita内関連記事

見当たらず。

Navigator API: deviceMemory

caniuse
RFC

05.png

解説

メモリ量を取得します

取得できる値は厳密値ではなく、0.5・1・2・4・8のような概算値です。
そのため、よっぽど変な仕様のレア端末でもないかぎり、特定の懸念はさほど大きくないのではないかと思われます。
もちろん他情報と組み合わせることで絞り込み精度が上がるのは間違いありませんが、それはこれ以外のどのような情報も同じことです。
このAPIがあえて単独で挙げられている理由はよくわかりませんでした。

まあ、そもそもなんでブラウザごときに搭載メモリ量渡さにゃならんのだっていう話ではありますが。

Qiita内関連記事

Webブラウザの世界から始めるIoT入門 #gigttt

NetworkInformation API

caniuse
RFC

06.png

解説

ネットワーク接続状況を取得します。

NetworkInformation.typeで"wifi"や"bluetooth"などの接続種別、NetworkInformation.effectiveTypeで最近繋いだネットワークと接続速度を取得したりできます。
またonchangeイベントで、接続先ネットワークの変更を感知することもできます。
遅い環境では画質を荒くしたりと、速度によって送り込むコンテンツを変更するようなことを考えているみたいですが、ゴミが送り付けられる未来しか見えない。

接続種別と応答速度からフィンガープリントが可能になります。
またonchangeイベントの発生頻度や発生時刻から、いつまで職場にいて何時に帰宅したというように行動を推測することも可能です。

ちなみにこの問題への対策は、"they should disable JavaScript, monitor that all outbound requests are made to trusted origins, and make diligent use of anonymizing VPN/proxy services."『JavaScriptを無効にする、全てのアウトバウンド要求が信頼された発信元であるか監視する、匿名VPNを使用する』だそうです。
正気なん?

Qiita内関連記事

見当たらず。

Battery Status API

caniuse
RFC
非公式日本語訳

07.png

解説

バッテリの残量、および充電状態などを取得するAPIです。

2016年にはフィンガープリントとして利用可能という論文が発表されています。
またかなり限定的ではありますが、バッテリの消費状況から位置を割り出すことにも成功しています

そのためか本APIは既にWeb標準から削除されており、かつては対応していたFirefoxもあえて非対応に戻しています
従って、今さらあえて名指しで出すようなAPIでもない気がしますね。

Qiita内関連記事

Fx0でBattery Status API

Web Bluetooth Scanning

caniuse……なし?
RFC
ChromeStatus

解説

デバイスの周囲にあるBluetoothデバイスをスキャンできるようになるという……この時点で有り得ない。
そりゃもう世界中にビーコン置いて回りますよ。

Chrome84時点ではフラグenable-experimental-web-platform-featuresを有効にしないと使用することができませんが、有効にすべきではなく一生殺しておくべき機能です。

Qiita内関連記事

見当たらず。

AmbientLightSensor API

caniuse
RFC
非公式日本語訳

08.png

解説

環境光センサの値を読み取ることができます。
Generic Sensor APIというセンサ関連の汎用APIの機能の一部という位置付けのようです。

規則的な生活をしている人ほど危険です。
毎日同じ時間に同じ光度であれば同一人物である可能性が高いでしょう。
二つのデバイスが似たような光度変化をすれば、同一人物あるいは家族程度に近い人物であると判断できるでしょう。

懸念点には『踏んだことのあるリンクは黒、未訪問のリンクは白で表示することで光度を変え、訪問済みか否かを識別する』みたいなことまで書かれていました。

visitedの色とか昔は普通に取れていたんですよね。
牧歌的な時代だった。

Qiita内関連記事

見当たらず。

EME Extension: HDCP Policy Check

caniuse……なし?
RFC……なし?
ChromeStatus
GitHub

解説

Encrypted Media Extensionsの追加機能のひとつで、メディアファイルのHDCPステータスをブラウザから見ることができるようになるみたいです。
HDCPはよく見るところだとHDMI接続とかで使われていて、PS3やAndroidをPCに繋ぐと画面映らん!!ってなる面倒で邪魔なやつです。

解説によると、この機能のプライバシー上の懸念は、EMEと同じ仕組みを使っているのでEMEと同じだということでした。
では、Appleがあえてこの機能だけをあえて非対応にする理由は何なのでしょうか。
正直EME自体が何言ってるか全然わからんので、何がどう危険なのかよくわかりませんでした。
識者あとよろ。

Qiita内関連記事

見当たらず。

Proximity API

caniuse
RFC

09.png

解説

近接センサです。
通話中にスマホから耳を離すと画面がついたり、逆に耳元に持っていくと画面が消えたりしますが、あれです。
距離の変化をブラウザに通知します。

実際は近付いた/離れたの変化どころか、具体的にセンチメートル単位での距離まで取得できるようです。
こんな情報をブラウザに渡すなんて有り得ないですね。

しかしこれをフィンガープリントとして使うのは難しそうな気がもしますが、懸念点にはユーザ識別・フィンガープリントの危険があると書かれています。
どのようなシナリオが想定されているのかは、よくわかりませんでした。

Qiita内関連記事

見当たらず。

WebHID API

caniuse……なし?
RFC

解説

Human Interface Device、すなわち人間が操作する各種デバイスへのアクセス手段を提供するAPIです。

ゲームパッドやMIDI機器などは、それぞれ専用のAPIによってJavaScriptからアクセスが可能です。
しかし、そのような対応は今のところ機器ごとに個々に行わなければならず、今後も追加したい機器が出るたびにAPI策定・実装ってしていたら大変です。
そこで、このAPIでは特定機器に囚われない、HIDデバイスへの汎用的なアクセス手段を提供します。

懸念点が3000文字以上ある時点でねーわってかんじですが、たとえばカメラやマイクなどが付いているデバイス、医療機器などに汎用アクセスされたらたまったものではありません。

またWebで公開されることを想定していない、任意にプログラムを登録できるデバイスが繋がっている可能性もあります。
たとえば今のところブラウザはキーボードの入力をキャッチすることはできますが、逆にキーボードに能動的に何かを働きかけることはできません。
このAPIではそれが可能になり、キーボードの種類によってはキーロガーを仕込むこともできるようです。
その対策は、『ユーザが同意しなければマクロを動かさないようにデバイスのメーカーが対応する』

不山戯けてんのかこれ?

Qiita内関連記事

見当たらず。

Serial API

caniuse……なし?
RFC

解説

ブラウザからシリアルポートに……はい終わり。

Qiita内関連記事

見当たらず。

WebUSB

caniuse
RFC

10.png

解説

ブラウザからUSBポートに……はい終わり。

Qiita内関連記事

WebUSBを使ってブラウザのJavaScriptからArduinoを制御してみよう!
micro:bitでWebUSBを使う

Idle Detection

caniuse……なし?
RFC……なし?
GitHub
ChromeStatus

解説

アイドル状態を通知します。

スクリーンセーバーが起動した、画面がロックされた、などのイベントをブラウザが検知できるようになります。
これにより、複数のWebサイトで同時に画面ロックイベントが発生すると、それは同じブラウザ、あるいは一つの端末である可能性が高い、すなわち同一人物であると判断できます。

この緩和策としてイベント発生をランダムに遅延させるという方法が考えられましたが、30秒遅らせたところで長期収集することで十分に特定可能だということがわかったので却下されました。

その代替策として策定されたのが『パーミッションを要求する』はい役に立たないやつ来た。

Qiita内関連記事

見当たらず。

Geolocation Sensor

caniuse……なし?
RFC
非公式日本語訳

解説

位置情報センサです。

既存のGeolocation APIの拡張という立場です。

何が拡張されたかというと、Geolocation APIでは不可能だったバックグラウンド動作が可能になります。
すなわち、いったん他の手段で端末を特定さえしてしまえば、自宅と職場と移動ルートを完全に把握されるということです。

このAPI単体での個人特定は余程の田舎でもないかぎり難しいかもしれませんが、他のフィンガープリントと組み合わせることで、とてつもなく危険なAPIになります。

Qiita内関連記事

見当たらず。

感想

なんでもかんでもブラウザで実行できてしまうとネイティブアプリに誘導できなくなって稼ぎが減ってしまう、という打算はもちろん有るでしょう。
しかし、それを別としても、ほとんどは対応しないのが当然すぎるAPIばかりですね。
特にNetworkInformation APIやらWeb Bluetooth Scanningなど、発想するまでは自由ですが、実装する時点で正気を疑うレベルです。
そんなわけでSafariがこれらのAPIから距離を取るのは、まあ妥当ですね。

なお、これらのAPIのほとんどは、Googleが先行して策定・開発し、W3CにWeb標準として提案するという形になっています。
すなわち、Chromeにはこれらの多くが既に実装されているということです。
おおこわいこわい。

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

開発体験を変える! Chrome DevTools Tips 7選

最近Chrome DevToolsについて調べていて発見した便利機能を紹介します。
誰もが使える最高便利な開発マシンChrome DevToolsを使いこなして開発体験を変えましょう!

1. $0で選択中のDOM要素の取得

特定の要素に何かしたいという時には、要素のIDやclassを確認してConsoleでdocument.querySelector("#xxx")で取得するというのが一般的だと思います。実はそれはカーソル選択と$0で代替できます。
Classや、IDがついていない特定のDOMを取得したい時とかにも使えるので地味に便利です。

手順

  1. カーソルで取得したい要素を選ぶ
  2. Consoleタブで$0を入力

$0はChromeが提供しているConsole Utilities APIです。Console Utilities API Reference で他にも色々便利機能が紹介されているので一読をお勧めします。

他のAPI一例

$(selector)
セレクタに一致するDOM要素を返す。document.querySelectorと同意。
(一見jQueryですが、Console Utilities APIです)

$('#hoge') 

$$(selector)
セレクタに一致するDOM要素の配列を返す。document.querySelectorAllと同意

$$('div')

$x(path)
xpathに一致するDOM要素の配列を返す。

$x("//p")

2. DOM変化からのDebugger起動(Break on)

DevToolsでのデバッグというとソースコード上にdebuggerを仕込んでというのがありますが、ソースコードを何もいじらずDevTools単体でも実行できます。

1つはElementsタブからのBreak onの設定です。
何か表示が変わるDOM要素を選択して右クリックでBreak onを選択、表示されるサブメニューでいずれかの項目にチェックを入れます。
その状態で画面上にて表示の変化のトリガーとなる動作を行うと、DOMの変化の実行前に処理が止まり、該当変化のイベント処理からステップ実行が可能になります。
(Chrome DevToolsを調べようと思ったきっかけの機能。会社のチームリーダーに教えてもらいました)

Break Onの起動オプションは以下の通りです。

option名 内容
Subtree modifications 現在選択されているノードの子が削除または追加されたとき、または子の内容が変更されたときにトリガーされます。子ノード属性の変更、または現在選択されているノードへの変更ではトリガーされません
Attribute modifications 現在選択されているノードで属性が追加または削除されたとき、または属性値が変更されたときにトリガーされます
Node removal 現在選択されているノードが削除された時にトリガーされます

手順

  1. Elementsパネルをひらく
  2. 下位要素が変化する、または自身の属性が変わる or 破棄される要素を選ぶ
  3. 右クリックでBreak onを選択、サブメニューのいずれかにチェックを入れる
  4. 画面上で変化のトリガーとなる操作をする

3. Exceptionの発生箇所で自動停止

エラー発生時に自動的にブレークポイントを設定することも可能です。
「console上にエラー出てるけど、何処で発生しているんだろう?」という時にわざわざ該当箇所を探しブレークポイントを設定する必要はありません。一瞬でデバッグできます。

手順

  1. Sourcesタブを開く
  2. 右側のデバッグコンソールのストップアイコンを押す
  3. Pause on caught exceptions にチェックを入れる
  4. エラーの発生する動作を行う

4. snippetの登録

良く使うスクリプトをsnippetとして登録する機能です。
登録したスクリプトはコントロールパネルから実行できます。私はちょっとしたデバッグ用のスクリプトなどを登録しています。

手順

  1. cmd + shift + p でコントロールパネルを開きsnippetで検索
  2. Sources Create new snippetを選択
  3. snippetウィンドウで自由にスクリプトを作成(自動保存されます)
  4. 実行ボタン、またはcmd + pでコントロールパネルを開き!を入力してsnippet一覧を表示し実行

5. ScreenShot(フルページ or ノード)

スクロール分を含んだフルページのスクリーンショットを撮りたい時ってありますよね。そんな時はおそらくChrome拡張を使っている人が多いと思います。
でも実はDevTools単体でもできます。他にも、Screenshot Capture node screenshotを選べばElementsの選択画面で選択しているノードの範囲でのスクリーンショットもお手軽に撮れます。

手順

  1. cmd + shift + P でコントロールパネルを開きscreenで検索
  2. Screenshot Capture full screenshotを選択
  3. フルページのスクリーンショットがダウンロードされる

6. APIリクエストの実行形式でのコピー

APIのテストや別クライアントでの操作の際に、APIリクエストをcURLやその他の形式で使いたいって時あると思います。そんな時もDevToolsが使えます。
ネットワークタブからリクエストを選び、右クリックでCopyを選択するだけで様々な実行形式のコードベースが取得可能です。

手順

  1. Networkタブを開く
  2. コピーしたいリクエストを選択して右クリック
  3. Copyのサブメニューから指定の形式を選ぶ

7. 使用していないCSS、JSの調査

DevTools単体で静的解析的に、ロードしているものの未使用のCSS、JSを調べることも可能です。
※ あくまでページ単体での調査なので、全ページで使う共通スクリプトをbunldeしていると値が悪く出たりします。そこは注意が必要です。

  1. cmd + shift+ P でコントロールパネルを開く
  2. Show Coverageを選択
  3. 表示されるタブで、リロードボタンを実行

apx. 画面上で自由にテキスト編集

これはChrome DevToolsの機能という訳ではないのですが、分かりやすく凄いので紹介します。
DevToolsのConsoleタブ上で以下のコードを実行すると、画面上の要素を自由に改変できるようになります。
文字列を編集したり、画像を削除したり色々できます。

document.designMode = "on"

ちょっと表現を変えてデザインを見てみたりとかする時に便利です。

ちなみに$0と組み合わせて、選択中の要素を編集可能にしたい場合は以下でも出来ます。

$0.contentEditable = 'true'

終わりに

以上「開発体験を変えるChrome DevTools Tips 7選」でした。
ふと調べ始めたら色々ありました。知ってると知らないとでデバッグ効率や開発体験がかなり変わると思います。
まだまだ知らない便利な機能があると思うので、よく使っている機能などあればコメントやRTで教えてもらえると嬉しいですー!

参考

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

【JavaScript関数ドリル】 初級編 uniq関数の実装

関数の課題内容

「課題内容」「解説動画」「解答例」を確認したい場合は、以下リンク先のページを参照。

https://js-drills.com/blog/uniq/

関数 に取り組む前の状態

  • 一通り動画を見ても理解できませんでした。

関数に取り組んだ後の状態

  • includesのバリューの中に入っていないものがあれば、pushして終わることが分かりました。

関数の実装コード(答えを見る前)

  • 流れを把握していましたが、自力で完走できませんでした。

関数の実装コード(答えを見た後)

uniq関数の使い方を理解できました。

function uniq(array) {
  const uniqArray = [];
  for(let i = 0; i < array.length; i++) {
    const value = array[i];
    if( !uniqArray.includes(value) ) {
      uniqArray.push( value );
    }
  }

  return uniqArray;
}

const numbers = [2, 1, 2];

console.log( uniq(numbers) );
// => [2, 1]

console.log( numbers );
// => [2, 1, 2]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Operator '>' cannot be applied to types 'boolean' and 'number'.って言われた時

 <div *ngIf="!hoge?.length > 0">
ERROR in hoge.html(123,456): Operator '>' cannot be applied to types 'boolean' and 'number'.

Oh...

解決

 <div *ngIf="!(hoge?.length > 0)">

参考

https://stackoverflow.com/questions/45974764/operator-cannot-be-applied-to-types-boolean-and-number

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

E2Eテストの始め方 TestCafe③ -スクリーンショット編-

E2Eテストの始め方 TestCafe① -導入編-
E2Eテストの始め方 TestCafe② -セレクタについて-

今回はTestcafeでスクリーンショットを撮る方法について書いていきたいと思います!

基本設定

test.js
fixture('topページ')
  .page('http://example.com')
test('topスクショ', async t => {
  await t
    .takeScreenshot({
      path:'sample/top.png'
    })
})
package.json
"screenshot": "testcafe chrome test.js --page-load-timeout 5000"
$ npm run screenshot
オプション 説明 デフォルト値
path スクリーンショットファイルの相対パスと名前 ./screenshots

上記サンプルの場合、./screenshots/sample/top.pngで保存されます。
基本はこれでスクショが撮れます。

ページ全体のスクショを撮る

上記までの設定だと、画面で表示されている部分しか撮ってくれず、上から下までフルサイズを撮りたい場合にはオプションを追加してあげる必要があります。

オプション 説明 デフォルト値
fullPage オーバーフローのために表示されないコンテンツを含む、ページ全体をキャプチャする。 false
.takeScreenshot({
  path:'sample/top.png',
  fullPage: true
})

使えるプロパティ

その他用意されているプロパティは、

オプション 説明 デフォルト値
includeMargins ターゲット要素のmarginを含めるかどうか。
trueにするとscrollTargetX、上と左(または下と右)のmarginが交差するコーナーから、scrollTargetYおよびcrop長方形の座標が計算される
false
includePaddings ターゲット要素のpaddingを含めるかどうか。
trueにするとscrollTargetXscrollTargetYとのcrop。四角形の座標は、要素のコンテンツ領域の上端と左端(または下端と右端)が交差する角から計算される
true
scrollTargetX、scrollTargetY ターゲット要素が大きすぎてブラウザウィンドウに収まらない場合は、ページがスクロールされ、このポイントがビューポートの中心に配置される。このポイントの座標は、ターゲット要素を基準にして計算される。数値が正の場合、ポイントは要素の左上隅を基準に配置される。
数値が負の場合、ポイントは右下隅を基準に配置。ターゲット要素がブラウザウィンドウに収まる場合、これらのプロパティは無視される。
要素の中心
crop スクリーンショットのターゲット要素をトリミングする 要素全体、またはビューポートに収まらない場合はその表示部分を切り抜く

cropプロパティに割り当てられたオブジェクトには、topleftbottomrightがあり、数値で指定します。

test.js
test('topスクショ', async t => {
  await t
    .takeScreenshot({
      path:'sample/top.png',
      includeMargins: true,
      crop: {
        top: -100,
        left: 10,
        bottom: 30,
        right: 200
      }
    })
})

上記の「その他用意されているプロパティ」として紹介したものは、ボタンとか1セクションなどページの一部分を撮るような時に有効なプロパティかなと思います。

アニメーションがあるページのスクショ

スクロールアニメーションが使われているページだとスクロールさせて要素を表示させてからスクショを撮らないと上手く写ってくれません。
そういう場合には別途処理を書いてあげる必要があります。

test.js
import { ClientFunction } from 'testcafe';

// 下までスクロール
const scrollBottom = ClientFunction(function() {
  const elementHtml = document.documentElement
  const bottom = elementHtml.scrollHeight - elementHtml.clientHeight
  window.scroll(0, bottom)
})

// 一応、上までスクロールしたい場合も
const scrollTop = ClientFunction(function() {
window.scrollTo(0, 0)
})


fixture('topページ')
  .page('http://example.com')
test('topスクショ', async t => {
  await scrollBottom();
  await t
    .takeScreenshot({
      path:'sample/top.png',
    })
})

こんな感じでスクショ撮る前に処理を書いてあげれば良いかと。

その他に、アニメーションが終わってから処理をしたいなど「指定された間、一時停止(待ち)たい」こともあるかと思います。
その際には、t.wait()メソッドが有効です。

メソッド タイプ 説明
t.wait( timeout ) Number ミリ秒単位の一時停止期間
test.js
test('topスクショ', async t => {
  await scrollBottom();
  await t
    .wait(5000)
    .takeScreenshot({
      path:'sample/top.png',
    })
})

スマホサイズのスクショを撮る

PCとスマホ両方のスクショは撮りたいですよね!
そういう時に便利なメソッドも用意されています。

t.resizeWindowToFitDevice( deviceName [, options] )

パラメータ タイプ 説明
deviceName String デバイスの名前
options Object デバイスに関する追加情報

対応デバイスはiphone,ipad,galaxy,xperiaなどなど、かなりたくさんのデバイスがサポートされているので、
こちらのデバイスリストからご確認ください。

optionsは次のプロパティを含めることができます。

プロパティ タイプ 説明
portraitOrientation Boolean true : 縦向きの画面
false : 横向きの画面
test.js
test('topスクショ', async t => {
  await t
    .resizeWindowToFitDevice('iphonexs', {
      portraitOrientation: true
    })
    .takeScreenshot({
      path:'sample/top.png',
    })
})

ブラウザウィンドウサイズを指定 or 最大化する

ウィンドウサイズを指定する

「任意のウィンドウサイズを指定したいな〜」って時にもメソッドがあります。

t.resizeWindow( width, height )

パラメータ タイプ 説明
width Number ピクセル単位の新しい幅
height Number ピクセル単位の新しい高さ
test.js
test('topスクショ', async t => {
  await t
    .resizeWindow(300, 500)
    .takeScreenshot({
      path:'sample/top.png',
    })
})

ウィンドウサイズを最大化する

こちらも使用しているモニターなどに合わせた、最大化されたウィンドウサイズの状態にしてくれるメソッドがあります。

t.maximizeWindow( )

test.js
test('topキャプチャ', async t => {
  await t
    .maximizeWindow()
    .takeScreenshot({
      path:'sample/top.png',
    })
})

以上、Testcafeでスクリーンショットを撮る方法について書いてみました。
私も絶賛勉強中ですので、もっと良い方法がありましたら教えていただけると嬉しいです!

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

ホームページ作成入門メモ1

前知識

HTML

ハイパー・テキスト・マークアップ・ランゲージの略
タグと呼ばれる<>で囲まれた文字を組み合わせたもので、IE や Chrome などのブラウザに文字や段組を表示させることができる
他にも意味を決めたり、別ページへのリンクを貼ることもできる
昔は色や文字サイズも細かくいじれたが、今は CSS でいじる決まりになっている

CSS

カスケーディング・スタイル・シートの略
HTML が文字を表示するなら、CSS は表示された HTML を飾る
色をつけたり文字サイズを変えたりアンダーバーを引いたり、見た目を飾れる(見た目のシート=スタイルシート)

JavaScript

そのままジャバスクリプトと読む
機能をつける為のもの。Javaという言語の人気にあやかってJavaScriptと名付けたとか。最近は HTML や CSS の領域にまで手を出しつつあるイケイケのプログラミング言語

エディタ

エディット(編集)する為のソフト。ここではHTML/CSS JavaScript などのプログラミング言語を編集する為のソフト。おすすめは VSCode という、Windows を開発した MicroSoft が開発したエディタ
デビューした頃からの人気者で、無料なのに加え、その使いやすさと汎用性が好評を博している
早速インストールしてみよう。

HTML 実践

一番最初のファイルを作る

  1. デスクトップか書類フォルダに新規フォルダを作る
  2. VSCode の ファイル -> Open... から 1 で作ったフォルダを開く
  3. VSCode 左のエリアを右クリックして new file から 新しいファイルを作り、index.html と名付ける スクリーンショット 2020-08-17 15.29.49.png
  4. 以下のソースコードをコピーして貼り付けて保存(保存は(mac)cmdキー + sキー か (win)ctrlキー + sキー)
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ホームページ入門</title>
</head>
<body>
    hello world
</body>
</html>

作ったファイルを、ブラウザのURLバーにドラッグアンドドロップして開く。ファイル名(ここではindex.html)をクリックすると引っ張れる
5. 画面にhello world と表示されていたら成功
6. どうやってもうまくいかない場合は、jsfiddleを開いて、html のところに上記の html を貼り付けて、左上の RUN ボタンを押すと 右下の result に結果が表示されるので試してみよう

スクリーンショット 2020-08-17 15.47.53.png

いじってみる

<body></body>に囲まれた内容を編集すると、画面に反映される
好きにいじって保存してみると、文字が変わる
例えば hello world と書かれた箇所を hogehoge world に書き換えて保存し、ブラウザを更新すると画面には hogehoge world と表示されている

色々なタグを使ってみる

タグには色々な種類がある


<h1>

ヘッダー1、1番大きな見出しを意味する

<h2>

ヘッダー2、2番大きな見出しを意味する。同様に以下6番目まで大きな見出しが存在する

<p>

パラグラフ、段落を意味する。文章の一段落文を表現するのに使う

<br />

ブレイク、改行するのに使います

<a>

アンカー、外部ページへのリンクを設定するのに使う。

<img />

イメージ、画像を設定するのに使う

<ol>

オーダードリスト、順番に並んだ箇条書きのリストを表現するのに使う。なお、順不同の場合は<ul>アンオーダードリスト を使う

table

テーブル、表組みを作るのに使う。昔はレイアウトを組むのに CSS ではなくこのタグを使っていた

これらのタグを、<body></body>の間に組み合わせて書き込むと、意味を持った文章が出来上がる。

上記のタグを組み合わせてみた例

<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ホームページ入門</title>
</head>
<body>
  <h1>ホームページ入門</h1>
  <h2>HTML</h2>
  <p>pタグはパラグラフを意味します</p>
  <a href="https://google.com">aタグはリンクを意味します</a>
  <br />
  <img src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png" />
  <h2>CSS</h2>
  <p>カスケーディングスタイルシート</p>
  <p>見た目の装飾に使います</p>
  <h2>JavaScript</h2>
  <p>以下のような用途に使います</p>
  <ul>
    <li>通信</li>
    <li>動きをつける</li>
    <li>様々な機能の実装</li>
  </ul>
  <p>JavaScriptを書く上で気をつけるのは以下の通りです</p>
  <ol>
    <li>エラーを残さない。大前提です</li>
    <li>処理を重くしない</li>
    <li>簡潔に実装する</li>
  </ol>
  <br />
  <h2>テーブルタグについて</h2>
  <p>テーブルタグは以下のように使います</p>
  <table>
    <thead>
      <tr>
        <th>色コード表</th>
      </tr>
      <tr>
        <th>白</th>
        <th>黒</th>
       </tr>
    </thead>
    <tbody>
      <tr>
        <td>#fff</td>
        <td>#000</td>
      </tr>
    </tbody>
  </table>
  <br>
</body>
</html>

これを index.html にコピペ、保存しブラウザを更新すると、ブラウザによってスタイリングされたタグの内容が出力されている

テーブルタグについて

<table>タグの中が複雑になっているように見えるが、そこまで複雑ではない。


<thead>

テーブルヘッド。テーブルの見出しグループを意味する

<tbody>

テーブルボディ。テーブルの本体を意味する

<tr>

テーブルロウ。テーブルの横一列の行を意味する。

<th>

テーブルヘッダー。行、列単位の見出しを意味する

<td>

テーブルデータ。文字通りデータを意味する

CSS 実践

CSSを使用する方法は3パターンある
1. HTML に直接<style>タグを使って記入する
2. CSS ファイルを分けて作り、HTML ファイルから読み込む
3. CSS in JS を使って、js に CSS を書き込み、その js を HTML に読み込ませる

最近流行りの react や vue では 3 の手法が取られるが、それ以外では基本的に 2 を使う。1 はほぼほぼ使われない。

CSS の読み込ませ方

  1. .css拡張子のファイルを作る。例えばcommon.cssとする
  2. htmlから、linkタグを使って読み込ませる。
    <link rel="stylesheet" href="common.css">

上記のlinkタグを<head>タグで囲まれたエリアの中に記載する

CSS の書き方

セレクタにプロパティを当てることで CSS は適用される。

セレクタ: {
  プロパティ名: 値;
  プロパティ名: 値;
}

セレクタとは

どの要素なのかを示す識別子。タグの名前、class の名前、 id の名前の 3 つの基本的なセレクタがあり、さらにそのセレクタの親子セレクタ、兄弟セレクタなど、特別なセレクタが存在する。

例えば

<p class="hoge" id="huga">ぬわあああん</p>

というHTMLタグがあったとして、このタグを指定できるセレクタは、
- p というタグ名
- hoge というclass
- huga というid

の 3 つになる
それぞれのセレクタの指定方法は


タグ名

// そのまま指定する
タグ名: {
  略
}


class 名

// 頭に『.』をつける
.class名: {
  略
}


id 名

// 頭に『#』をつける
#id名: {
  略
}



なので、上記のタグでいうと指定方法は
p: {
  略
}
.hoge: {
  略
}
#huga: {
  略
}

と表せる

プロパティと値

どのような属性にどのような値を設定するかという指定の方法を指す
例えば
background-color: red;は読んで字のごとく、背景色を赤くする
なので、全ての<p>タグの背景色を赤くしたい場合は、

p {
  background-color: red;
}

と表せる。なお、セミコロンを忘れると次の行以降が認識されないので注意
どのようなプロパティがあるのかはここをみると参考になる。

プロパティの書き順

プロパティの書き順はよく話題に上がるが、だいたい以下の 2 パターンになる
1. abc 順
2. 外側から内側へ定義していく(余白、ボーダー、文字色といった順)
よくわからないうちは 1 の abc 順で書いていくといい

css animation

海外産のクールな動きのある CSS はだいたい CSS Animatino を利用して作られている
CSS Animation とは特定プロパティに CSS プロパティを記載することで HTML にアニメーションを加えさせることのできる機能だ

設定方法は単純で、@keyframes 名前 と名付けた、アニメーションの進捗パーセンテージとそれに応じたプロパティを指定の方法で記入するだけだ。

@keyframes anime1 {
0% {width: 50px; height: 50px; background-color: aqua;}
100% {width: 200px; height: 50px; background-color: blue;}
}

例えば上記の例でいうと、anime1 と名付けた css アニメーションを定義している
その内容は、進捗率が0% の時は幅 50px 、高さ 50px、 背景色 aqua とし、進捗率が 100% の時は幅 200px 、高さ 50px、 背景色: blue とするものだ
つまり幅が広がって背景色の青が濃くなるアニメーションが定義されている
これに手を加えて、50% { background-color: red;}と入れれば、アニメーションの途中で背景色が赤くなり、また青に戻っていく

さて、これで定義されたアニメーションを、今度はセレクタを指定して当ててやる必要がある
設定の方法は以下のように行う

セレクタ {
  animation: anime1 5s ease -2s infinite alternate;
}

これらは前からanimationというプロパティ名の後から順番に
animation-nameの値、 animation-durationの値、 animation-timing-functionの値、 animation-delayの値、 animation-iteration-countの値、 animation-directionの値
となっている。

つまり、指定するアニメーションの名前、アニメーションにかける時間、アニメーションの変化具合(最初が早い、あとが遅い、一定など)、アニメーションが動き出すまでどれだけ遅延させるか、アニメーションの繰り返し回数、アニメーションを交互に反転再生させるかどうか、をスペースで区切って表示している

上記の例でいうと、anime1 というアニメーションを、5secの間、ease(最初が遅くあとが早い)という変化具合で、-2sec遅延させて、無限に、0% -> 100% -> 0% を繰り返すという設定をしている

JavaScript 実践

式と評価

JavaScriptは、ソースコードの上から下まで順々に一行ずつ評価(≒実行)している
例えば 3 と書かれた行があれば、3と評価して返却するし、3 + 4 と書かれた行があれば 7 と評価して結果を返却する
JavaScript に限らずプログラムの多くは式の評価とその返却の繰り返しを行なっている

例えばここに画面に alert を出すための命令 alert(3) があったとして、この命令が書かれた式が評価されると undefined(未定義)が返却される

変数

varキーワードを使うことで、変数を宣言することができる
変数とは値の入れ物のことで、例えば

var hoge

とすると、hogeという入れ物を用意することができる

代入式 =

変数とセットでよく使われるのが代入式である
代入とは、変数に値を入れることを指す言葉
代入式の使い方は簡単で

var hoge = 3

とすることで、hoge という変数(入れ物)に 3 を代入することができる

また、=は右の式を先に評価する性質があり

var hoge = 3 + 5

という式では、変数 hoge に 3 + 5 の計算結果である 8 が代入される

変数は上書き可能で、

var hoge = 3 + 5
hoge = 2

とすると、変数 hoge には 2 が代入されている。

var hoge = alert(3)

という式があれば、hoge には undefined(未定義) が代入されている。なぜなら、先にも書いたが alert の評価結果が undefined だからだ。もし alert の評価結果が100なら、この場合 hoge には 100 が代入されている

メソッド(関数)、引数、返値

メソッド(関数)
何かをさせる命令のこと。クラス(jsの場合はオブジェクト)に定義されている命令はメソッド、それ以外は関数と呼ぶ
引数
メソッド(関数)が引き取る値のこと `alert(3)` この例でいうと3が引数
返値
メソッド(関数)が評価された時に返す値のこと `alert(3)` この例でいうとundefined が返値

算術演算子

プログラムに計算を行わせる際に使う演算子(記号)の一種。
以下のような意味合いがある


+

足し算

-

引き算

*

掛け算

/

割り算

%

残算 例) 5 % 3 = 2, 9 % 8 = 1

jsの値には型がある。文字だったり数字だったり。


number

数字。

string

文字

boolean

真偽値。日本語で言えば有りか無しか。true か false で表現される。なお、全ての値は真偽値で評価可能。その際 false として評価されるのは、false, 0, ''(空文字), NaN, undefined, null の 6 つである

array

配列。配列とは要素の並びを指す。['ほげほげ', 'ふがふが', 'ぴよぴよ']とあったら、見た通り ほげほげ と ふがふが と ぴよぴよ が入った配列を表す。さらに['ほげほげ', 'ふがふが', 'ぴよぴよ'][0]とすると、ほげほげが返却される。お尻についている[]の中身は0始まりの数字。この数字のことを添字という

object

配列の添字に整数以外のものを使ったのがobject。正確には逆で、整数を添字にしているobjectが配列。{a: 'aaaa', b: 'bbbb', c: 'cccc'}というkey: valueで表現し、{a: 'aaaa', b: 'bbbb', c: 'cccc'}.a という風に.キー とすると、オブジェクトのうち指定されたキーと相対するvalueを返却する

NaN, undefined,null

それぞれ唯一の型。NaNはNot a Numberの略で、数字ではないことを示す。undefinedは先ほども書いている通り未定義を示す。nullは空を指す

比較演算子

式を boolean として評価するための演算子(記号)

演算子 説明
> 左辺が右辺より大きい
>= 左辺が右辺以上
< 左辺が右辺より小さい
<= 左辺が右辺以下
== 左辺と右辺は等しい(型変換は行われる)
=== 左辺と右辺は等しい(型変換は行われない)
!= 左辺と右辺は等しくない(型変換は行われる)
!== 左辺と右辺は等しくない(型変換は行われない)

繰り返し文

文字通り繰り返しを行う命令文として、for や while が存在している

for

初期化式;条件式;変化式の3つで構成される繰り返し文
以下は 0~99 をコンソールのログに出力する命令。コンソールのログは(win)ctrl + shift + c (mac)cmd + shift + c で開発者メニューを開き、consoleタブをクリックすると表示できる

// for (初期化式; 条件式; 変化式)
for (var i = 0; i < 100; i += 1) {
  console.log(i)
}

var i を初期化し、iが100未満の限り、繰り返すたびにiを1ずつ足していくというのが上記式。

while

whileの引数がtrueの限り実行され続ける

// この例では hoge が false にならない限り実行され続ける
var hoge = true
while(hoge) {
  document.addEventListener('click', () => {hoge = false})
  console.log('わん')
}

上記はクリックされない限り console の log に わん と出力され続ける式

その他

++
インクリメント(1を足す)。式の前に置いても後に置いてもいい。hoge ++ とすると、hoge に 1 を足す。hoge ++ なら hogeが評価された後にインクリメントされ、 ++ hoge ならインクリメントされた後に評価される
--
デクリメント(1を引く)。こちらも式の前に置いても後においてもいい。 hoge --とすると hoge から 1 を引く。 hoge -- なら hoge が評価された後にデクリメントされ、 -- hoge ならデクリメントされた後に式が評価される

メモ2に続く。誤字脱字間違った記載がありましたら修正いたします。

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

TypeScript学習ロードマップ

typeScript全然わかんない...
という状態から、プロジェクトに導入できるまでになんとかなったので、
学習の参考になったものなどをまとめて学習ロードマップを作成いたしました。
私自身もまだまだのレベルですが、これからTypeScriptを勉強したい!という方の道しるべになれば幸いです:runner_tone3:

Level 0: TypeScriptってなんぞや?

まず学習する前に、その対象がなんなのか、を見極める作業です。

typeScriptは altJS の1つです。
JSは元々大規模なコードを組むには不向きな設計になっているので、
altJSというメタ言語でラッピングすることで扱いやすくするものです。
altJSで他に有名なのはcoffeeScriptなどでしょうか。

typeScriptはtypeと名乗っている通り、静的型付けを特徴としています。
また、jsと互換性があり、jsの上位互換(スーパセット)です。

【おすすめ資料】
TypeScriptを入門者向けに解説!JavaScriptとの違いや勉強法までわかりやすく

Level 1: 記述等の基礎学習

以下のような資料をまずはざっくりと読みます。
下記資料全て熟読する、というよりは、それぞれをつまみ食いして大まかに把握していくイメージです。

【おすすめ資料】
TypeScriptチュートリアル① -環境構築編-
-> なにはともあれ環境構築!ですね。

TypeScriptの型入門
-> qiitaの良記事です。かなり長いので途中で苦しくなってきたら、また後から読み直すのもいいかもしれません。(私もお世話になりました)

サバイバルTypeScript
->網羅的な日本語でのts解説です。かなりわかりやすく導入も丁寧に感じました。

typeScript deep dive
-> 私はこれを中心に学習しました。意外と深い部分まで解説されているようです。ただ、元々英語の有志による翻訳なので、少しわかりづらい部分もありました。
訳に混乱したら他の資料を見、また戻って見直して...とすると理解が進みます。

Level2: TypeScript完全に理解した()

Level1までで全貌を掴んだところで、実際にどういう風に利用するのか?
という部分の理解の助けになります、みんな大好きオライリーの本です。

【おすすめ資料】
プログラミングTypeScript ――スケールするJavaScriptアプリケーション開発
発行日も2020年3月13日と比較的新しく、deepでかつわかりやすい良書でした。
後半はかなり高度な解説もあり、一読しただけで全てを吸収できるレベルではなかったです:eyes:
この先もなんども読み返すことになりそう、そんな本です。

Level3: TSXとの連携

実際にはreactやvueなどと組み合わせて使う人も多いと思います。
私はreactを使うのでreactの資料中心になってしまいましたが、その他フレームワークでも解説サイトがたくさんあると思います。

【おすすめ資料】
ReactのプロジェクトにTypeScriptを導入する〜npm installからコンパイルまで〜
-> なにはともあれ環境構(ry

React公式チュートリアルをTypeScriptでやる
-> Reactの公式チュートリアルでお馴染みの三目並べゲームをtypeScriptに移行する方法を解説されています。
ざっくりとしたReactのJSからTSへの移行を理解することができます。

typescript-cheatsheets/react
-> react/typescriptの実装をチートシートとしてまとめてくれています。
英語に抵抗がなければ実践的な実装の仕方がわかっていいのではないでしょうか。(実はまだ読んでいる途中です)

Level4: とにかくやってみることだ

ここまできたら実際にサンプルでもなんでもいいので組んでみたり、
既存のコードを移行してみるのが一番ですね。
TypeScriptに対応したパッケージなどであれば、公式サイトにtypeScriptという項目があったりしますので、それに目を通してみると色々為になります。

【おすすめ資料】
ここは実際には人それぞれですが、型定義ファイルををたくさん探しにいくことになるかと思います。

npm @type探し
styled-components typescript
redux-toolkit advanced

Level5: 読める...読めるぞ!!

実際に一通り組めるようになったら人のコードをみて勉強するのがgoodですよね!
ブログなどで解説されている方やqiita記事、githubや公式のチュートリアルなんかも学習になります。

【おすすめ資料】
仮想DOMは本当に“速い”のか? DOM操作の新しい考え方を、フレームワークを実装して理解しよう
-> 実際に簡単な仮想DOMフレームワークを実装して、仮想DOMについて考えるサイトです。
ちょっと長いですがすごく勉強になります。

apollo docs
-> GraphQLでお馴染みapolloの公式チュートリアルはtypeScriptで書かれています。

vercel/next.js
-> next公式のtypescript記述のサンプルです。かなりシンプルに実装されています。

・今までの資料を読み返す

そして伝説へ...

参考資料

altJS(代替JavaScript言語)とは?選び方と注意点を徹底比較!

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

【超入門レベル】JavaScriptの基本シート

JavaScriptの基本チートシート

キノコードさんのYouTubeを参考にしています。

目次

  1. JavaScriptの特徴
  2. 環境構築
  3. プログラムの実行

JavaScriptの特徴

  • 1995年
  • ブレンダン・アイクさん
  • ページを更新しなくても画像が切り替わる
  • 入力中に検索結果が出てくる
  • フロントでもサーバー(node.js)でも動かせる
  • 非同期処理ができる
  • 豊富なフレームワーク→「jQuery」「React」など

環境構築

  • Node.js→サーバー側で動くJavaScript→JS自体はMacに標準でインストールされている。
  • VSCode本体

Node.js

公式サイトで推奨版をインストール
セキュリティとプライバシーで、一般で「このままクリック」
ターミナルで、「node --version」でインストール確認

プログラミングの実行

ファイルの拡張子→「.js」
ソースコードの記述→処理の終わりにセミコロンを書く
ソースコードの保存→⌘+Sで保存
ソースコードの実行→「表示」>「ターミナル」、「node ファイル名」

変数

変数の宣言

var num;
num = 1;
console.log(num);

var num = 1;
console.log(num);

変数名のルール

  • アルファベット、数字、アンダースコア、ドルマークが使える
  • 数字から始めることができない
  • アンダースコアやドルマーク以外の記号は使えない
  • 大文字と小文字は区別される
  • 予約語は使えない(returnやclass、for、whileなど)

コメントアウト

「//」を先頭につける
⌘+/がショートカットキー

データ型

  • JavaScriptは、動的型付け言語。

数値型(number型)

  • 数値型のnumber型
  • typeof演算子(タイプオフ)で、データ型を表示することができる

文字列型(string型)

  • " "で囲む

ブール型(Boolean型)

  • TrueかFalseのどちらかを持つ型

配列

  • 複数のデータを格納することができるデータ型。
  • 一次元配列、多次元配列
  • 添字は「順番 - 1」

配列の作り方

  • var 変数 = new Array(要素数)
var a = new Array(3);

a[0] = "sato";
a[1] = "suzuki";
a[2] = "takahashi";

console.log(a[0]);
console.log(a[1]);
console.log(a[2]);

var a = new Array("sato","suzuki","takahashi");

console.log(a[0]);
console.log(a[1]);
console.log(a[2]);

  • var 変数 = [データ1,データ2,……]
var a = ["sato","suzuki","takahashi"]

a[1] = "tanaka"

console.log(a[0]);
console.log(a[1]);
console.log(a[2]);

多次元配列

var arr;
arr = [["sato","suzuki"],["takahashi","tanaka"]]

console.log(arr[0][0]);
console.log(arr[0][1]);
console.log(arr[1][0]);
console.log(arr[1][1]);

演算子

  • 算術演算子→+,-,*,/,%
  • 関係演算子→>,<,>=,<=,==,!=
  • 論理演算子→&&,||
  • 代入演算子→=
  • 複合代入演算子→+=,-=,*=,/=
  • インクリメント、デクリメント→変数名++,変数名--

条件分岐

if文

var age = 0;

if (age >= 20) {
  console.log("adult");
} else if (age==0) {
  console.log("baby");
} else {
  console.log("child");
}

繰り返し

for文

  for(var i = 0; i <= 4; i ++) {
    console.log(i);
  }

break文

  for(var i = 0; i <= 4; i ++) {
    if(i == 3) {
      break;
    }
    console.log(i);
  }

continue文

  for(var i = 0; i <= 4; i ++) {
    if(i == 3) {
      continue;
    }
    console.log(i);
  }

ネスト

  for(var i = 0; i <= 2; i ++) {
    for(var j = 0; j <= 2; j ++) {
      console.log( i + "-" + j );
    }
  }

配列を繰り返す

  var arr = [2,4,6,8,10];
  var sum = 0;

  for(var i = 0; i <= 4; i ++) {
    sum += arr[i];
  }
  console.log(sum);

関数

function say_hello() {
  console.log("Hello World");
}
say_hello();

  • 関数式→関数を変数に代入することができる。
var hello= function say_hello() {
  console.log("Good Morning");
};

hello();

関数式は関数名を省略することができる。→無名関数
関数ではなく、変数として扱うみたいな感覚。

var hello = function() {
  console.log("Good Morning");
};

hello();

引数をを呼び出すと

function say_hello(greeting) {
  console.log(greeting);
};

say_hello("Good Morning");

function cal(x) {
  console.log(x * 3);
};

cal(6);

function cal(x, y) {
  console.log(x / y);
};

cal(6, 3);
function cal(x, y) {
  return x / y;
};

var result = cal(6, 3);
console.log(result);

※returnがあるメソッドのほうがお得
→その戻り値をさらに計算や文字列に結合することができる。

クラス

JavaScriptのクラス

  • データをプロパティという→クラス内で定義された変数
  • 処理をメソッドという→クラス内の関数

クラスの定義と使い方(インスタンス化)、メソッド実行

2つの方法がある。

クラス宣言

class Student {

    avg() {
        console.log((80 + 70) / 2);
    }
}

var a001 = new Student();
a001.avg();
class Student {

  avg(math, english) {
    console.log((math + english) / 2);
  }
}

var a001 = new Student();
a001.avg(80, 70);

プロパティの定義

class Student {

  avg(math, english) {
    console.log((math + english) / 2);
  }
}

var a001 = new Student();
a001.name = "sato";
a001.avg(80, 70);

console.log(a001.name);

コンストラクタの使い方→プロパティの自動生成

class Student {
  constructor() {

  }
  avg(math, english) {
    console.log((math + english) / 2);
  }
}

var a001 = new Student();
a001.name = "sato";
a001.avg(80, 70);
console.log(a001.name);

var a002 = new Student();
console.log(a002.name);
class Student {
  constructor() {
    this.name = "";
  }
  avg(math, english) {
    console.log((math + english) / 2);
  }
}

var a001 = new Student();
a001.name = "sato";
console.log(a001.name);

var a002 = new Student();
a002.name = "tanaka";
console.log(a002.name);
class Student {
  constructor(name) {
    this.name = name;
  }
  avg(math, english) {
    console.log((math + english) / 2);
  }
}

var a001 = new Student("sato");
console.log(a001.name);

var a002 = new Student("tanaka");
console.log(a002.name);

クラス式

var Student = class {
  constructor(name) {
    this.name = name;
  }
  avg(math, english) {
    console.log((math + english) / 2);
  }
};

var a001 = new Student("sato");
console.log(a001.name);

var a002 = new Student("tanaka");
console.log(a002.name);

総まとめ

  • クラスを定義
  • コンストラクタの定義(プロパティの定義)
  • 平均を計算するメソッドを定義
  • テスト結果を判定するメソッドを定義
  • インスタンス化する
  • 配列に点数を格納
  • 変数に平均点を代入
  • 変数に判定結果を代入
  • それぞれ表示させる
class Student{

  constructor(name) {
    this.name = name;
  }

  cal_avg(data) {
    var sum = 0;
    for(var i = 0; i < data.length; i++) {
      sum += data[i];
    }
    var avg = sum / data.length;
    return avg;
  }

  judge(avg) {
    var result;
    if(60 <= avg) {
      result = "passed";
    } else {
      result = "failed";
    }
    return result;
  }

}

var a001 = new Student("sato");
var data = [70, 65, 50, 90, 30];
var avg = a001.cal_avg(data);
var result = a001.judge(avg);    

console.log(data.length);
console.log(a001.name);
console.log(avg);
console.log(result);

まとめ

そろそろ疲れてきた。
RubyとPythonとJavaScriptと3つのプログラミング言語を学んできたけど
それぞれサーバーサイドをいじれるのね。
だけど、やっぱりJavaScriptはフロントのイメージが強い!

だって、この動画では、非同期感がまったくなかったから。
ただただ、文法を学んだって感じ。

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

プログラミング言語を学ぶ時には、はじめにプログラミング言語の家系図を見ておくとよいよ、という話

プログラミミング言語の家系図ってなに

あるプログラミングがいつ、どのような言語に影響を受けて誕生したかを表す系譜のことです。

プログラミミング言語の家系図はここにある

プログラミング家系図を載せているサイトはいくつかあるのですが、今回はこちらのサイトを紹介します。

diagram & history of programming languages (プログラミング言語の系譜)

program_diagram.PNG

主要言語のみものと、150以上の言語を載せているバージョンの2種類が記載されています。
2020年8月時点で、2018年の分まで記載されています。

いくつかの言語をピックアップ

主要言語の図から、いくつかの言語をピックアップしてみましょう。

C言語(K&R)

  • 誕生: 1978年
  • 影響を受けた言語: Algol 60
  • 影響を与えた言語: C++, Python (他、子孫多数)

注)
C言語の誕生は1972年であるが、リンク先の図では、『プログラミング言語C』(原題:The C Programming Language、通称 K&R)が出版された年を採用している。(参考: C言語 - Wikipedia, プログラミング言語C - Wikipedia)

Python

  • 誕生: 1991年
  • 影響を受けた言語: C, C++, Pascal
  • 影響を与えた言語: Ruby

Ruby

  • 誕生: 1995年
  • 影響を受けた言語: Perl, Eiffel, Python
  • 影響を与えた言語: Swift

Java

  • 誕生: 1995年
  • 影響を受けた言語: C++
  • 影響を与えた言語: JavaScript, C#, Kotlin

JavaScript

  • 誕生: 1995年
  • 影響を受けた言語: Java
  • 影響を与えた言語: Kotlin

プログラミング言語(2言語目以降)を学ぶ時には、はじめにプログラミング言語の家系図を見ておくとよいよ

さて表題の件ですが、2言語目以降のプログラミング言語を学ぶときにはこの家系図を最初に確認することをおすすめします。

理由は

  • 自分がすでに知っているプログラミング言語と、新しく学ぶ言語がどのくらい離れているかによって習得難易度を把握することができるから
  • 自分がすでに知っているプログラミング言語より新しい場合、新しくて便利な構文があることを期待しながら学習を進められるから
  • 自分がすでに知っているプログラミング言語より古い場合、便利な機能がないことを覚悟しながら学習を進めることができるから

です。

自分の場合は、Java歴5年、Perlはバッチ程度ならで少し書ける程度のときに、Rubyを習得しようとしました。
そのときに、RubyはPerlから影響を受けていることを知ったので構文の理解に役立ちました。
一方、関数型言語の知識はほぼ無かったのでその部分は苦労しましたが、そのことを事前に予測できたのはよかったと思います。

あとがき

はじめてのRuby』(著: Yugui) にRubyに関係する部分のプログラミング言語家系図が記載されていました。この図はRubyの理解に大変役に立ちました。

ほかの言語の図も探していたところリンク先のものが見つかりました。

プログラミング言語はここ数十年で大幅に進化しているのだなあ。

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

JSONを楽にJavaScriptObjectへ変換する

JSONファイルを読み取って, JavaScript Objectへ変換する関数を実装しようとしました.
行き詰まっていたところ, teratailで質問をするとすぐに回答が付きました. 便利な世の中になりましたね.
https://teratail.com/questions/285284

以下ソースコードです. jQuery依存コードです.

const readjson = path => {
    let jsondata;
    $.ajaxSetup({ async: false }); // Ajax通信を同期通信にする
    $.getJSON(path, data => {
        jsondata = data;
    })
    $.ajaxSetup({ async: true }); // Ajax通信を非同期通信に戻す
    return jsondata;
};

使うときは, [const/let] [変数名] = readjson([jsonのパス])を記述します.

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

RPGツクールMV プラグイン作成入門 (3) プラグイン用テンプレートの更新

RPGツクールMV で利用できるプラグインは、JS (JavaScript) ベースで Game Scripting System (JGSS) 環境上で動作します。これは JS 基本知識がある方が対象のプラグイン作成の入門資料っぽい何か、です。

4年ほど前に jgss-hack リポジトリ で公開した内容をもとに、今の環境用にリライトしてみました。これは第3回です。

RPGツクールMZ の発売を控えているので、その前にまとめておきたい、と焦っていたりします。なので内容薄目でごめんなさいw

関連リソース

RPGツクールMV プラグイン作成入門

  1. 簡単なプラグインを作成してみる
  2. 簡単なプラグインを拡張してみる
  3. プラグイン用テンプレートの更新【このページ】

今回の内容

第1回でご紹介したプラグインの元ファイル ですが、4年以上前ということもあり、いろいろ改善の余地がありました。(@elleonard_f さん、コメントありがとうございます!)

そこで今回は、MZ 発売前のまとめとして、現時点で私が使用している最新版をご紹介します。また簡単に説明したいとおもいます。

現在のテンプレート

以下がテンプレート用コードです。GitHub 上 にも置いてあります。

RTK_Test2.js
//=============================================================================
// RTK_Test2.js 2020/08/16
// The MIT License (MIT)
//=============================================================================

/*:
 * @plugindesc Test Plugin 2
 * @author Toshio Yamashita (yamachan)
 *
 * @help This plugin does not provide plugin commands.
 * This is for the test purpose only. So, please rename for the production use.
 */

/*:ja
 * @plugindesc テスト用プラグイン2
 * @author Toshio Yamashita (yamachan)
 *
 * @help このプラグインにはプラグインコマンドはありません。
 * テスト用に作成したものなので、実際に利用する場合には適当にリネームしてください
 */

(function(_global) {
    "use strict";
    var N = document.currentScript.src.replace(/^.*\/([^/]+)\.js$/, '$1');
    var param = PluginManager.parameters(N);

    // ここにプラグイン処理を記載
})(this);

説明テキストの多言語化

新しいテンプレートでは、英語と日本語の2種類の説明テキストを設定してあります。

ここでポイントとなるのは、以下の赤枠の ja 部分です。これは、この部分のコメントが「日本語」であることを示しています。
image.png
日本語環境で実行されている RPGツクールMV のプラグイン設定の画面では、この ja が指定された日本語のテキストが優先して表示されます。

それ以外 (英語とか中国語とか) の環境では、特に言語指定のないテキスト、つまり上にある英語テキストが表示されます。

海外でもプラグインを使って貰えるチャンスだと思って、できれば英語のテキストも用意しておくと良いでしょう。

Strict モードの利用

Strict モード はエラーチェックが厳しくなるもので、関数単位で指定します。間違いを見つけやすくなりますし、より高速に実行できるようチェックしてくれますので、ぜひ積極的に利用していきましょう。

関数の最初にある "use strict"; 文がこのための指定になります。
image.png
これは JavaScript として意味のある文ではありませんが、関数定義の最初にこの指定があることで、その関数内では Strict モードが有効になります。

プラグイン名の自動設定

さて、今回のテンプレートで一番複雑なのは、プラグイン名を設定している以下の部分ですね。
image.png
プラグインのファイル名から、拡張子を除いた名前 (今回だと RTK_Test2.js から RTK_Test2) を得たいだけなんですよね。

現在実行しているスクリプトのタグは Document.currentScript で得られます。その src 属性は以下の感じなので、
image.png
ここから 正規表現 で欲しい部分だけを抜き出しているわけです。なお元ネタはコメントでも紹介いただいた、尊敬する トリアコンタン さんの、こちらの情報 です。

パラメーター処理の準備

オマケとして、パラメーター処理の準備をしておきます。今回のテンプレートでは特に指定していませんが、まあ、実際にプラグイン作成する場合にはたいてい必要になりますよね。なので準備だけ。
image.png
なお実際のパラメーター処理なのですが、具体的な指定方法は プラグインパラメータのtypeまとめ を参照してみてください。(情報をいただいた @elleonard_f さん、こちらも感謝です!)

というわけで

だいぶ古くなっていた、そして当時の理解不足もあった、プラグインの基本テンプレートを更新してみました。自分の知識も整理できたので、RPGツクールMZ を迎える準備としては、やって良かったかな、とも思います。

それではまた!

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

自分用メモ reactスタイリング styled-components

はじめに

reactとjsをいじり始めて、まだ日が浅い素人ですが、人によってcssに記述方法が違っていて、自分もどれにすればいいのかまた古い情報や新しい情報が入り混じって大変わかりにくいので、ここでまとめておこうと思います!!

至らない点が数あると思いますが、その際はアドバイスしていただけると幸いです。

そしてトラハックさんのこちらの動画を中心にまとめています!!
この記事より詳しくわかりやすく説明しておりますので、ぜひこちらからご覧になってみてください!!

インラインスタイル

・JSXのstyle属性を使う
・導入が簡単
・公式では非推奨
・計算が増える  パフォーマンスが落ちる!
・擬似要素を使えない 例)before, hover, after,

import React from 'react';
const style = {
  backgroundColor: 'none';
  border: 'none',
  display: 'block',
  padding: '4px 16px',
}
const PrimayButton = (props) => {
  return(
    <button style={style}
      青いボタン
    </buttoon>
  );
};
export default PrimaryButton;

注意)キャメルケース

クラスによるスタイリング

・HTMLファイル読み込み
・導入が簡単
・全てがグローバルスコープ
 →命名規則を工夫しなければならない!
 →どこにどのクラスを書いたか?

注意)元になるindex.jsで読み込まなければならない!(create-react-app)
一般的なスタイルになるのでコードは省かせていただきます!

CSS Modules

・Webpack+Babelを使う  
・CSSをモジュールとしてimport
・慣れ親しんだCSS記法
・クラス名の自動変換
・jsの読み取り順序で適用順序も変わる

先ほどと違うのはjsのなかでimportする!
モジュール化している(css)

webpack・・・モジュールバンドラーと呼ばれ複数のファイルをまとめる!
babel・・・コンパイラー(トランスコンパイラー)中身を変換する(ここではcss)

.button{
  background-color: #42a5f5;
  border: none;
  display: block;
  padding: 4px 16px;
}
import React from 'react';
import './button.css';
const PrimayButton = (props) => {
  return(
    <button className={"button"}>
      青いボタン
    </button>
  )
}

Css in js

・styledーcomponentが有名
・スコープ限定
・ベンダープレフィックス付与
  ブラウザ対応を自動でやってくれるもの
・propsでスタイルを変換できる!
・シンタックスハイライト
→style objectで解決

import React from 'react';
import styled from 'styled-components';
const Button = styled.button({
  backgroundColor: '#442a5f5',
  border: 'none',
  display: 'block',
  padding: '4px 16px',
  '&:hover': {
    backgroundColor: '#80d6ff',
  }
})
const PrimaryButton = (props) => {
  return(
    <Button>
      青いボタン
    </Button>
  )
}

propsによってスタイル変更もできてスコープも限定し、命名規則によって縛れることもないまたオブジェクト型にすることでシンタックハイライトも入るようになった!
またコンポーネントにスタイルをつけたい場合は、styled.(component名)({})とする!

styled-component

この方法がcss in jsの一般的になるので説明しておきます!
これについてもトラハックさんの別動画を参照しておりますので詳しく知りたい方はこちらからご覧いただくようにお願いいたします!

記法

・Tagged Template Literal(シンタックスハイライトなし)
→キャメルケースではない

const Temlate Literal = styled.button`
  background-color: #42a5f5;
  border: none;
  display: block;
  padding: 4px 16px;
  &:hover: {
    background-color: #80d6ff
  }
`

・style Objects(シンタックスハイライトあり)
→キャメルケースになる

const Button = styled.button({
  backgroundColor: '#42a5f5',
  border: 'none',
  display: 'block',
  padding: '4px 16px',
  '&:hover': {
    backgroundColor: '#80d6ff'
  }
)}

propsでスタイルを切り替える

const Button = styled.button(props => ({
  backgroundColor: props.isPrimary ? '#41B&E&' : '#FFB549',
    border: 'none',
    display: 'block',
    padding: '4px 16px',
    '&:hover':{
      backgroundColor: props.isPrimary ? '#a2dbf3' : '#FFCA7C',
    }
}))
const BaseButton = (props) => {
  return(
    <Button>
      {props.label}
    </Button>
  );
};
//呼び出し側
//<BaseButton isPrimary={true} label={"Primary"}
//<BaseButton isPrimary={false} label={"Secondary"}


注意)? true : false

ここではisPrimaryのtrue,falseによってスタイルを変えている!(backgroundColor)

themeを使う

import React from 'react';
import styled, {ThemeProvider} from 'styled-components';
const theme = {
  main: '#41B6E6',
  light: '#a2dbf3'
}
const BaseButton = (props) => {
  return (
    <ThemeProvider theme={them}>
      <Button>
        {props.label}
      </Button>
    </ThemeProvider>
  );
};
export default BaseButton;

ここではthemeを宣言後、使いたいコンポーネントでラッピングする!
そしてthemeを渡す!

const Button = styled.button({
  backgroundColor: 'theme.main',
  border: 'none',
  display: 'block',
  padding: '4px 16px',
  '&:hover':{
    backgroundColor: theme.light
  }
})

そしてコンポーネント内で呼び出す!

最後に

まとめてみてstyle Objectsの形がスコープやシンタックスハイライト、propsの切り替えなどの観点から見て一番使いやすいのかなと感じたので積極的に使っていこうと思いました。
まだまだ知識不足で見落としている箇所などあると思いますがその際はアドバイスしていただけると幸いです!
最初にも書いた通り参照したのはトラハックさんの動画になりますので詳しく知りたい方はそちらの動画を見ていただくようお願いいたします!

参照

トラハック 【結論】Reactのスタイリング方法を比較するぞ【CSS in JS推したい】

トラハック Reactのスタイリング定番styled-componentsの活用パターン

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

(JavaScript) fetchとPromise.allで複数のJSONファイルを取ってこよう!

async/awaitでPromise.allがresolveを返してくるまで待つ!

sample.js
(async()=>{
    const jsons = await Promise.all(["/a.json","/b.json"].map(async(v)=>(await fetch(v)).json()));
    console.log(jsons);
    //[{...}, {...}]
})();

インデントを入れて読みやすく

sample.js
(async () => {
    const jsons = await Promise.all(
        [
            "/a.json",
            "/b.json"
        ].map(
            async(v) => (await fetch(v)).json()
        )
    );
    console.log(jsons);
    //[{...}, {...}]
})();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLライクな「グラフ」クエリエンジンOpen SOQLを作ってみた

本日(2020-08-17)、Open SOQL 最初の安定版となる v0.1.0 をリリースしました?

Open SOQLはSalesforceで使われている独自のクエリ言語「SOQL」(Salesforce Object Query Language) のオープンソース実装版です。

オリジナルのSOQLは長い歴史を持ちますが (詳しい歴史を辿れないのですが、2008年には既に存在していたようです)、オブジェクトのグラフを利用者が必要な項目に絞って取得できる (つまり、オーバークエリしない) という、まるでGraphQLのような特徴を持っています。
(GraphQLの初版は2015年のようです)

文法もSQLに近く学習コストが抑えられており、さらにGroup byによる集計もサポートしています。

Open SOQLはオリジナルのSOQLとは異なり、SalesforceのデータをクエリするものではありませんGraphQLApollo Server等と同様に、ノードに対応するリゾルバ (コールバック関数) にデータの取得・更新を移譲します。これにより、任意の永続化ストレージ、あるいは動的データに対してSQLライクなクエリ・インターフェースを提供することができます。

モチベーション

仕事でとあるローコード・プラットフォームのカスタマイズを行ったときに、複数のテーブルに跨がるデータを親子のデータ構造に纏めるのが苦痛でした。

そのプラットフォームではテーブル同士のリレーションシップが設定できるのですが、カスタマイズにおいては、テーブル毎に異なるREST APIエンドポイントからデータを取得する必要がありました。

欲しいデータ構造にするために自力で (つまり、毎回コードを書いて) 結合したり集計したりするのは効率が悪く、「ちょっとしたカスタマイズ」が複雑化しやすくなります。

同じローコード・プラットフォームに分類されることもあるSalesforceならばSOQL一発で取得できるのに、と考えたくなります。
(コード書かなくてもいいし、書くとなったら気持ちよく書けるプラットフォームがいいですね!)

少し前に自作のパーサーコンビネータ・ライブラリで割と複雑な構文解析を行うライブラリを作成できたので、SQLっぽい言語くらい余裕だろうと思って作成を開始しましたが、それ以外のところで色々上手くいかず、リリースまでに予定の倍である2か月も掛かってしまいました?

使い方の簡単な説明

インストール

npm install open-soql

利用

  • まず、リゾルバを設定します。
import { build } from 'open-soql/modules/builder';
import { staticJsonResolverBuilder,
         staticCsvResolverBuilder,
         passThroughResolverBuilder } from 'open-soql/modules/resolvers';

const commands = build({
    relationships: {                                    // リレーションシップを定義します
        Account: {                                      // リゾルバ名
            Contacts: ['Contact'],                      // リレーションシップ項目名 → リゾルバ名
            Opportunities: ['Opportunity', 'Account'],  // 相手のリレーションシップ項目を明示的に指定する場合
        },
        Contact: {
            Account: 'Account',
        },
        Opportunity: {
            Account: 'Account',
        },
        Event: {
            Account: { resolver: 'Account', id: 'WhatId' },  // Id項目を明示的に指定する場合
            Contact: { resolver: 'Contact', id: 'WhatId' },
            Opportunity: { resolver: 'Opportunity', id: 'WhatId' },
        },
    },
    resolvers: {  // リゾルバを定義します
        query: {
            Contact: staticCsvResolverBuilder(  // 固定のJSON・CSV・オブジェクト配列からは、簡単にリゾルバを作成できるように標準のリゾルバ実装を提供しています。
                'Contact', () => Promise.resolve(`
                    Id         , Foo      , Bar      , Baz      , Qux      , Quux  ,   Corge , Grault       , Garply                 , AccountId
                    Contact/z1 , aaa/z1   , bbb/z1   , ccc/z1   , ddd/z1   , false ,    -1.0 , 2019-12-31   , 2019-12-31T23:59:59Z   , Account/z1
                    Contact/z2 , aaa/z2   , bbb/z2   , ccc/z2   , ddd/z2   , true  ,     0.0 , 2020-01-01   , 2020-01-01T00:00:00Z   , Account/z1
                    Contact/z3 , "aaa/z3" , "bbb/z3" , "ccc/z3" , "ddd/z3" ,       ,     1   , "2020-01-02" , "2020-01-01T00:00:01Z" , "Account/z2"
                    Contact/z4 ,          ,          ,          ,          ,       ,         ,              ,                        ,
                    Contact/z5 ,       "" ,       "" ,      " " ,       "" ,       ,         ,              ,                        ,
                `)
            ),
            Account: staticCsvResolverBuilder(
                'Account', () => Promise.resolve(`
                    Id         , Name     , Address
                    Account/z1 , fff/z1   , ggg/z1
                    Account/z2 , fff/z2   , ggg/z2
                    Account/z3 , "fff/z3" , "ggg/z3"
                    Account/z4 ,          ,
                    Account/z5 ,       "" ,       ""
                `)
            ),
            Opportunity: staticCsvResolverBuilder(
                'Opportunity', () => Promise.resolve(`
                    Id             , Name     , Amount , AccountId
                    Opportunity/z1 , hhh/z1   ,   1000 , Account/z1
                    Opportunity/z2 , hhh/z2   ,   2000 , Account/z1
                    Opportunity/z3 , "hhh/z3" ,   3000 , Account/z2
                    Opportunity/z4 ,          ,        ,
                    Opportunity/z5 , ""       ,      0 , Account/z2
                `)
            ),
            Event: staticCsvResolverBuilder(
                'Event', () => Promise.resolve(`
                    Id         , Title    , Address  , WhatId
                    Event/z1   , iii/z1   , jjj/z1   , Account/z2
                    Event/z2   , iii/z2   , jjj/z2   , Contact/z2
                    Event/z3   , "iii/z3" , "jjj/z3" , Contact/z3
                    Event/z4   ,          ,          ,
                    Event/z5   ,       "" ,       "" , Opportunity/z5
                `)
            ),
        },
    },
});

const { soql, insert, update, remove, transaction } = commands;
  • リゾルバ構築で得られたコマンドを使って、クエリ・DMLを発行します。
// Contact > Account > Opportunity の3階層から成るオブジェクトを返します
const selected = await soql<Partial<Contact>>`
    select
        id, foo, bar, baz, acc.id, acc.name,
        (select id, name, amount from acc.opportunities)
    from
        contact con, account acc
    order by id, foo desc
    offset 1 limit 2`;

const inserted = await insert('Contact', [{
    Foo: 'foo', ...
}]);
const updated = await update('Contact', inserted);
await remove('Contact', updated);
  • トランザクション・スコープでクエリ・DMLを纏めることもできます。
await transaction(async (commands, tr) => {
    const { soql, insert, update, remove } = commands;

    const inserted = await insert('Contact', [{
        Foo: 'foo',
    }]);
    const selected = await soql<Partial<Contact>>`Select Id, Foo from Contact`;
    const updated = await update('Contact', selected);
    await remove('Contact', updated);
});
  • 集計も可能です。
const aggregationResult = await soql<ContactAgg>`
    Select
        AccountId
      , count()
      , count(id) cnt
      , sum(bar) sum
    from
        Contact
    where
        foo > ''
    group by AccountId
    having count(id) > 0
    order by AccountId
    offset 1 limit 2`;

N+1問題 (N+1クエリ問題) への対策

このようなリゾルバで解決するようなクエリ・エンジンやO/RM等の実装では、いわゆる「N+1問題」が発生します。

親オブジェクトとその関連する子オブジェクトを取得する際、親オブジェクトのリストをクエリするのに1回、取得できたN個の親に対する子を得るためにN回のクエリが必要になり、パフォーマンス問題が発生します。
合計のクエリ回数が N+1 回であるため、N+1問題と呼ばれます。

GraphQLではDataLoaderという仕組みを使って解決することができます。

Open SOQLに於いては、クエリ前のイベントでデータを事前に一括取得することで解決します (親レコード一覧が得られるので、そこからキーを取得してクエリします)。

const commands = build({
    ...
    events: { // optional: For resolving transaction and N+1 query problem.
        beginTransaction: (evt) => Promise.resolve(),
        endTransaction: (evt, err) => Promise.resolve(),
        beginExecute: (evt) => Promise.resolve(),
        endExecute: (evt, err) => Promise.resolve(),
        beforeMasterSubQueries: (evt) => Promise.resolve(),  // 一括取得します
        afterMasterSubQueries: (evt) => Promise.resolve(),
        beforeDetailSubQueries: (evt) => Promise.resolve(),  // 一括取得します
        afterDetailSubQueries: (evt) => Promise.resolve(),
    },
    ...
});

追記 (2020-08-17)

使い方のサンプルリポジトリを作成しました。
(トランスパイラ・バンドラを使用せずに直接ES ModulesをNodeで実行しています)
https://github.com/shellyln/open-soql-usage-example

今後の開発について

まだ、ネストした関数呼び出しが処理できないため、当面は関数呼び出し関連の改善を優先して行う予定です (集計関数とスカラー関数両方を含むネストの対応が特に課題です)。
余裕ができたら「とあるローコード・プラットフォーム」用のリゾルバも作成したいです・・・

さいごに

よろしければ、是非、使ってみてください。
フィードバックを頂けると大変嬉しいです?

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

Rails jsファイルの読み込みはなぜviewの一番下に記述するのか?

今までなんとなくでjsファイルの読み込みをviewの一番下に記述してたが、なぜそうするのかが学べたので備忘録投稿

scriptタグを記述する場所

scriptタグを記述する場所は以下の2つ
- </body>の直前
- <head></head>の中

どっちに記述した方がいいの?

通常は</body>の直前に記述するのがいい。
理由としてはブラウザがHTML要素を読み込むとき、上から順々に読み込んでいく。そのときにjsのコードを読み込むのはHTMLに比べて時間がかかる。
つまり上に記述される<head></head>の中にすると、jsの読み込み時間がかかってしまい肝心なHTML要素が読み込まれずに、ブラウザに何も表示されなくなる時間が発生してしまう。

つまり</body>の直前に記述すると、、、?

<body></body>の最後に記述することによって、HTML要素の解析を済ましてからjsファイルの読み込みをさせた方が、ユーザビリティが高い

例外

<body></body>が表示されると同時にjsを実行させたり、jsでcssを操作する場合などは<head></head>の中に記述する必要がある。

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

【JavaScript関数ドリル】 初級編 without関数の実装

without関数の課題内容

「課題内容」「解説動画」「解答例」を確認したい場合は、以下リンク先のページを参照。

https://js-drills.com/blog/without/

without関数 に取り組む前の状態

without関数は、第3引数の数字を省くのかな?と感じました。

without関数に取り組んだ後の状態

・...の3つが第三引数のすべてを表すと初めて学びました。
・includesに!をつけることで、「含まれない」を表す!
・if文の書き方コードの内容が変わる変えれる。

without関数の実装コード(答えを見る前)

・自力で実装できませんでした。

without関数の実装コード(答えを見た後)

function without(array, ...values) {
  const newArray = [];
  for(let i = 0; i < array.length; i++) {
    const candidateToPush = array[i];

    if (!values.includes(candidateToPush)) {
      newArray.push(candidateToPush);
    }
  }
  return newArray;
}


console.log( without([2, 1, 2, 3, 4], 1,2) )

バリューに含まれていない3、4が結果として出る。

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

JavaScript オブジェクト型データ操作の基本

オブジェクト型データを制する者はJavaScriptを制す

JavaScript中級者になるためには、オブジェクト型データ { } の理解が必須です。
コードを読む場合も、書く場合も、JSONデータを作る時も、オブジェクト型データは基本となります。
この記事は、オブジェクト型データの重要ポイントだけまとめました。

オブジェクト型データの基本文法

オブジェクト型データの定義方法

  let person = { name: "ICHIRO", age: 20 }

nameの部分をプロパティ、"ICHIRO"の部分を値と呼びます。key/valueと表現したほうがわかりやすいかも。
複数のkey:valueセットをカンマ区切りで格納することができます。

データの参照方法

  person.name // ICHIRO
  person.age  // 20

変数名にドット演算子「.」を付与して、key文字列を記述すると、valueが取り出せます。
nameもageもpersonに関する情報だということが一目でわかるので、とても便利。

データの編集方法

  person.name = "イチロー" // keyに紐づくvalueの上書き更新 ( update )
  person.job  = "baseballer" // いきなりkeyとvalueのセットを追加可能 ( insert )
  person.age  // ageのkeyと紐づくvalueを削除する ( delete )

データベースのSQLのように、格納したデータのCRUD操作すべてが可能です。

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

【JavaScript】オブジェクト型データ操作の基本

はじめに

JavaScriptのオブジェクト型データがよくわからない方のために、重要ポイントを基本から応用までまとめました。
データの基本操作からJSONの活用、連想配列の解説をしています。

オブジェクト型データの基本文法

オブジェクト型データの定義方法

  let person = { name: "ICHIRO", age: 20 }

nameの部分をプロパティ、"ICHIRO"の部分を値と呼びます。key/valueと表現したほうがわかりやすいかも。
オブジェクト型は中括弧{}の外殻の中に、複数のkey:valueセットをカンマ区切りで格納することができます。

オブジェクト型データの参照方法

  person.name // ICHIRO
  person.age  // 20

変数名にドット演算子「.」を付与して、key文字列を記述すると、valueが取り出せます。
nameもageもpersonに関する情報だということが一目でわかるので、とても便利。

オブジェクト型データの編集方法

  person.name = "イチロー" // keyに紐づくvalueの上書き更新 ( update )
  person.job  = "baseballer" // いきなりkeyとvalueのセットを追加可能 ( insert )
  delete person.age  // ageのkeyと紐づくvalueを削除する ( delete )

データベースのSQLのように、格納したデータのCRUD操作すべてが可能です。
オブジェクト型データの削除は、delete文を使います。

オブジェクト型データに関数を持たせる(メソッドの設定)

  // 基本的な記述方式
  person.throw = function(){
    console.log("投げます!");
  };

  // ES6以降の最新の記述方式
  person.hit = () => console.log("打ちます!");

  // 実行する場合
  person.throw(); // 投げます!
  person.hit();   // 打ちます!

オブジェクト型データに関数を格納して実行することもできます。
なお、オブジェクト型に持たせた関数は、「メソッド」と呼ばれます。
この場合、現実世界の「人」をモデル化した「person」の動きを表現するのがperson.throw()やperson.hit()になりますので、そういった意味で関数ではなく、メソッドと呼んだ方が確かにしっくりきます。

メソッドの記法は、平成時代のfunctionを使った定義方式と、令和時代のECMA2015(ES6)における定義方式の2通り用意しました。
実行時には()を付与することになっていますので、他人のソースコードを読んでいる時にもメソッドであることがすぐにわかりますね。

連想配列

連想配列は、オブジェクト型データを別の記法で表現したもので、データそのものは全く同一です。

連想配列の記述方式

  let person = {}; // 空のオブジェクト型データの生成
  person["age"] = 20; // person.age = 20; と全く同じ

連想配列の記法は、ブラケットの中にkeyとなる文字列を指定するだけです。

連想配列記法を使う利点

ところが、連想配列の記法を使うと、より柔軟な操作をオブジェクト型データに実行することができます。

  let str1 = "weight"
  person[str1] = 78; // person.weight = 78; と同じ

オブジェクトのkeyに、文字列格納済みの変数を設定することができています。

この性質を利用すると、例えば以下のように、for文でオブジェクトのkeyを一気に追加することも可能です

  for(let i=0; i<3; i++){
    let keyStr = `others0${i+1}` ;
    person[keyStr] = "未設定";
  }

このようなコードにより、以下のような結果が得られます

  person.others01; // 未設定
  person.others02; // 未設定
  person.others03; // 未設定

for文により、オブジェクトのkeyを一気に3つ自動追加できたことになります。

その他、DBから読み込んだidをkeyに設定して、idに紐づくデータを値として持たせる、といったプログラミングも可能となります。
実際の開発現場では、そのようなコードを見る機会もあると思います。

オブジェクト型データの基本的な記述方式であるドット演算子を使ったやり方では不可能な操作が、ブラケット[]を使った連想配列記法では可能になるんですね。

連想配列のネーミングの由来

ところで、「連想配列」ってなぜこのようなネーミングになったのか考えてみました。
まず、ブラケット[]を使っている時点で記法は配列ですね。配列と比較すると、index指定が違いますね。

  let arrayData = []; // 配列型の定義
  arrayData[0] = "こんにちは";
  let objData = {}; // オブジェクト型の定義
  objData["aisatsu"] = "こんにちは";

配列は0から始まる番号で番振りされますが、オブジェクト型は何か意味のある値をkeyにしますね。
「aisatsu」という文字列がkeyなので、だいたい値が何か連想できる。それで「連想配列」なのかなと思います。

JavaScriptのオブジェクトとオブジェクト型データ

ところで、JavaScriptにはオブジェクトという概念があって、非常に幅の広い扱い方をされています。
例えばこちらの記事では、そういった「広義のオブジェクト」についての解説があります。
[JavaScript] オブジェクトの基礎

組み込みオブジェクトとメソッド

特に重要なポイントとして、「組み込みオブジェクト」と「実装済みメソッド」を解説します。
オブジェクト型だと呼び方が紛らわしいので、配列型で見てみましょう。

  let arrayData = ["パン", ""]; // 初期化データを2つ持った配列型データの定義
  arrayData.push("宿直"); // pushメソッドで要素を1つ追加

このような基本的な配列型データを用意しているコードを見てみます。
ところで、.push()の記法ってオブジェクト型データ特有のものですよね。
しかも、.push()は配列型以外のデータで使おうとすると、エラーになります。

実は、配列型データはArrayクラスというオブジェクトから生成されたインスタンスであり、このArrayクラスのようなJavaScript標準で用意されたクラスのことを「組み込みオブジェクト」と呼びます。
Array - MDN web docs

JavaScript の Array クラスはグローバルオブジェクトで、高水準、リスト風のオブジェクトである配列の構築に使用されます。

そして、Arrayクラスは標準でpush()などのArrayクラス用メソッドが実装されています。
Array.prototype.push()

Javaに詳しい方はすぐにピンとくる話ですね。
Javaは、クラスを用意して、クラスからインスタンスを生成して、インスタンスからドット演算子により実装済みメソッドを実行できます。
JavaScriptでも、ほぼ同じような概念で組み込みオブジェクトの配列クラスを扱うことができます。
JavaScriptの場合は、インスタンス化作業は単なるクラスコードのコピー(clone)をprototypeというプロパティに格納して、そこからクラスに定義されたメソッドが実行できるという仕組みです。

なお、正式な配列型データの記法は、Arrayクラスからnewして生成します。

  let arrayData1 = [] ; // 実はシンタックスシュガー構文
  let arrayData2 = new Array() ; // 全く同じ配列型の初期化データを生成できる

JavaScript専門の方は、配列などの組み込みオブジェクトがあって、便利メソッドが実装済みで、それを使えばいいんだという理解でOKだと思います。
オブジェクトってイメージが大切なので、「なんか便利な配列さん」がいて、いろいろサービスを提供してくれる・・・みたいな理解で通用してしまいます。

この章はだいぶ話が難しくなってしまいましたが、広義のオブジェクトとオブジェクト型データの言葉の違いを理解していただければOKです。
また、組み込みオブジェクトって、だいたいオブジェクト型データみたいな扱いでメソッドを使えるんだなと思っていただけると、コードの速読スキルが上がるし、コードを書くスピードも上がると思います。

オブジェクト型も組み込みオブジェクト

冒頭から解説しているオブジェクト型データの正式な記法は、Objectクラスからnewして生成します。

  let objData1 = new Object(); // 正式な記法でクラスからインスタンス生成して定義
  let objData2 = {}; // 実はシンタックスシュガー構文

要するに、本記事で解説しているオブジェクト型データとは、Objectクラスのコンストラクタを実行してnewして生成したインスタンスということになります。
Objectクラスのインスタンスですね。

配列の場合は、Arrayクラスのインスタンスです。インスタンスの概念が良くわからない場合、メルヘン世界で擬人化された生きた存在だと思っていただけると、けっこう通用します。これに対し、クラスは「設計図」だと言われています。インスタンスは、設計図から3Dプリンタで生成された実体で、しかも生きている感じなわけですね。

オブジェクト型データとJSON

JSONとは

JSONとは、JavaScript Object Notation の略で、直訳すると「JavaScriptオブジェクト記法」ということになります。

JSONとして扱えるコードは以下のようなものです

  { "name" : "ICHIRO", "age" : 20, "isSuccess" : true } // オブジェクト型データ
  [ 1, 3, 5 ] // 配列型データ
  { "skills" : ["JavaScript", "HTML", "CSS" ] } // オブジェクト型と配列型の組み合わせ

要するに、JSONとはオブジェクト型、配列型の組み合わせを基本としたデータ形式です。

JavaScriptの記法でデータを表現すると、CSVデータの表現も簡単だし、多種多様な組み合わせでデータを表現できる。しかも、JSプログラム互換で便利ということで、今やJSONはAPIデータ通信のフォーマットのデファクトスタンダートとなっています。

JSONとJavaScriptオブジェクト

JSONとは、JavaScript言語の実行環境以外でも使える、共通の文字列データ仕様です。
データ構造は複雑ですが、JavaScriptでプログラミングしている分には簡単に構築することができます。

  // JSONデータオブジェクトの生成例
  let engineer = { name : "kenshirou" };
  let languages = ["JavaScript", "HTML", "CSS"];
  engineer.languages = languages

このような形で、オブジェクト型や配列型を別々に定義してから、後から一つに統合してJSON形式データが簡単に構築できるのですが、そのまま上記の「engineer」変数をAPIの引数に渡してもエラーになってしまいます。

JavaScript以外の言語や仕組みにも対応可能なJSONデータを生成するには、以下の処理が必要です

  let json_engineer = JSON.stringify( engineer ); // シリアライズ処理を実行

これはJSON.stringfy()メソッドによるシリアライズ処理と呼ばれます。
どういうことかと言いますと、このような違いがあります

  engineer.name // kenshirou
  json_engineer.name // エラー。

engineerはオブジェクト型データなので、ドット演算子でプロパティにアクセスできます。
ところが、json_engineerはただのString文字列なので、.nameのような記法は使用不可能なんですね。

まとめ

JavaScriptにおけるオブジェクト型データの操作方法について、開発現場でよく使うポイントに要点を絞って解説しました。
以下の内容も追記予定です
・オブジェクト型データの入れ子構造
・フォームデータをオブジェクト型データで扱う
・for...in文でループ処理を実施

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

function(e)とは何なのか?

事前知識

イベント

Javascriptにおけるイベントとは、Webページ上で起こるあらゆるアクションの総称。例えば、ボタンのクリック、フォームへの入力、ページの読み込みなど、すべて「イベント」と呼ぶ。

イベントハンドラー

ハンドラとは、英語でhandlerであり、動詞でいうhandle(扱う)に由来している。日本語にすると、「イベントを扱う」的な感じ。
実際、イベントハンドラーは様々なイベント(アクション全般)の発生時、それに紐づかせた処理を実行させることができる。
具体的に言うと、イベントハンドラーを登録することで、「あるイベントA(クリックなど)が発生したとき、ある処理B(ページの色を変更させるなど)を実行させる」という自動的な流れを作ることができる。それがイベントハンドラー。

イベントリスナー

上で書いたイベントハンドラーとほとんど同じだが、1つだけ大きく異なる部分がある。それは、「イベントハンドラーは一つのイベントに対して、1つの処理しか設定できない」ということ。しかし、「イベントリスナーは一つのイベントに対して、複数の処理を設定できる」ということ。(イベントハンドラーに複数の処理を登録しようとすると、後の処理が優先され前の処理は上書きされる)

コールバック関数

そもそもコールバックとは、簡単に言うと、関数の引数に別の関数をセットすること。
つまり、コールバック関数とは、そのセットされた関数を指す。
これによって何のメリットがるのかというと、例えばAという関数があり、その関数の引数にBという関数をセットしていたとする。すると、「関数Aの処理が終わった後に、関数Bの処理を実行させる」ということが可能になる。
よく使われるケースは、サーバーからデータを取得したあとに何らかの処理を実行するときなど。サーバーからのデータの取得を待たずに次の関数が実行されてしまうと不具合が生じる。

本題 function(e)とは?

eventの略。イベントオブジェクトのこと。イベントオブジェクトとは、発生したイベントに関する様々な情報を提供するオブジェクトのこと。これはそのイベントと紐づくイベントハンドラーやイベントリスナーで設定されたコールバック関数の引数として自動的に渡される。加えて引数として渡されるこのイベントオブジェクトは省略可能なので注意。

イベントオブジェクトにセットされている主なプロパティ一覧

プロパティ 内容
type 発生したイベントの型を返す
target イベントの発生元であるオブジェクトを返す
timestamp イベントの発生時間
pageX クリックされたX座標

etc...

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

JavaScriptにおけるfunction(e)とは何なのか?

事前知識

イベント

Javascriptにおけるイベントとは、Webページ上で起こるあらゆるアクションの総称。例えば、ボタンのクリック、フォームへの入力、ページの読み込みなど、すべて「イベント」と呼ぶ。

イベントハンドラー

ハンドラとは、英語でhandlerであり、動詞でいうhandle(扱う)に由来している。日本語にすると、「イベントを扱う」的な感じ。
実際、イベントハンドラーは様々なイベント(アクション全般)の発生時、それに紐づかせた処理を実行させることができる。
具体的に言うと、イベントハンドラーを登録することで、「あるイベントA(クリックなど)が発生したとき、ある処理B(ページの色を変更させるなど)を実行させる」という自動的な流れを作ることができる。それがイベントハンドラー。

イベントリスナー

上で書いたイベントハンドラーとほとんど同じだが、1つだけ大きく異なる部分がある。それは、「イベントハンドラーは一つのイベントに対して、1つの処理しか設定できない」ということ。しかし、「イベントリスナーは一つのイベントに対して、複数の処理を設定できる」ということ。(イベントハンドラーに複数の処理を登録しようとすると、後の処理が優先され前の処理は上書きされる)

コールバック関数

そもそもコールバックとは、簡単に言うと、関数の引数に別の関数をセットすること。
つまり、コールバック関数とは、そのセットされた関数を指す。
これによって何のメリットがるのかというと、例えばAという関数があり、その関数の引数にBという関数をセットしていたとする。すると、「関数Aの処理が終わった後に、関数Bの処理を実行させる」ということが可能になる。
よく使われるケースは、サーバーからデータを取得したあとに何らかの処理を実行するときなど。サーバーからのデータの取得を待たずに次の関数が実行されてしまうと不具合が生じる。

本題 function(e)とは?

eventの略。イベントオブジェクトのこと。イベントオブジェクトとは、発生したイベントに関する様々な情報を提供するオブジェクトのこと。これはそのイベントと紐づくイベントハンドラーやイベントリスナーで設定されたコールバック関数の引数として自動的に渡される。加えて引数として渡されるこのイベントオブジェクトは省略可能なので注意。

イベントオブジェクトにセットされている主なプロパティ一覧

プロパティ 内容
type 発生したイベントの型を返す
target イベントの発生元であるオブジェクトを返す
timestamp イベントの発生時間
pageX クリックされたX座標

etc...

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

Azure Static Web AppsのAPI作成でLINE BOTを作る

前に書いた記事でAzure Static Web Appsを簡単に試してみましたが、後半の手順でAPIの作成も出来ました。

参考: たぶん10分で試せる。Azure Static Web AppsにWebサイトをデプロイして独自ドメイン設定とFunctionsでAPI公開まで

今回はこれを使ってLINE BOTを作ってみようと思います。

ちなみにStatic Web AppsのAPIの中身はAzure Functionsになる模様です。

参考: Azure Functions による Azure Static Web Apps プレビューでの API のサポート

環境

  • macOS Catalina
  • Node.js v12系

現状だとローカル実行やAzure FunctionsがNode.js v12までしか対応してなさそうでした。

APIエンドポイントを作ってみる(おさらい)

こんな手順です。

  • まずは参考記事をもとに、Azure Static Web Appで静的サイトを作成してみる
  • apiフォルダを作成し、さらにlinebotフォルダを作成
  • linebotフォルダ内にindex.jsfunction.jsonを作成
index.js
module.exports = async function (context, req) {
    context.res = {
       body: { 
         text: "Hello from the API" 
       }
    };
  };
function.json
{
    "bindings": [
      {
        "authLevel": "anonymous",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
          "get"
        ],
        "route": "linebot"
      },
      {
        "type": "http",
        "direction": "out",
        "name": "res"
      }
    ]
  }

この状態でプッシュしてしばらくするとAPIが有効になります。

https://<APP ID>.azurestaticapps.net/api/linebotにアクセスするとAPIが見えます。

ローカル実行まで

まずはローカルで実行できる環境を整えます。

Azure Functionsの拡張機能のインストールと設定

VScodeにAzure Functionsの拡張機能をインストールします。

インストールが完了するとAzureマークがVSCodeのサイドバーに表示されます。選択するとサインインしましょう的なボタンがあるのでサインインします。

サインインすると少し読み込み待ち

Create New Project...のボタンを選択し、Browseを選択。

作成したapiフォルダを選択

言語選択でJavaScriptを選択

スクリーンショット 2020-08-17 1.27.33.png

テンプレート選択でHTTP triggerを選択

スクリーンショット 2020-08-17 1.27.28.png

関数名をwebhookとし、承認レベルをanonymousにします。

こんな感じでapiフォルダ内にwebhookフォルダやその他ファイルが作成されます。

Azure Functions拡張機能のタブに戻るとこのような表示になっています。

core toolsでローカル実行

Azure Functions Core Toolsというツールを使ってローカル実行をする模様です。

VScodeのコマンドパレットを開き(macだとcommand+shift+p)、core toolsと入力しインストールします。

スクリーンショット 2020-08-17 1.43.35.png

バージョン選択ですが、レコメンドされてるのでAzure Functions v3を選択します。

スクリーンショット 2020-08-17 1.43.44.png

しばらくするとインストールが完了します。

VSCodeのメニューのRun > Start Debuggingを選択するとローカルサーバーが起動します。

スクリーンショット 2020-08-17 1.55.28.png

ちなみに、このときにNode.js v14を利用してたらエラーが出ました。執筆時点ではv12までの対応らしいのでv12に切り替えたらうまく行きました。

ターミナルではこんな感じで作成したwebhooklinebotのエンドポイントが有効になったような表示になります。

ブラウザでアクセスしてみるとちゃんと表示されました。

LINE BOTプログラムの作成

現状だとルートディレクトリにapiフォルダindex.htmlがありますが、apiフォルダ内にpackage.jsonがあるのでそこでnpm installを実行していきます。

  • ルートからapiフォルダへ
$ ls
api        index.html
$ cd api
$ ls
host.json           local.settings.json package.json        webhook
linebot             package-lock.json   proxies.json
  • apiフォルダでモジュールのインストール
npm i @line/bot-sdk express azure-function-express

通常だとAzure Functionsのお作法で書く必要がありますが、azure-function-expressを利用することで、Azure Functionsっぽい書き方に依存せずに通常のexpressの利用っぽい雰囲気で書くことが出来ます。ひらりんさんの記事が参考になりました!

参考: LINE Bot を Azure Functions (Node.js) で作る際のオウム返しテンプレ

  • api/webhook/index.jsにコードを記述

'use strict';

const line = require('@line/bot-sdk');
const createHandler = require("azure-function-express").createHandler;
const express = require('express');

const config = {
    channelSecret: '作成したBOTのチャンネルシークレット',
    channelAccessToken: '作成したBOTのチャンネルアクセストークン'
};

const app = express();

app.get('/api/webhook', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/api/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return;
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text //実際に返信の言葉を入れる箇所
  });
}

module.exports = createHandler(app);

大元はこちらの1時間でLINE BOTを作るハンズオンの記事のコードになっています。

参考記事をもとにLINE BOTのチャンネルシークレットとアクセストークンもコードに記載しておきます。

参考: 1時間でLINE BOTを作るハンズオン

おうむ返し

ngrokでトンネリングして試してみましょう。

コードが出来たら先ほどと同様にRun > Start Debuggingでローカルサーバーを起動させます。

今回(デフォ?)7071ポートで起動したのでngrokで7071ポートにトンネリングさせます。

$ npx ngrok http 7071

これは別のターミナルで実行しておいた方が良いと思います。

こんな感じでhttps://35027c542caa.ngrok.ioというURLが発行されました。

このURLを使ってLINE DevelopersでWebhook URLの設定を行います。

今回はエンドポイントが/api/webhookになっているので、この場合はhttps://35027c542caa.ngrok.io/api/webhookとなります。

この状態(ローカルサーバー起動中 + ngrok起動中 + Webhook URL登録済)でLINE BOTに話しかけると無事におうむ返しをしてくれました。

ひとまずローカルで実行できて一安心。。

デプロイして永続化する

デプロイは簡単で、もともとGitHub連携ありきでAzure Static Web Appsを作っているのでローカルで作ったものをプッシュすればOKです。

裏側でGitHub Actionsでビルドされる模様なので少し待ちましょう。

しばらくするとポータルの画面にもwebhook関数が表示されます。

https://<APP ID>.azurestaticapps.net/api/webhookにブラウザでアクセスするとHello LINE BOT!の表示が見えるようになると思います。

最終的にこのアドレスをLINE BOTの管理画面からWebhook URLに登録します。

これでデプロイも完了し永続化まで出来ました。

念のため最後におうむ返しがちゃんと動くか確認しましょう。

おまけ 環境変数の利用

ソースコードのチャンネルシークレットチャンネルアクセストークンをコードに直書きしてましたが環境変数置き換えも出来ます。

//省略

const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
};

//省略

ポータルの構成の箇所から環境変数の追加ができます。

パスワードやトークンはここを活用すると良さそうです。

最後にチェックをして保存を押すことで保存されるのですが、これを忘れると値が保存されないので注意です。

まとめと所感

Static Web AppsでLINE BOTを作成することが出来ました。

当たり前ですがMSエコシステムということでVSCodeを使わないと厳しいかもしれないですね。
この辺は趣味趣向ありそうです。

内部はAzure Functionsを使ってるっぽいですが、Azure Functionsのリソースは一個も作っていないので金額とかは特に発生せずに使えてるのかな......

Static Web Appsの管理画面がシンプルすぎて稼働状況もいまいちわからないですが、とりあえずしばらく稼働させてみます。

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

3秒で理解する async/await関数 の並列実行

async/awaitによる非同期処理を、いくつかまとめて並列実行したい場合のやり方です。
多くの資料が、返り値を利用しない場合のやり方しかまとめられておらず、
返り値を利用したい場合にどうすればいいかわからなかったので、調べてまとめました。

  1. 関数の返り値を利用したい場合
async function 関数() {

  //実行するときにはawaitをつけず
  const 変数A = 非同期関数A();
  const 変数B = 非同期関数B();

  //利用するときにawaitをつける。
  console.log([
    await 変数A,
    await 変数B,
  ]);

}
  1. 関数の返り値を利用しない場合
async function 関数() {

  //await Promise.All()の引数(配列)に関数をいれて実行することで、並列実行してくれる。
  await Promise.All([
    非同期関数A(),
    非同期関数B(),
  ]);

}

P.S.
間違っていたら教えてくださいm(_ _)m

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