アロー関数の間違った使い方

JSに関する記事をいろいろ見ていたら、アロー関数の理解が違っていることに気づいた。

efcl.info www.tsubasa-note.blog

「ES6ではfunctionをアロー関数で置き換えられるんだね!」という程度の理解だったが、どうもそうではないらしい。
上の記事とかぶってしまうが、忘れないように書いておく。

thisの扱いの違い

まず、アロー関数ではthisの解釈が他の関数宣言とは異なる。
通常の関数宣言では、thisが何を指すのかはコンパイル時ではなく実行時に決まる。
オブジェクト内でメソッドを定義してすぐ呼び出せば、スコープチェーンをたどり、thisはそのメソッドを定義したオブジェクトを指す。
一方で、一度メソッドとして定義した後、別の変数に代入してただの関数として呼び出すと、thisがundefinedになってしまうことがよくある。

これに対して、アロー関数でのthisはコンパイル時に決まるため、どこで呼び出しても常に「アロー関数を定義したコンテキストのthisオブジェクト」を参照するようになる。

アロー関数がうまく使えない場合

例えば、以下の簡単なコードを例にする。

const obj = {
    num: 3,
    sayNum: function() {
        return this.num;
    }
}
console.log(obj.sayNum); // 3

このコードでは、sayNumメソッドはobj.sayNumとして呼び出されているので、thisはobjを指す。そのため、this.numは3になる。

これをアロー関数で書き換えようと思って、やってしまったミスがこちらのコード。

const obj = {
    num: 3,
    sayNum: () => {
        return this.num;
    }
}
console.log(obj.sayNum); // undefined

アロー関数はどう呼び出されたかに関係なく、自身が定義されたコンテキストのthisオブジェクトを参照する。
上のコードは以下と同じと考えるとわかりやすい。

const obj = {}
obj.num = 3,
obj.sayNum = () => {
    return this.num;
}
console.log(obj.sayNum); // undefined

thisはグローバルオブジェクトとなってしまうため、当然obj.sayNumはundefinedとなる。

アロー関数はコールバック関数として使う

よく仕様を読むと、こう書いてあった。

アロー関数は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。

アロー関数の一番の使い所は、メソッドの中でコールバック関数を書くとき。
理解のために、上のコードを強引に書き換えると以下のようになる。

const obj = {
    num: 3,
    sayNum: function() {
     message = () => {
        return this.num;
     }
     return message();
  }
}
console.log(obj.sayNum()); // 3

こう書くとアロー関数はsayNumメソッドの中で定義されているので、thisはsayNumメソッドを指すようになる。

もちろんこの程度のコードであればアロー関数を使う必要は全くない。アロー関数は、メソッドの中でsetTimeoutのような処理を入れたいときなどによく使われている。

JavaScriptの関数定義の方法まとめ

今まではPythonをメインに書いてきて、JavaScriptはそれっぽくすませてきただけなんだけど、
仕事で真正面から触れることになり、逃げるわけにいかなくなった。
関数定義でいろんなやり方がありすぎて混乱してきたので、一度まとめてみる。

function命令

function sum(a, b) {
     return a + b;
}
console.log(sum(3, 5)); // 8

コンパイル時に評価される。そのため、関数定義より前の行でその関数を使ってもエラーにならない。

functionコンストラクタ

var multiply = new Function('a', 'b', 'return a * b;');
console.log(multiply(3, 5)); // 15

Functionと大文字で書くことに注意。
実行時に評価されるので、関数を使う前に関数定義を書かなければいけない。
関数内部でグローバルスコープを参照するため使いにくい。そのためか、あまり使われない。

即時関数

(function(){
    console.log("executed");
}()) // executed

その場で関数が実行される。
この書き方は、ES2015以前で、関数内部でブロックスコープを実現させるためのテクニック。
ES2015以降はletが使えるようになったため、即時関数を使うことは推奨されない。

関数リテラル(無名関数)

var devide = function(a, b) {
    return a / b;
}
console.log(devide(15, 5)); // 3

実行時に評価されるので、関数を使う前に関数定義が必要。
関数の中はローカルスコープとなる。

アロー関数

var devide_arrow = (a, b) => a / b;
console.log(devide_arrow(15, 5));

ES2015からは、関数リテラルをこのように書き直せる。
関数内部の処理が一文だけなら、中括弧とreturnを省略できる。

Google Homeで音声アプリを作るときの3つのポイント

supporterzcolab.com

このイベントで初めてのLTをしてきました。
前の人のテーマもGoogle Homeというまさかの事態が起きましたが、スベることなく無事に終わりました。

会社で買ったGoogle Homeを使い、Dialogflowと組み合わせて会議室予約システムを作ったときの話です。 5分という短い時間用に作ったスライドなので、3つのポイントについて内容を少し補足しておきます。

本当に音声がベストか?

最新のテクノロジーは、やはり過剰な期待がつきもののようです。
今回のGoogle Homeは会社で買ったのですが、何を作ってほしい?といろいろな人に聞いたところ、
- 資料を印刷してほしい
- 退社時刻を知らせてほしい
- 電話に出てほしい
など、絶対自分でやった方が早いものばかり出てきました。

VUI(音声によるUI)が効果的なシーンは、音楽とIoT以外ではまだほとんど確立していません。イベントでGoogle Homeを持っている方何人かと話しましたが、どの方もいつGoogle Homeを使うとぴったりはまるのか、悩んでいる様子でした。
今回の会議室予約アプリは、「会議室で急な打ち合わせをしたいが、今空いている部屋は空室なのか、単にまだ使う人が来ていないだけなのかがわからず、その場でノートPCを開いて確認するのも面倒くさい」という実体験をもとに作ったものです。(これが他の人にもあてはまるかは、しばらく様子を見ないとわかりませんが)
他の人に使ってもらうアプリを作るときは、本当にスマートスピーカーでやるべきかをしっかり考えてからの方がよいです。

フローをシンプルに

スマートスピーカーは「話しかけるだけで使える」という簡単さが売りなのに、そのフローが複雑だと利用価値が減ってしまいます。
今回、非エンジニアの社員に使ってもらったのですが、基本的に覚えられるフローは一直線だけです。
もともとは、Google Homeを会議室の中に置き、その会議室が予定上空いているときとそうでないときでフローを分けていたのですが、2つのフローを覚えてもらえず、空いているときしか使われませんでした。
まだスマートスピーカー自体になじみのある人が少ないため、機能を多くするよりもフローを単純にすることを優先させる方がよいでしょう。

日本語の固有名詞は取扱注意

これは作ってみるまでわからなかったのですが、音声入力の場合、日本語は勝手に変換されます。
そのため、社員の名前を認識させたい場合、難しい漢字の場合は思うように認識されません。
対策としては、DialogflowのTraining機能を何度も使うことや、Googleの音声入力履歴を見ながらEntityにいろいろなパターンを登録していくという地道な作業が必要です。
ちなみに、英語の名前であれば、よくある名前がDialogflowのデフォルトのEntityとして用意されてたりします。いいなあ。

会議室予約アプリは今も鋭意改善中です。また進化したら書きます。
Amazon Echoも早く開けてセットアップしないと...

「若手エンジニアが転職で困らないように今から実践できること」に参加しました

supporterzcolab.com

紹介文の「会社にロックインされないために」というところに惹かれて参加しました。
自分自身、転職に興味があっても今すぐ転職はできないという感じなのですが、そういう人にこそ刺さるコンテンツでした。

ロックインされない自覚を持つ

今の自分、ロックインされてるんじゃない?という自覚はなんとなくあったのですが、まず「ロックイン」とは何かという定義から始まりました。
講師のmncさんによると、

  • プロダクトの要件定義
  • プロダクトに関する問題解決
  • 社内調整

こういう「社内に長くいるほど自然とやりやすくなる」ようなスキルしか持っていない状態が、ロックインされているということです。

それと反対のものが、いわゆる技術力。これは社内ではなく、市場で一般的に通用する考え方のことです。
企業に転職するのであれば、何かの分野について深い技術力を持つ、尖った人材の方が求められます。 社内の問題を考えるときも、社内の事情だけではなく、一般的な解を意識して考えなければ、気づかないうちにロックインされてしまいます。

アウトプット前提で仕事をする

転職をするとき、自分のブログや発表資料などは名刺代わりになるとはよく言われていますが、mncさんは面接で「ブログやってないの?」「githubは?」などと本当に聞かれたそうです。 そしてその時は何もアウトプットがなく、とても苦労したとのこと。

継続的なアウトプットをするには、「良い仕事をしたらアウトプットしよう」ではなく、何か仕事をしたら必ずアウトプットをするくらいの意識が必要です。
そうすれば、記事を作るために仕事に関連する技術的知識も自然と調べるようになります。 家に帰るとだらけちゃう、という人は、帰宅してからではなくオフィスでブログを書くのがおすすめだそうです。

mncさんのブログはこちら。

manchose.hatenablog.jp

コミュニケーションから逃げない

そして今回の最大にして最難関のポイントがこれ。
一人で勉強してるだけだと、どうしても業務レベルの知見までたどり着くのが難しいです。自分のやりたい分野の仕事を会社が常に振ってくれるわけでもないですし。
なので、人の力を使って機会をつかむのが重要になります。

mncさんは、副業をやりたくても自力では見つけられないという状況から抜け出すために、エンジニアの知り合いや勉強会で出会った人に「サービス作りたいんです!人手が必要になったら声かけてください!」とひたすら話しかけまくり、案件をゲットされたそうです。こういうの、自分がいちばん苦手なやつだ...

「恐れない」の一言に尽きる

最終的にはこういうことなのかなと思いました。
エンジニアになって日が浅いし、実績もないのですが、だからといって言われた仕事をこなすだけでいいわけないですね。
まずは社外にいる知り合いのエンジニアに連絡を取ってみようと思います。それこそ「久しぶりだから話聞きたい!」みたいな感じで。

【Vue.js】computedとmethodsの違い

会社のソフトウェアがVue.jsによってリファクタリングされることになり、急に身につけなければいけないことになった。
まさか自分がVue.jsをやるなんて、先週までは考えもしてなかったんだが...

今公式ドキュメントを読み始めたところだけど、computedとmethodsの違いがよくわからなかったので、そこだけしっかり調べてみた。

computedはプロパティ

ドキュメントでcomputedの説明を見てみる。

Vue インスタンスに組み込まれる算出プロパティ (Computed property) です。

ん、プロパティ?
ここがmethodsとの違いその1。computedとmethodsって何が違うの?という問いには、「プロパティとメソッドは違うだろ」という答えを返せる。

試しに、computedとmethodsの部分だけを変えた、同じ処理を書いてみた。

computed

<div class="use-computed">
    <p><input v-model="number" type="number"></p>
    <p>Even / Odd : {{ answer }}</p>
</div>
new Vue({
  el: '.use-computed',
  data: {
    number: '0'
  },
  computed: {
    answer: function() {
        if (this.number % 2 != 0) return 'Odd';
      return 'Even';
    }
  }
});

methods

<div class="use-methods">
    <p><input v-model="number" type="number"></p>
    <p>Even / Odd : {{ answer() }}</p>
</div>
new Vue({
  el: '.use-methods',
  data: {
    number: '0'
  },
  methods: {
    answer: function() {
        if (this.number % 2 != 0) return 'Odd';
      return 'Even';
    }
  }
});

HTMLのmustache構文に注目。同じanswerでも、computedの方はプロパティなので()がない。対してmethodsの方はメソッドなのでanswer()と書く。
またcomputedはプロパティなので、getterだけでなくsetterも定義できる。

computedはキャッシュされる

2つめの違いとして、computedは依存関係にもとづきキャッシュされる。
上の例では、入力した整数をもとに偶数か奇数かを判定する処理をしているが、仮にページを更新しても前の入力値が変わらなければ、answerが呼び出されてもfunction()以下の処理は実行されず、前に算出したプロパティの値がそのまま出力される。
もちろん入力値が変われば再び処理が走るが、そうでなければ何回呼び出されても計算が発生しないので、不要に処理が遅くなることがない。
これに対して、methodsはメソッドなので、入力値が変わらなくても、呼び出されるたびに毎回処理が走る。

methodsは計算量を増やしてしまうことが多いが、あえてmethodsを使う場合もある。
ドキュメントにもあるが、例えばDateオブジェクトを使って今の日時を返したい場合、computedを使うと日時の値がキャッシュされ、いつアクセスしても同じ日時になってしまう。
このような場合はmethodsにしなければいけない。

【Python】親クラスのプロパティ・メソッドを子クラスで使いたいときの書き方

書くたびに調べることになるので、忘れないようにメモ。

親クラスと同じメソッドを子クラスでそのまま使うとき

superなどで指定しなくても普通に使える。

class Animal:
    def __init__(self, legs):
        self.legs=legs
    def legnum(self):
        return "I have " + str(self.legs) + " legs!"
 
class Dog(Animal):
    pass

John = Dog(4)
John.legs # 4
John.legnum() # "I have 4 legs!"

Pythonでは、指定したメソッドがクラス内で見つからない場合、親クラスまでたどって探してくれる。

親クラスのメソッドを使って別のメソッドを定義するとき

superが必要。でないとローカルスコープの変数とみなされ、NameErrorになる。

class Cat(Animal):
    def greet(self):
        return "Hello " + super().legnum()

Tom = Cat(4)
Tom.greet() # "Hello I have 4 legs!"

親クラスのプロパティと子クラス独自のプロパティを併用したいとき

今までの例は、子クラスで独自のコンストラクタを何も書いていなかったが、
子クラスでも独自にコンストラクタを立てると、親クラスのコンストラクタが完全に上書きされ潰されてしまう。 そうならないよう、superを使う必要がある。

class Mouse(Animal):
    def __init__(self, legs, ears):
        super().__init__(legs)
        self.ears = ears
    def greet(self):
        return "Hello " + super().legnum() + " And " + str(self.ears) + " ears!"  

Jerry = Mouse(4, 2)
Jerry.legs # 4
Jerry.greet() # "Hello I have 4 legs! And 2 ears!"

【MySQL】カラムがNULL値を取りうるときの検索条件の書き方

最近MySQLを扱う業務をやってみたら、がぜん興味が出てきて、この機会にちゃんと勉強しようと思い立った。
今回は業務で少しはまった、空文字を絞込でうまくはじく書き方について。

空文字をはじきたい

例えば、コンバージョンポイントと会員IDというカラムがあって、両方CHARやVARCHAR型を取る。そして会員IDはnull値も取りうるとする。
このとき、会員IDがある行だけを絞込みたくて、こんな感じのクエリを書いてしまった。

SELECT cv_point, user_id FROM table WHERE user_id IS NOT NULL;

これだとnull値しか弾かれず、空文字は弾かれない。null値と空文字は違う。
正しくはこう。

SELECT cv_point, user_id FROM table WHERE user_id != '';

これで空文字が弾かれる。ちなみにnull値が許可されているカラムでも、この書き方でnull値も弾くことができる。

特定の値以外に絞り込みたい

例えば、会員IDがaaaでない行に絞りたいとき。

SELECT cv_point, user_id FROM table WHERE user_id != 'aaa';

これで良いときが大半だが、もし会員IDのカラムがnull値を許可している場合、これだとnull値が入ってこない。
そのときはこう書く。

SELECT cv_point, user_id FROM table WHERE user_id != 'aaa' OR user_id IS NULL;

初めてのテーブルでは、null値を許可しているかなどをチェックしてから触るようにしよう。