
JavaScript の勉強
JavaScript (VanillaJS)の勉強を始めた。PHP にある変数のリファレンス渡しとかがなかったり、変数の扱いがゆるかったりしてすごい言語だなと驚かされた。DOM操作をマスターすることが最終目的であるが、その前に学ぶべきことも多くあると考えられるので、勉強がてらにどんどん内容を追加していく予定。
目次
反復処理
thread_titles
にクロージャを突っ込んで配列として代入してくれるコードをおれが書いた。(もちろんクソコード)
const parent = document.getElementsByClassName('title');
let parent_length = parent.length;
let thread_titles = function(){
titles = new Array(); // URL を格納する配列
for (let i = 0; i < parent_length; i++) {
tmp_thread_titles = parent[i].getElementsByTagName('a')[0].text;
titles.push(tmp_thread_titles);
}
return titles;
}();
let output = thread_titles;
console.log(output);
友だちがコードレビューしてくれた結果、let thread_titles
の部分に関して改善案をくれた。
const parent = document.getElementsByClassName('title');
const thread_titles = function(){
const titles = [];
for (const element of parent) {
title = element.querySelector('a').text;
titles.push(title);
}
return titles;
}();
メモ
- クロージャ内のスコープは親元と同じなのでわざわざ引数を指定する必要がないらしい
- foreach みたいなことが JavaScript でもできる
- PHP だと
$array as $item
と書くから逆だな
- PHP だと
- const はてっきり定数の略なんだから再代入できないと思っていたら実は違うらしい!
- 配列を変数に割り当てる場合でも再代入や追加ができてしまう
.freeze()
を使用すれば本当の不変にできる
querySelector
を使うと id と class を大きく分けることなくスッキリかける- 同様に、配列も
[]
を使うとスッキリかける
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const
const
宣言は、値への読み取り専用の参照を作ります。これは、定数に保持されている値は不変ではなく、その変数の識別子が再代入できないということです。たとえば、定数の中身がオブジェクトの場合、オブジェクトの内容(プロパティなど)は変更可能です。
本人だったらこう書くらしい
const parent = document.getElementsByClassName('title');
const thread_titles = Array.from(parent).map(element => (
element.querySelector('a').text
));
- アロー関数と map 関数を使って本当に簡潔に書いていてすごい
Array.from()
は配列風オブジェクトを引数にとってマジの配列を生成してくれるメソッド- アロー関数は従来のクロージャをもっとシンプルな形にしたやつ
- 普通の関数からこの形に変形できる方法がちゃんと紹介されている
- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
.map()
は配列のアイテムの数だけ引数に取った関数を実行し、得た戻り値をそのまま配列に再度マッピングしてくれる foreach のようなループを回すことが前提のメソッド- https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map
- アロー関数の意味に関しては、 配列から変数
element
として取った文字列を矢印の向いているquerySelector('a').text
に引数として渡したのち、その戻り値をマッピングしていますよーという意味
- Python とかだったら lamda 記法とかで同じことができるのかな
関数式について
変数に代入して関数定義を行う構文を関数式という。一方で function を使って関数を定義する方法を関数宣言という。
いずれの際も、関数を使用する際、括弧なしでは関数オブジェクトが返ってきてしまうため、関数を呼び出す時は何らかの形で括弧を付けるようにする。その際、従来の関数構文を使う際、引数を早い段階で固定したいなら変数定義時に括弧を付けることもできるし、そうでなければ呼び出す時に括弧を付けても良い。
引数を取らない関数式
アロー関数を使用する際は、呼び出し元で括弧を付ける。お好みで return
を付ける場合は、hello3()
のように構文にカーリーブラケットを付ける。
{
let hello1 = function() {
return 'やっはろー'
}()
console.log(hello1)
// やっはろー
let hello2 = () => 'やっはろー'
console.log(hello2())
// やっはろー
let hello3 = () => {
return 'やっはろー'
}
console.log(hello3())
// やっはろー
}
引数を取る関数式
アロー関数を使用する際、取りうる引数が1つだけなら引数を囲む括弧は省略できる。
{
let hello1_arg = function(obj) {
return 'やっはろー' + obj
}
console.log(hello1_arg('!'))
// やっはろー!
let hello2_arg = obj => 'やっはろー' + obj
console.log(hello2_arg('!'))
// やっはろー!
}
関数型プログラミング
こういったループやカウンターを用いず、変数の代入や配列操作をその場の関数の定義によって行ってしまうような手法を手続き型プログラミングと対比して、関数型プログラミングというらしい。
単純な手続き型の手法で配列操作を行う際、まずは for ループが第一選択肢となる。しかし、この for ループはカウンタ変数を必要とする。foreach の場合も戻り値を返す関数の中で使うなら、一度配列から取り出した値を格納するための変数が必要である。いずれの場合も、ミュータブルな変数が必要になってくる。このように、ある目的を達成するために、目的以外の変数まで変化することを強いてしまうことを、巷では副作用を生むというらしい。
副作用を生むことは、扱う変数の数が増え、同時にプログラムの安全性が保証されづらくなってしまうことを意味する。一方で、関数型プログラミングの場合だとその配列操作は変数の定義ないし準備段階で完了してしまい、この操作は他の変数の著しい変異を伴うこともなく静的である。ゆえに、より安全なコードの記述が期待される。
map() を使った配列のzip化
{
const fruits = ['apple', 'banana', 'grape'];
const vegetables = ['carrot', 'tomato', 'peanut']
// i はインデックス番号
const combined = fruits.map((e, i) => [e, vegetables[i]])
console.log(combined)
// 0: Array(3) [ "apple", "banana", "grape" ]
// 1: Array(3) [ "carrot", "tomato", "peanut" ]
}
オブジェクトの場合でも同様に可能。
{
// 一人一人に野菜とフルーツをそれぞれ1つずつプレゼント
const name = ['Ken', 'Hana', 'Kumi'];
const fruits = ['apple', 'banana', 'grape'];
const vegetables = ['carrot', 'tomato', 'peanut'];
// i はインデックス番号
const present = name.map((e, i) => {
return {
name: e,
given: [fruits[i], vegetables[i]],
}
});
console.log(present);
}
Try-Catch 文を用いた失敗処理
他の言語を使っているとき、特にエラーを Try-Catch 文を用いてハンドリングすることはなかった。自分はかなり簡単なプログラムを書いていたのもあって、それから正直勉強するのが面倒くさいと感じていたのもあって、今まで失敗処理を if
文を用いつつ無理やり exit()
で押し流していた。ところが、exit() 関数のないJavaScript ではどうもそうはいかないようだ。
Map Object
キーと値のペアを保持し、キーが最初に挿入されたインデックスをその構造自体に記録しているが、同じキー名は存在できない。キー名が要らないなら普通に Array を使った方が良い。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map
{
let contacts = new Map()
contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})
contacts.set('Hilary', {phone: "617-555-4321", address: "321 S 2nd St"})
// キー名を名無しにしても一応大丈夫だった
contacts.set('', {phone: "512-742-8463", address: "213 S 1st Ret"})
console.log(contacts);
}
コンソールの結果

演算子
演算子の使い方について興味深いものがあったので書き残していく。
論理演算子
AND 演算子は、複数の AND された値の中から最初に false となる値を探し、OR 演算子は最初に true となる値を探す!今まで if 節などで論理演算子を使いつつ条件判定を行ってきたが、特にその論理演算子自体が true や false を返すという考えには至っていなかった。ドキュメントで改めて該当箇所を読んだら本質的なところを理解できたような気がする。
この即時関数は、二つの値 a >= 10
と false
の中から最初に true となるものを返す。与えられた引数は 10 で、a >= 10
が true となるので、返り値はその真偽値が返されることになる。
値が null あるいは undefined かどうかを確認したいときには、Null 合体演算子を使うと良い。
{
(function is_this_more_than(a) {
return a >= 10 || false;
})(10); // true
}
型を変換するために用いられる演算子
NOT 演算子を使って引数の型を Boolean 型に変換する例。
{
function is_empty(a) {
return !a; // Boolean(a) でもできる
}
is_empty(''); // true
is_empty(9); // false
}
単項演算子を使って引数の型を Number 型に変換する例。
{
typeof((function (a) {
return +a;
}))('9'); // 'number'
}