業務中ワイ
ワイ「なあ、ハスケル子ちゃん」
ハスケル子「はい」
ワイ「今、ハスケル子ちゃんの書いたJSのコードを見てたんやけど」
ワイ「これどういう意味?」
const nodes = document.querySelectorAll(".list > li");
const nodesArray = Array.from(nodes);
const nodesHeightArray = nodesArray.map(getHeight);
ハスケル子「えっと、うーん・・・」
ハスケル子「(見ての通りなんだけど、逆にどこが分からないんだろう・・・)」
ハスケル子「どこが分かりにくかったですか?」
ワイ「このnodesArray.map
いうメソッドは何なん?」
ハスケル子「なんでそんなことが分からないんだろう・・・」
ワイ「お、おい・・・心の声が出てもうてるで・・・」
ハスケル子「(す、すいません!)」
ワイ「なんで謝罪は心の声になっとんねん」
ワイ「どんな忖度や」
ワイ「判断基準どうなってんねん」
ハスケル子「ごめんなさい」
ハスケル子「お詫びにmap
メソッドについて説明しますね」
map
メソッドとは
ハスケル子「map
メソッドは、配列が持ってるメソッド1です」
ハスケル子「ある配列を元に、新しく別の配列を作ることができるメソッドですね」
ハスケル子「さっきのコードでいうと───」
const nodes = document.querySelectorAll(".list > li");
ハスケル子「↑まずquerySelectorAll
で、いくつかのli
要素を取得して」
ハスケル子「それをnodes
とします」
const nodesArray = Array.from(nodes);
ハスケル子「↑次にnodes
をArray.from
メソッドで配列に変換します」
ハスケル子「nodes
は**配列みたいだけど配列じゃない2**ですからね」
ハスケル子「そしてnodes
から作った配列nodesArray
の───」
const nodesHeightArray = nodesArray.map(getHeight);
ハスケル子「───map
メソッドを実行します」
ハスケル子「nodesArray
の各要素に対して何らかの処理を施して」
ハスケル子「新しくnodesHeightArray
という配列を作る、って感じです」
ハスケル子「その何らかの処理というのを、関数として渡します」
ワイ「なるほどな」
ワイ「getHeight
が関数なんやね」
ハスケル子「はい」
const getHeight = element => element.clientHeight;
ハスケル子「getHeight
関数の中身は↑これです」
ワイ「element
という引数を1つ受け取ってelement.clientHeight
を返すということは」
ワイ「要素の高さを返す関数やな」
ハスケル子「はい」
ハスケル子「さっきのコードを実行すると、最終的にnodesHeightArray
には」
[24, 48, 72, 120, 24, 72, 48]
ハスケル子「↑という配列が入ることになります」
ワイ「ほえ〜、DOM要素のリストを元に、各要素の高さを集めた配列を作れるんか」
ワイ「たまに便利そうやな」
ワイの気になったこと
ワイ「でもハスケル子ちゃん」
const getHeight = element => element.clientHeight;
ワイ「↑このgetHeight
の引数であるelement
はどこで渡してんの?」
ハスケル子「どこで渡すっていうか、勝手に入ってくるんです」
ワイ「え・・・勝手に入ってくる・・・?」
ワイ「空き巣みたいに・・・?」
ハスケル子「やめ太郎さんも、自分でmap
メソッドを実装してみたら分かりますよ」
ハスケル子「やってみましょう」
Object.defineProperty(Array.prototype, "myMap", {
value: function(callback) {
/* ここに処理内容を書く */
}
});
ハスケル子「↑こうやって」
ハスケル子「Object.defineProperty
メソッドを使うことで」
ハスケル子「配列にmyMap
というメソッドを追加できるんです3」
ワイ「おお、そんな機能があるんか」
ワイ「ほなちょっと挑戦してみるわ」
ワイ「でも・・・このcallback
いう引数はどこから入ってくんの」
ハスケル子「だから・・・」
ハスケル子「その辺を理解するために今からコードを書いてもらうんです」
ハスケル子「再帰的な質問をしないでください」
ハスケル子「もうとりあえず空き巣が勝手に入ってくるとでも思っておいてください」
ワイ「お、おう・・・」
ハスケル子「配列のmap
メソッドには、関数を1つ渡して実行しましたよね?」
ワイ「ああ、さっきしてたな」
ハスケル子「そのときmap
メソッドに渡した関数が」
ハスケル子「今から書く関数のcallback
という引数として入ってくる・・・」
ハスケル子「そんなイメージでmyMap
メソッドも実装してみてください」
ワイ「なるほど、やってみるで」
Object.defineProperty(Array.prototype, "myMap", {
value: function(callback) {
const resultArray = []; // まず空の配列を作成
/* ここで配列に色々する */
return resultArray; // その配列を戻り値として返す
}
});
ワイ「まずは↑こんな感じやな」
ワイ「空の配列を作って、なんか色々して、最終的にその配列を返す、と」
ハスケル子「いいですね」
ワイ「ほんで、元々の配列の各要素に対して何らかの処理をしていけばいいんやな」
ワイ「そうか」
ワイ「その何らかの処理が、callback
として受け取った関数に入ってるというテイで考えればいいんやな」
ハスケル子「そうです」
ハスケル子「ちなみに今書いてる関数の中ではthis
って書けばその配列自身にアクセスできますよ」
ワイ「なるほど、this
が配列自身やな」
ワイ「ほな、配列の分だけfor文
で回して───」
for (let i = 0; i < this.length; i++) {
const newElement = callback(this[i]); // i番目の要素にcallbackを適用。
resultArray.push(newElement); // それを「戻り値として返す配列」に追加。
}
return resultArray;
ワイ「↑こうやな!」
ワイ「this[i]
、つまり配列の**i
番目の要素に対してcallback
関数を実行**して」
ワイ「それをnewElement
として配列にpush
する」
ワイ「そして出来上がった配列resultArray
を戻り値として返す」
ハスケル子「いいですね!」
ワイ「よっしゃ、じゃあワイのmyMap
メソッド試してみるで!」
const nodesHeightArray = nodesArray.myMap(getHeight);
console.log(nodesHeightArray);
// コンソール結果: [24, 48, 72, 120, 24, 72, 48]
ワイ「おお、さっきのmap
メソッドと同じ結果や!」
ハスケル子「いい感じです」
ハスケル子「実際のmap
メソッドは**もっと複雑なアルゴリズム**ですけど」
ハスケル子「イメージ的はこんな感じです」
ワイ「なるほどな〜」
ワイ「map
メソッドに対して渡したgetHeight
いう関数を」
ワイ「map
メソッドの中で、配列の各要素に対して実行してくれてたんやな」
ワイ「そのときにthis[i]
的な感じで」
ワイ「callback
関数の引数として、元の配列の各要素を渡してくれてたんやな」
ワイ「せやから引数が勝手に入って来てたんか〜」
ワイ「謎が解けましたわ〜」
ワイ「ありがとう、ハスケル子ちゃん」
ハスケル子「実際のmap
メソッドは、callback
関数を実行するときにthis[i]
だけじゃなく」
ハスケル子「i
とthis
も渡してくれるので、さらに色々できますよ」
ワイ「なるほどな」
ワイ「第二引数に今何番目の要素かを表すインデックス」
ワイ「第三引数には配列自身が入ってくると」
ワイ「工夫次第で色々できそうやな」
ワイ「array[i - 1]
で一つ前の要素を見たりとかな」
ハスケル子「そうですそうです」
コールバック関数について
ワイ「今みたいな感じで、関数を渡して使うタイプの関数を」
ワイ「自分で作ってみてもオモロそうやな〜」
ワイ「例えば共通化できそうな処理があったとして」
ワイ「ほとんどの処理を関数として共通化できるけど」
ワイ「最後にちょっとだけ挙動を変えたい」
ワイ「しかも何パターンかの挙動があるから、引数で何かパラメータを受け取ってやる方法やと」
ワイ「if文が増えすぎて見通しが悪くなる・・・そんな時には」
ワイ「関数の最後にこの処理をやってくれい!」
ワイ「っていう処理の内容を、コールバック関数として受け取って」
ワイ「最後に実行してやればええんやな〜」
ワイ「そうすれば」
ワイ「同じ関数を使うけど、一部だけ自由に挙動を変えることが出来んねやな」
ハスケル子「だんだん中級者っぽくなってきましたね」
ワイ「(いや、君よりだいぶJS歴長いねんけどな・・・)」
Object.defineProperty
について
ワイ「あと、Object.defineProperty
すごいな」
ハスケル子「Vue.jsなんかも、このObject.defineProperty
で」
ハスケル子「Array
のpush
メソッド等を上書きすることで」
ハスケル子「配列に要素を追加しただけでDOMが更新されるようにしてるんですよ」
ワイ「せやろな〜(まじか!)」
ワイ「よっしゃ、ワイも明日から配列にメソッド追加しまくるで〜!」
ワイ「あ、メソッドの上書きもできるんやったな!」
ハスケル子「レッツ、プロトタイプ汚染っ!」
社長「(ハスケル子ちゃん、そこはちゃんと止めてや・・・!)」
そうこうしてるうちに18時
ワイ「やば、ワイがコード読めへんせいでもう定時やないかい」
ワイ「コーディング全然進んでへん・・・」
Riot兄さん「俺に任しとけ」
ワイ「あ、兄貴・・・!」
Riot兄さん「俺が界王拳2倍でコーディングして、なんとかしたるわ」
Riot兄さん「よっしゃ、今日から毎日7時間残業するで〜!」
社長「おっ、兄貴の物理界王拳やな!」
ハスケル子「物理界王拳・・・?(兄貴、すごい・・・///)」
社長「せや」
社長「ただし物理界王拳は3倍までしか使えへん」
社長「24時間超えてしまうからな」
ワイ「物理界王拳て」
ワイ「ただのブラック会社4やないかい!」
〜おしまい〜
追記
ワイ「っていうかホンマの界王拳も物理やろ!」