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 値を探します。
アロー関数であるshowName
が定義された際、関数内のthisは更に外側のthisを見ていたことになる。
今回Node上で上記コードを実行したため、thisとして定義されていたのは{name: "globalName"}
だったため、showName
関数内のconsole.logで表示されたthis.nameは、コードの最初に定義したthis.nameだった、ということになる。
ちなみにトップレベル、グローバルスコープあたりはここを参考にした。
JavaScriptのトップレベルスコープは常にグローバルスコープではなかった - Qiita
まとめ
改めてbind
、通常関数とアロー関数それぞれでのthisの使い方を確認した。
通常関数とアロー関数の違いについては他にも色々あるが、一旦知りたいと思っていたことを確認することができた。
需要が薄い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="date"にした際の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" />
良かった例
// 年、月、日の区切りがハイフンだときちんとdefaultValueとして指定できる const today = dayjs().format('YYYY-MM-DD') <TextField label="testDay" type="date" defaultValue={today} margin="normal" />
詳細は調査中だが、同じようにdatapickerとして使用できるKeyboardDatePicker
ではformatにformat="MM/dd/yyyy"
のように指定してもいける感じなので、どうしてこのような仕様にしているかはよく分からない。
しかも表示される時にはYYYY/MM/DD
形式だし。スラッシュどこに行った。
https://material-ui.com/components/pickers/#material-ui-pickers