カテゴリー内の他の記事

JavaScriptの便利な書き方

フォローする

Index

はじめに

基本的に、Developer Networkで公開されているTipsなどではInternet Explorerでもそのまま動作するよう、ECMA Script5というバージョンでJavaScriptが書かれています。

新しいバージョンのECMA Script 2017などは、それよりももっと便利で使いやすくなっているので、今回いくつか新しい記法とkintoneのJavaScriptカスタマイズでの使い所を紹介します。

※ 一部 Internet Explorerでは動作しない記法について、【IE非対応】の表記をしています。
IE で使用する場合は webpack入門 ~Babel,Polyfillを使って快適ES6ライフ~ のように Webpack や Polyfill を使用する必要があります。

変数宣言をconst / letにして安全に初期化する

var abc = 123; など今まで変数宣言は var を使っていますが、constとletを使っていくことをおすすめします。

  • varは関数スコープ、const / let はブロックスコープ
    スコープとは変数の名前や関数などの参照できる範囲を決めるものです。スコープの中で定義された変数はスコープの内側でのみ参照でき、スコープの外側からは参照できません。
    varはスコープが広く、関数スコープまでです。const / let はブロックスコープといって、ifブロックなど {} の中の範囲で有効になります。
  • constは再代入/再宣言ができない
    constは再代入も再宣言もできません。この変数は代入し直さない、とわかっている場合はconstを使いましょう。
    長いコードになってくると、同じ変数名を間違って再度宣言したり、意図せず再利用してしまうこともありますが、積極的にconstを使うとそれを防げます。
    ※ ただし、Objectや配列の中身は書き換えることができますのでご注意ください。
  • letは再代入が可能(constと同様再宣言は不可)
    letはconst同様再宣言はできませんが、再代入はできます。値を途中で書き換えることがわかっている場合はletで変数を宣言しましょう。
    基本的にはconstを使い、明らかに再代入する場合はletを使う、という風に使い分けていくといいと思います。
     

for文の使用を必要最低限にする

例えばレコード配列のデータなどを繰り返し処理をするケースは多々ありますが、それをforでやると無限ループさせてしまったり、記述が長くなってしまったりしてしまいます。そのため、配列を扱うときはなるべく下記に紹介するforEach() / filter() / map() / reduce() を使ってみるようにしましょう。

forEach()

単純にすべてのレコードのデータを参照したい場合は forEach を使うのが適しています。

Array.prototype.forEach()

例)会社名一覧をconsole.logに表示

  • for で記述した場合
    弱点として、レコードの数の指定を間違えると無限ループに陥ってしまいますし、records[i]と書く必要がありiが何なのか常に意識せざるをえません。
  • forEach で記述した場合
    わざわざ、何行目かを意識せずとも、record 変数に records のデータが1つずつ入って繰り返し処理を行ってくれますので見通しもよく便利です。
    今まで for でやっていることは、大抵このように実装できるはずですので、まず forEach で実装できないか考えてみましょう。

filter()

filter を使えば条件に一致した配列を取得できます。
例えば、売上n円以上のデータだけほしい、ということを簡単にかけます。

Array.prototype.filter()

例)小計が10,000円以上のレコードのみを求める

filter を使えば条件に一致した配列を取得できます。
例えば、売上n円以上のデータだけほしい、ということを簡単にかけます。

  • for で記述した場合
  • filter で記述した場合
    if文を使わずとも任意のデータが取得できるので見た目もすっきりします。
    実際には、この程度の条件であればkintone APIを呼ぶときに指定すればいいですが、もうちょっと複雑な条件を指定したいときには重宝します。

map

map を使えば、配列全てに処理を行うことができます。(新しい配列が返却されます)
Array.prototype.map()

例)小計を求める(消費税込)

  • for で記述した場合
  • map で記述した場合
    これもタイプ数は大分減らせることが出来ます。
    records[i]... から始まらない分、短くてすみますし、バグを引き起こす可能性を低くすることができます。
    実際には、小計を出す程度だと自動計算フォームを使えばいいですが、それではできない複雑な計算をする場合に使えます。

reduce

reduce を使うとrecords配列の全ての合計値などを求めることができます。
reduceは上記に挙げたものと比べると少しむずかしく見えるかもしれませんが、慣れてしまえば簡単にデータの合計値などを求めることができます。
reduce は左から右に配列を処理しますが、逆の reduceRight もあります。
Array.prototype.reduce()

例)全ての小計の合計を求める

  • for で記述した場合
  • reduce で記述した場合
    タイプ数は殆ど変わりませんので違いがわかりにくいですが、やはりindex変数 i を使わない分バグの確率は減ります。
    forEach でも実装はできますが、こちらは results の初期化も不要ですし、reduce 内で処理が完結するのでまとまりがあります。
    例のように合計値を求めたり、records配列をloopしてデータを計算したいときは積極的につかっていきたい関数です。

Arrow関数を使ってシンプルに関数を記述する【IE非対応】

関数の宣言には下記の種類があります。

  • 今までの関数宣言(通常のfunction式)
    function someFunc() {
      // 処理内容
    };

    // もしくは

    var someFunc = function() {
    // 処理内容
    }
  • Arrow関数
    const someFunc = () => {
    // 処理内容
    };

ES6からは上記Arrow関数が使えるようになりました。 (=> この形が矢印にみえるのでArrow関数と呼ばれています。 )

Arrow関数のメリット

Arrow関数にはいくつかメリットがあります。

  1. 短くかける
    上記のように普通に関数宣言しているだけではほとんど差はありませんが、Callback関数など、関数を多様するときにかなりスマートにかけます。
    とあるレコード一覧のデータをArray.map()やfilter()を使って必要なものだけとりだし、それぞれ処理をするパターンをみていきましょう。

    Array.prototype.filter()関数で税込にするべきレコードか判別し、その後Array.prototype.map()で税込にした金額を配列で取得する場合を考えてみます。
    このように function がなくなっただけでもスッキリしますね。また、さらにルールがあって、
    引数が1つなら引数の括弧が省略できる、1行で結果を返せるならReturnを省略できるというルールがあり、それを適用すると下記のようにもっと短くかけます。
    // Arrow関数(カッコやReturn省略版)
    const includedTaxPriceArray = records.filter(record => record.taxType.value === '税込').map(record => record.price.value * 1.1);
    returnがいらない分、関数を作る関数(カリー化)を作りやすくなります。( ... spread opereatorなどは後述
  2. thisを固定できる
    kintoneのJSカスタマイズではDOMを操作するときにthisを参照するケースはあると思います。
    通常の関数宣言の場合「thisは何を指しているか?」を意識する必要がありましたが、arrow関数の場合thisが固定されるのでその混乱はなくなります。
    その代わりcurrentTargetなどをつかって押された要素自体を取得するなどができます。

Promiseの代わりにAsync/Awaitを使う【IE非対応】

複数のアプリのレコードからデータを取得したい場合などに、Promiseによる非同期処理をしなければいけませんが、
Promiseは.then()に次の処理を書く都合上、入れ子構造になり慣れていないとあまり直感的とは言えません。
下記あるアプリのレコードを4件取得して合計値を求めるサンプルで試したいと思います。

  • 例) 4件のデータを取得し合計値を求める(Promise)
    ※ レコード一括取得APIを使うほうが適切ですがPromiseのサンプルのためにあえて1件ずつ取得します。

そこで、Promiseをそのままつかうのではなく、Async/Awaitを用いることで下記の様に普通の関数を使うようにかけるようになりました。
どちらが正しい、とかではないのですが、複数のPromiseだったり複雑だったりするとAsync/Awaitの書き方のほうが見通しがよいかと思います。

  • 例) 4件のデータを取得し合計値を求める(Async/Await)

ただし、注意点としては、asyncをつけることでPromiseObjectが返却されてしまうので、Promiseに対応していないイベントハンドラでは即時実行関数で包むなりしてあげないとエラーがでてしまうはずです。

分割代入でスマートに変数を取り出す【IE非対応】

これも便利な機能です。通常、下記のようにkintone.events.on()の中でrecord変数を取り出すときはこうします。

  • kintone.events.on()の中でevent.recordからrecord変数を取り出す(通常) 分割代入を使うと下記のように2回「record」と書かなくて良くなります。
  • kintone.events.on()の中でevent.recordからrecord変数を取り出す(分割代入)
    このように、わざわざ「record」と2回書く手間が省けます。
    更に潜ってrecordのpriceとcustomerフィールドを取りたい場合はこのようにします。
    先にpriceとcustomerをとってしまうことも可能です。

必須な書き方ではありませんが値取り出すために数行かかってしまうと見通しが悪くなってしまうこともあるので、スマートに取りたい場合は有効です。

スプレッド構文を使って展開する【IE非対応】

スプレッド構文も使いこなせば便利です。スプレッド構文は3つのピリオド ... を使い、配列・関数の引数・オブジェクトを展開できます。

配列で使うスプレッド構文

  • kintoneのレコードの配列である、records配列を展開し結合する

結合はArray.prototype.concat()でもできますが、こちらのほうが直感的でしょう。
records1、records2でそれぞれ3つ要素があるとすれば、[...records1, ...records2] とすることで、

[records1-1, records1-2, records1-3, records2-1, records2-2, records2-3]

とやっているのとほぼ同義です。...で配列を一度展開していると思えばわかりやすいかと思います。

関数の引数で使うスプレッド構文

関数の引数に使えば可変数の引数に対応できます。

  • 複数の契約レコードの合計金額を集計する関数

オブジェクトで使うスプレッド構文

オブジェクトも配列のように展開がされますので、下記の用にオブジェクト同士の結合ができます。

これも、配列と同様にcontractRecordとcustomerRecordが一度展開されているイメージです。

副作用をなくして安全にコードを書く

プログラミングで、状態を変化させることを副作用と呼びますが、副作用を起こすコードはなるべく少ないほうがバグを回避できます。言葉だけではわかりにくいので、起こりうるバグを見てみましょう。

直感的には、上記のコードはコンソールに、 税込金額 (1100)と税抜金額(1000)を表示してくれそうです。しかし実際には下記のように表示されます。

mceclip0.png

なんと、両方とも税込金額(1100)になってしまっていますね。これは「もとの変数に対して副作用が起きている」状態となります。このように、副作用がいつ起こるかというのを意識していなければ、思わぬバグを生んでしまいます。

JavaScriptでは常に変数は値への参照なので起きてしまう現象です。これを回避するには、いくつか方法があります。

IE 以外のブラウザの場合

SpreadOperatorを駆使して下記のようにaddTax関数を書き換えることができます。新しいObjectを作り、返すようなイメージです。

IE の場合

Objectを先にコピー しておくという方法や、immerimmutable.jsなどのライブラリを使ってイミュータブル(不変) にデータを扱うという方法もあります。

jQueryを使わず素のJSでDOMを操作する

もちろん込み入ったDOM操作をしたい場合はjQueryは有用ですが、1つのDOMを操作したい場合はわざわざjQueryを読み込むのは冗長なこともあります。素のJSでもDOMを操作しやすくなっているので紹介します。

  • 要素を取得する
    document.querySelector() / querySelectorAll() を利用することで要素を取得できます。
    ※ document.getElementById() などもありますが、上記のようにclass/id関係なくquerySelectorのほうが柔軟に要素を指定できます。
  • 取得した要素のStyleを変更する
    要素.style.プロパティ = "値"; とすることで、要素に対してstyleを変更することができます。
    CSSで設定できるstyleを割り当てられます。
    // id: fooの要素の文字を、赤い太文字にする
    const element = document.querySelector('#foo');
    element.style.color = "red";
    element.style.fontWeight = "bold";
    もちろん、kintone.app.record.getFieldElementなどで取得した要素にも同様のことが可能です。
    mceclip0.png

TypeScriptを導入する

TypeScriptでkintoneカスタマイズ開発をしてみようの記事で紹介されていますが、kintoneのサブテーブルのRecord構造などは結構難しいので、
TypeScriptを使うと、オブジェクトのキーの指定や型の間違いなどをなくすことができます。
Visual Studio CodeなどのIDEを使えば補完しながらコードを書くこともできます。がっつりJavaScriptカスタマイズをしたい場合は導入したほうがよいです。

最後に

上記紹介したようにJavaScriptはよりプログラミングしやすくなるように進化していっています。
const/let、Spread構文やArrow関数などを組み合わせたり新しい機能を使うと、バグを減らし副作用のない堅牢なコードも書けるようになると思います。少しずつでもぜひ試してみてください。

記事に関するフィードバック

直接的に記事と関連がないご質問はcybozu developer コミュニティをご活用ください。

Avatar
中村

ご紹介ありがとうございます。

> ※ 一部 Internet Explorerでは動作しない記法をご紹介しています。
> IE で使用する場合は webpack入門 ~Babel,Polyfillを使って快適ES6ライフ~ のように Webpack や Polyfill を使用する必要があります。

とのことですが、「一部」とはどの部分なのか、
webpack入門 ~Babel,Polyfillを使って快適ES6ライフ~
などを参照しないとわからないので、
記事中に「Internet Explorerでは動作しない記法」の部分を注記していただけますと助かります。

よろしくお願いいたします。

Avatar
cybozu Development team

中村様

フィードバックをありがとうございます。

Internet Explorerで動作しない記法についてわかるよう、追記をいたしました。

ログインしてコメントを残してください。