JSのbind、及び`this`について理解を少し深める

2021/01/05 20:24


bindそのものについて

JSのbindの用法を知り、「どういう時に使われるのか」が漠然とイメージできるようになりたかったため、概要と使われ方を調べた。

とりあえず以下のサイトのデモコードを自分なりにいじってどういう挙動なのかは調べた。

Function.prototype.bind() - JavaScript | MDN

const module = {
  a: 50,
  b: 30,
  text: 'this is test',
  showText: function(){
    return this.text;
  },
};

const moduleShow  = module.showText;
console.log(moduleShow);

// moduleから抜き出した関数にmodule自体をbindする
// moduleShow内のthis = bindされたmoduleとなる
// そのため、this.text = module.text、になる
const boundGetX = moduleShow.bind(module);
console.log(boundGetX());


その他、bindの概要、callとの違いについては下記の記事を見てイメージは出来た。 JavaScript thisの内容を指定する(bindメソッド) | ITSakura

ただ、こいつがどういう目的で使われるかは分からなかった。*1

単純に、関数内のthis(thisがない場合はbind内の第一引数にnullを指定する)を特定のものに設定する(=undefinedにしない)場合にbindを使う、という使い方でいいのだろうか。

アロー関数内でのthisの扱われ方とbindの比較

ここまで調べて、そういえばJSの通常関数とアロー関数ではthisの指すものが異なる、という話しを思い出した。

【JavaScript】アロー関数式を学ぶついでにthisも復習する話 - Qiita

JavaScript: 通常の関数とアロー関数の違いは「書き方だけ」ではない。異なる性質が10個ほどある。 - Qiita


両者のthisに関する違いとしては、アロー関数はthisを束縛し、通常関数はthisを束縛しない(=関数呼び出し元(レシーバ)をthisとする)という点である。

下記のコードを例に比較する。

  • 通常関数
this.name = "globalName";

// 通常関数
function showName() {
  console.log(this.name);
}

let arrowFunc = {
  name: "john",
  func: showName,
};

arrowFunc.func();
=> john

// bindを使った場合
showName.bind(arrowFunc)();
=> john

arrowFunc.func()の返り値がjohnとなっており、これはarrowFunc.nameの値であることが分かる。 このことから、arrowFunc.func()、つまりはarrowFunc.showName()のshowName内部のthisはarrowFunc(レシーバ)を指していたことが分かる。

  • アロー関数
this.name = "globalName";

// アロー関数
const showName = () => {
  console.log(this.name);
};

let arrowFunc = {
  name: "john",
  func: showName,
};

arrowFunc.func();
=> globalName

arrowFunc.func()の返り値が「globalName」となっている。これは最初に定義したthis.name = "globalName"のnameの値が表示されていることが分かる。 ここで、アロー関数内のthisについてのリファレンスを見てみる。

アロー関数自身は this を持ちません。レキシカルスコープの this 値を使います。つまり、アロー関数内の this 値は通常の変数検索ルールに従います。このためスコープに this 値がない場合、その一つ外側のスコープで this 値を探します。

アロー関数 - JavaScript | MDN


アロー関数であるshowNameが定義された際、関数内のthisは更に外側のthisを見ていたことになる。 今回Node上で上記コードを実行したため、thisとして定義されていたのは{name: "globalName"}だったため、showName関数内のconsole.logで表示されたthis.nameは、コードの最初に定義したthis.nameだった、ということになる。


ちなみにトップレベル、グローバルスコープあたりはここを参考にした。

JavaScriptのトップレベルスコープは常にグローバルスコープではなかった - Qiita

まとめ

改めてbind、通常関数とアロー関数それぞれでのthisの使い方を確認した。 通常関数とアロー関数の違いについては他にも色々あるが、一旦知りたいと思っていたことを確認することができた。

*1:こういったサンプルを見て、「じゃあこういうものにも応用できるな」と考えつく能力もプログラマに必要不可欠な能力だよな、と最近感じている

需要が薄いMaterial-UIのvalidationの一例

2021/01/04 12:54


空文字を許容しない、というバリデーションを一部分だけ書きたい、しかしそのために本格的なバリデーションの機構を組むのはめんどくさくて死にそう、という時にその場しのぎで書いたもの。

<TextField
  label="名前"
  variant="outlined"
  margin="normal"
  required

  // input.valueに入力された文字が入っているものとして、空文字の場合は input.value はfalseを返す
  // 上記の場合に{ error: true }を返すことで <TextField error /> と書いた時と同じようになる
  // { error: false } の場合はerrorは指定されていない状態になる
  {...(!input.value ? { error: true } : { error: false })}
/>

Material-UIのTextFieldをtype=&quot;date&quot;にした際のdefaultValueに指定する日付のフォーマットについて備忘録

2021/01/01 14:50


material-uiのTextFiledというAPIの中で、type="date"のようにタイプを指定することでDate pickerのように使用することができる。

Date picker, Time picker React components - Material-UI

その際、デフォルトの日付をdefaultValueというオプションで設定できるが、この部分で少し詰まったので備忘録として残す。

ダメだった例

// 年、月、日の区切りがスラッシュになっているとdefaultValueとして認識されない
const today = dayjs().format('YYYY/MM/DD')

<TextField
  label="testDay"
  type="date"
  defaultValue={today}
  margin="normal"
/>

f:id:fujjima:20210101144435p:plain

良かった例

// 年、月、日の区切りがハイフンだときちんとdefaultValueとして指定できる
const today = dayjs().format('YYYY-MM-DD')

<TextField
  label="testDay"
  type="date"
  defaultValue={today}
  margin="normal"
/>

f:id:fujjima:20210101144333p:plain

詳細は調査中だが、同じようにdatapickerとして使用できるKeyboardDatePickerではformatにformat="MM/dd/yyyy"のように指定してもいける感じなので、どうしてこのような仕様にしているかはよく分からない。 しかも表示される時にはYYYY/MM/DD形式だし。スラッシュどこに行った。

https://material-ui.com/components/pickers/#material-ui-pickers