問題一覧
1
サロゲートペアの文字はlengthで2が出てしまうが、 サロゲートペアはただしく扱うメソッドはなにか?
String.fromCodePoint と str.codePointAt はサロゲートペアを正しく扱う稀なメソッドです
2
文字の正規化とはなにか?
マーク文字を付与した際に比較を確実に行うために正規化する。 具体的には、正規化されると、一つの文字として扱われるようになる 詳細は以下 多くの言語では、上/下にマークを持つ基本文字で構成される記号があります。 例えば、文字 a は àáâäãåā のベースの文字です。最も一般的な “複合” 文字は、UTF-16テーブルに独自のコードを持っています。 任意の複合文字をサポートするため、UTF-16 ではいくつかのユニコード文字を使うことができます。ベース文字とそれを “装飾” する1つまたは複数の “マーク” 文字です。 以下のように Sのベース文字に対し、マーク文字を複数付与できる。 let s1 = 'S\u0307\u0323'; // Ṩ, S + 上のドット + 下のドット let s2 = 'S\u0323\u0307'; // Ṩ, S + 下のドット + 上のドット alert( `s1: ${s1}, s2: ${s2}` ); alert( s1 == s2 ); // false (文字は同一に見えますが) これを解決するために正規化が用意された。
3
ユニコード正規化のメソッドはなにか?
str.normalize() で実装されています。 記号 Ṩ の場合 alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true 例の場合、normalize() は実際に3つの文字の並び(ベース文字とマーク文字2つ)を1つの文字 \u1e68 (S と2つのドット)にまとめています。 alert( "S\u0307\u0323".normalize().length ); // 1 alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
4
文字列操作に関するメソッドで、 最初と最後のスペースをのぞくメソッド ある文字列を何回か繰り返すメソッドをあげよ
str.trim() – 文字列の最初と最後のスペースを除去します。 str.repeat(n) – 文字列を n 回繰り返します。
5
最初の文字を大文字化して文字列 str を返す関数 ucFirst(str) を書いてください。 例: ucFirst("john") == "John";
JavaScript では文字列はイミュータブル(不変)なので、最初の文字を “置換” することはできません。 しかし、既存のものをベースに、最初の文字が大文字化された新しい文字列を作ることはできます。: let newStr = str[0].toUpperCase() + str.slice(1); が、そこには少し問題があります。str が空の場合、str[0] は未定義です。なのでエラーになります。 function ucFirst(str) { if (!str) return str; return str[0].toUpperCase() + str.slice(1);
6
オブジェクトと配列の違いは?
配列では、要素の順序を管理するためのメソッドが提供されているため、順序付けされたコレクションを格納するのに適している。
7
配列の宣言方法は? 配列がサポートする型は?
let arr = new Array(); let arr = []; 配列はどんな型の要素も格納することができます。 // 値の混在 let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ]; // インデックス 1 のオブジェクトを取得し、その名前を表示 alert( arr[1].name ); // John // インデックス 3 の関数を取得し、実行 arr[3](); // hello
8
配列やオブジェクト宣言時、要素の末尾にカンマをいれても問題ないか
問題ない 配列は、オブジェクトのようにカンマで終わる場合があります: let fruits = [ "Apple", "Orange", "Plum", ]; すべての行が同じようになるので、“末尾のカンマ” は項目の挿入や削除が容易になります。
9
配列の最後の要素を抽出して返すメソッドは? 配列の末尾に要素を追加するメソッドは?
list.pop() let fruits = ["Apple", "Orange", "Pear"]; alert( fruits.pop() ); // "Pear" を削除し alert する alert( fruits ); // Apple, Orange list.push(...)※複数要素にも対応 let fruits = ["Apple", "Orange"]; fruits.push("Pear"); alert( fruits ); // Apple, Orange, Pear
10
配列の先頭の要素を抽出して返すメソッドは? 配列の先頭に要素を追加するメソッドは?
list.shift() let fruits = ["Apple", "Orange", "Pear"]; alert( fruits.shift() ); // Apple を削除し alert する alert( fruits ); // Orange, Pear list.unshift(...)※複数要素にも対応 let fruits = ["Orange", "Pear"]; fruits.unshift('Apple'); alert( fruits ); // Apple, Orange, Pear
11
配列はなにをもとに実現されているか?(ベースはなに?)
ベースはオブジェクト なので、任意のプロパティの追加もできる。 ただし非推奨! let fruits = []; // 配列を作ります fruits[99999] = 5; // その length よりも非常に大きなインデックスでプロパティを割り当てます fruits.age = 25; // 任意の名前でプロパティを作成します
12
メソッド push/pop 、shift/unshift どちらがパフォーマンスがよいか説明せよ
push/popのほうがよい shift/unshift の場合は、先頭を削除してから、インデックスの再割り当てが必要なので遅い ↑配列が大きいほど遅くなる
13
配列でよく用いられる、 値のループの方法は?
配列のための for..of という別のループの形式があります: let fruits = ["Apple", "Orange", "Plum"]; // 配列要素の反復処理 for (let fruit of fruits) { alert( fruit ); } ↑値をとりだしてループ処理を行う もちろんオブジェクトでも使える
14
配列に対して for..inループを避けるべき理由は? 2点上げよ
1 for..in は数値のものだけでなく、 全てのプロパティ を繰り返し処理します。 なので、配列に似たオブジェクトなどで、余分なプロパティが混じる可能性あり。 ブラウザや他の環境では 配列のように見える いわゆる “配列のような” オブジェクトがあります。つまり、それらは length とインデックスプロパティを持っています。しかし、それらは通常は必要のない他の非数値プロパティやメソッドも持っています。for..in ループはそれらもリストします。なので、もし配列のようなオブジェクトを処理する必要があるとき、それらの “余分な” プロパティが問題になる場合があります。 2 配列に最適化されてないため遅い for..in ループは配列ではなく、汎用オブジェクトに対して最適化されているため、10から100倍遅くなります。もちろんそれでもとても速いです。高速化はボトルネックの場合にのみ問題なり、それ以外ではさほど重要でないこともあります。しかしそれでも私たちは違いに気をつけるべきです。
15
配列のlengthプロパティについて説明せよ
配列を変更したとき、length プロパティは自動的に更新されます。正確には、それは配列の実際の値の数ではなく、最大の数値インデックスに1を加えたものです。 例えば、大きなインデックスの1つの要素は大きなlengthを返します: let fruits = []; fruits[123] = "Apple"; alert( fruits.length ); // 124 通常、そのように配列を使わないことに注意してください。
16
配列のlengthを書き込み、減らした場合どんなことがおきるか?
length プロパティの別の興味深い点は、書き込み可能と言う点です。 手動で増やした場合、面白いことは起きません。しかし、それを減らしたとき、配列は切り捨てられます。この処理は不可逆です。これはその例です: let arr = [1, 2, 3, 4, 5]; arr.length = 2; // 2つの要素に切り捨てる alert( arr ); // [1, 2] arr.length = 5; // length を戻す alert( arr[3] ); // undefined: 値は返ってきません なので、配列をクリアする最もシンプルな方法は arr.length = 0; です
17
配列に定義されたtoStringメソッドについて説明せよ
配列は、要素のカンマ区切りのリストを返す独自の toString メソッドの実装を持ってます。 let arr = [1, 2, 3]; alert( arr ); // 1,2,3 alert( String(arr) === '1,2,3' ); // true
18
配列同時で等価比較を行う際 配列とプリミティブの等価比較を行う とどうなるか?
== で比較しないでください オブジェクトと同様の動作、つまり プリミティブへの変換が行われる。 == の引数の一方がオブジェクトで、もう一方がプリミティブの場合、プリミティブに変換されます。 ちなみに厳密比較 === は、型変換をしないためよりシンプルです。 なので、== で配列を比較する場合、全く同じ配列を参照している2つの変数を比較しない限り、決して等価にはなりません。 例: alert( [] == [] ); // false alert( [0] == [0] ); // false プリミティブとの比較では、以下のように、一見すると奇妙な結果がでることがあります: alert( 0 == [] ); // true alert('0' == [] ); // false ここでは、両方のケースで配列オブジェクトとプリミティブを比較しています。なので、配列 [] は比較のためにプリミティブに変換され、空文字 '' になります。 そこから、0と比較する場合は、空文字から0に変換されてtrueとなる。
19
以下はどうなる? let arr = ["a", "b"]; arr.push(function() { alert( this ); }) arr[2](); // ?
呼び出し arr[2]() は、構文的には古き良き obj[method]() であり、arr が obj の役割を、2 が method の役割を持っています。 従って、arr[2] の関数をオブジェクトメソッドとして呼び出します そのため、答えは以下となる。 // "a","b",function
20
配列から要素を削除する方法は?
削除する場合、配列のインデックス移動などの操作が必要なので、deleteは使わない。 arr.splice(str) メソッドを利用する。 追加、削除、また要素の挿入もできるメソッドである。※副作用あり 消す位置と消す個数をきめて、あとは入れる要素をわたせる 構文: arr.splice(index, deleteCount, elem1, ..., elemN) 位置 index から始まります。 deleteCount の要素を削除した後、その場所に elem1, ..., elemN を挿入します。このメソッドは削除した要素の配列を返します。 このメソッドは例で簡単に把握できます。 次の例では、3つの要素を削除し、他の2つの要素でそれらを置き換えます: let arr = ["I", "study", "JavaScript", "right", "now"]; // 最初の 3 要素を削除し、別のものに置換 arr.splice(0, 3, "Let's", "dance"); alert( arr ) // 今は ["Let's", "dance", "right", "now"] splice メソッドは削除せずに挿入することも可能です。 負のインデックスも許容します let arr = [1, 2, 5]; // インデックス -1 (末尾から1つ前) から // 削除 0 要素, // その後 3 と 4 を挿入 arr.splice(-1, 0, 3, 4); alert( arr ); // 1,2,3,4,5
21
部分配列を返すメソッドは?
arr.slice(start, end) 文字列といっしょ let arr = ["t", "e", "s", "t"]; alert( arr.slice(1, 3) ); // e,s (1 から 3 をコピー) alert( arr.slice(-2) ); // s,t (-2 から末尾まで)
22
配列のコピーはどうやるか?
引数なし arr.slice() でも呼び出すことができ、これは arr のコピーを生成します。 これは、オリジナルの配列に影響を与えない形でさらに変換するためのコピーを取得するのによく使用されます。
23
配列同士の結合はどうやる? またオブジェクトそのものを結合対象としたい場合はどうするか?
メソッド arr.concat は配列を他の配列またはアイテムと結合します。 構文: arr.concat(arg1, arg2...) 任意の数の引数(配列または値)を許容します。 結果は arr, 次に arg1, arg2 などのアイテムを含む新しい配列を返します。 ※arr自体は変わらない もし、引数が配列、もしくは Symbol.isConcatSpreadable プロパティを持っている場合、その全ての要素がコピーされます。そうでない場合、引数自体がコピーされます。 let arr = [1, 2]; // arr と [3,4] をマージ alert( arr.concat([3, 4])); // 1,2,3,4 // arr と [3,4] と [5,6] をマージ alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6 // arr と [3,4] をマージ後, 5 と 6 を追加 alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6 オブジェクトを追加する場合、要素としてそのまま追加されてしまうが、 オブジェクトに対してSymbol.isConcatSpreadableプロパティを追加することで、配列ライクなオブジェクトとしてふるまうことができ、Concatされた時に、length以外のプロパティを結合対象とすることができる。 let arr = [1, 2]; let arrayLike = { 0: "something", 1: "else", [Symbol.isConcatSpreadable]: true, length: 2 }; alert( arr.concat(arrayLike) ); // 1,2,something,else ↑もし、 [Symbol.isConcatSpreadable]が定義されてなかったら、// 1,2,[object Object]になる。
24
配列の全要素に対して関数を実行することができるメソッドはなにか?
arr.forEach メソッドは配列の全要素に対して関数を実行することができます。 構文: arr.forEach(function(item, index, array) { // ... item に対して何か処理をする }); 例えば、これは配列の各要素を表示します: // 各要素は alert を呼び出す ["Bilbo", "Gandalf", "Nazgul"].forEach(alert); この場合はalert関数は引数itemだけを受け取る ["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => { alert(`${item} is at index ${index} in ${array}`); });
25
配列でのアイテム検索を行うメソッドは?
メソッド arr.indexOf, arr.lastIndexOf と arr.includes は文字列の場合と同じ構文を持ち、基本的に同じことを行いますが、文字の代わりにアイテムを操作します: arr.indexOf(item, from) はインデックス from から item を探し、見つかった場所のインデックスを返します。そうでない場合は -1 になります。 arr.lastIndexOf(item, from) は同じですが、右から左に見ていきます。 arr.includes(item, from) はインデックス from から item を探し、見つかった場合、true を返します。 let arr = [1, 0, false]; alert( arr.indexOf(0) ); // 1 alert( arr.indexOf(false) ); // 2 alert( arr.indexOf(null) ); // -1 alert( arr.includes(1) ); // true これらのメソッドは厳密な比較、 === 比較を使うことに留意してください! もしも false を探す場合、ゼロではなく、正確な false を見つけようとします。 また、includes の非常に小さな違いは、indexOf/lastIndexOf と違い、NaN を正しく処理することができます: const arr = [NaN]; alert( arr.indexOf(NaN) ); // -1 (0 になるべきですが, === 等値は NaN では機能しません) alert( arr.includes(NaN) );// true (正しい)
26
配列から特定条件のアイテムを一つ特定するメソッドは? 例 let users = [ {id: 1, name: "John"}, {id: 2, name: "Pete"}, {id: 3, name: "Mary"} ]; を例に説明せよ
arr.find メソッド、※arr.findIndex メソッドもある 構文はこうです: let result = arr.find(function(item, index, array) { // true が返却されると、item が返却され、イテレーションは停止します // 偽の場合は undefined です }); 関数は配列の要素毎に繰り返し呼ばれます: item は要素です。 index はインデックスです。 array は配列自身です。 もし true を返すと、検索が止まり、item が返却されます。見つからない場合は undefined になります。 例 let users = [ {id: 1, name: "John"}, {id: 2, name: "Pete"}, {id: 3, name: "Mary"} ]; let user = users.find(item => item.id == 1); alert(user.name); // John arr.findIndex メソッドは基本的に同じです。が、要素自体ではなく要素が見つかったインデックスを返します。
27
配列から特定条件でフィルタリングするには? 下記を例に説明せよ let users = [ {id: 1, name: "John"}, {id: 2, name: "Pete"}, {id: 3, name: "Mary"} ];
検索結果の要素が複数になる可能性がある場合、arr.filter(fn) を使います。 filter はマッチしたすべて要素の配列を返します: let results = arr.filter(function(item, index, array) { // true の場合、item は results にプッシュされ、イテレーションは継続します // 何も見つからない場合は、空配列を返します }); 例: let users = [ {id: 1, name: "John"}, {id: 2, name: "Pete"}, {id: 3, name: "Mary"} ]; // 最初の2人のユーザの配列を返します let someUsers = users.filter(item => item.id < 3); ※アロー関数は1行ならreturnキーワードを省略できる 上の例はBooleanを返している alert(someUsers.length); // 2
28
配列の各要素に対して、何らかの処理を適用させたい場合は何のメソッドがつかえるか? let lengths = ["Bilbo", "Gandalf", "Nazgul"]を文字数の配列にせよ 5,7,6が答え。
arr.map メソッド これは、配列の各要素に対して関数を呼び出し、結果の配列を返します。 構文は次の通りです: let result = arr.map(function(item, index, array) { // item の代わりに新しい値を返します }) 例えば、ここでは各要素をその長さに変換します: let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length) alert(lengths); // 5,7,6
29
配列をソートする方法は? let arr = [ 1, 15, 2 ]; 上記を昇順でソートしたい場合は? 関数を利用しない方法と利用する方法をかけ
メソッド arr.sort は配列を 決まった位置に ソートし、要素の順番を変更します。 これもソートされた配列を返しますが、arr 自身が変更されるので、返却値は通常無視されます。 ※副作用あり! 関数を渡さない場合は、 文字列に変換されてソートされる。 let arr = [ 1, 2, 15 ]; // このメソッドは arr の内容を並べ替え(てそれを返します) arr.sort(); alert( arr ); // 1, 15, 2←文字列としてソートされた 関数を渡す場合は、 引数二つとる関数を用意する必要がある。 ちなみにa bに入ってくる値は 最適化されたクイックソートアルゴリズムによって選出された二つの要素ということになる。 返却値に関しては、 0より上なら、aをbのあとに並び替える 0より下なら、aをbのまえに並び替える(そのまま) 0なら、そのまま a>b return 1で昇順とおぼえる! なので b aになる! function compareNumeric(a, b) { if (a > b) return 1; if (a == b) return 0; if (a < b) return -1; } let arr = [ 1, 2, 15 ]; arr.sort(compareNumeric); alert(arr); // 1, 2, 15 もっと簡単に記載するとこうなる let arr = [ 1, 2, 15 ]; //aが大きい場合は正の数となり、順番が入れ替わり、bが大きい場合は負の数となりそのまま。 arr.sort( (a, b) => a - b ); alert(arr); // 1, 2, 15
30
文字列がアイテムの配列をソートする場合は、Öなどの文字を考慮するためにどのような方法がとれるか?
str.localeCompare(str2) を利用する これは、 言語規則に従って、str が str2 より大きい場合、1 を返します。 str が str2 より小さい場合は -1 を返します。 それらが等価な場合は 0 を返します。 という関数なのでそのまま利用できる。 let countries = ['Österreich', 'Andorra', 'Vietnam']; alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (間違い) alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (正しい!)
31
配列の要素の順序を反転させるメソッドは?
メソッド arr.reverse は arr 内の要素の順序を逆転させます。 ※副作用あり 例: let arr = [1, 2, 3, 4, 5]; arr.reverse(); alert( arr ); // 5,4,3,2,1 また、反転後に配列 arr を返します。
32
文字列に対して、区切り文字で配列に分割するメソッドは?
str.split(delim) メソッドは、与えられた区切り文字 delim で文字列を配列に分割します。 let names = 'Bilbo, Gandalf, Nazgul'; let arr = names.split(', '); for (let name of arr) { alert( `A message to ${name}.` ); // A message to Bilbo }
33
文字列を文字に分割する方法は?
split(s) を呼び出すとき、sを空にすると、文字列を文字の配列に分割します: let str = "test"; alert( str.split('') ); // t,e,s,t
34
splitとは逆に配列の要素を区切り文字で繋げるメソッドは?
arr.join(str) は split と逆を行います。arr のアイテムを str で繋いだ文字列を作ります。 例: let arr = ['Bilbo', 'Gandalf', 'Nazgul']; let str = arr.join(';'); alert( str ); // Bilbo;Gandalf;Nazgul
35
配列に対して、合計値がなどの単一の値を算出するために利用するメソッドはなにか? 以下を合計せよ let arr = [1, 2, 3, 4, 5];
メソッド arr.reduce と arr.reduceRight は、配列に基づいて単一の値を計算するために使用されます。 arr.reduceRight メソッドは右側の要素から計算していくだけの違い。 構文: let value = arr.reduce(function(accumulator, item, index, arr) { // ... }, initial); 関数は各要素に順番に適用され、その結果を次の呼び出しに “引き継ぎ” ます: initial は省略可能だが、記載したほうがよいとされている 引数: accumulator – 前の関数呼び出しの結果で、初回は initial と等価です(initial が指定されている場合) item – 現在の配列の項目です。 index – 位置です。 arr – 配列です。 関数が適用されると、前の関数呼び出しの結果が、次の関数呼び出しの最初の引数として渡されます。 let arr = [1, 2, 3, 4, 5]; let result = arr.reduce((sum, current) => sum + current, 0); ↑0はinitial、つまり初期値のこと alert(result); // 15 0+1 1+2 3+3 6+4 10+5 てかんじで足し合わされてった感じ。
36
メソッド arr.reduce に関して、initialを省略した場合の留意事項を述べよ
初期値が指定されていない場合、reduce は配列の最初の要素を初期値とみなし、2つ目の要素から繰り返し処理を始める。 そのため空配列を初期値なしで処理しようとすると、エラーとなるらしい 従って、常に初期値を指定することをおすすめします
37
alert(typeof {}); // object ↓これはどういう結果になるか? alert(typeof []);
配列は別の言語の型を形成していないため Objectとなる!
38
任意のオブジェクトが配列かどうかを調べるためには?
メソッド Array.isArray(value) があります。これは、value が配列のときに true を、そうでない場合には false を返します。 alert(Array.isArray({})); // false alert(Array.isArray([])); // true
39
関数を呼び出すほとんどの配列メソッドは 任意の追加パラメータ thisArgをサポートしている。 上記について詳細を説明せよ
まず、sortはサポートしてなくて、 find、filter、mapとかでサポートしているらしい。 配列メソッドは渡された関数を実行するが、その際メソッド実行ではなく関数実行となってしまうので、内部のthisの値がグローバルオブジェクトを指してしまう。 これを回避するため、thisArgパラメータでthisの値を明示的にバインドする。 let army = { minAge: 18, maxAge: 27, //booleanを返すメソッド canJoin(user) { return user.age >= this.minAge && user.age < this.maxAge; } }; let users = [ {age: 16}, {age: 20}, {age: 23}, {age: 30} ]; // army.canJoin関数に各userが渡されて関数実行される。 let soldiers = users.filter(army.canJoin, army); alert(soldiers.length); // 2 alert(soldiers[0].age); // 20 alert(soldiers[1].age); // 23 もし上の例で users.filter(army.canJoin) としていた場合、army.canJoin はスタンドアローンの関数として呼び出されるため、this=undefined であり、即時エラーになります。
40
前問題と同じ以下に関して、thisArgを利用しない方法を示せ let army = { minAge: 18, maxAge: 27, canJoin(user) { return user.age >= this.minAge && user.age < this.maxAge; } }; let users = [ {age: 16}, {age: 20}, {age: 23}, {age: 30} ]; // army.canJoin が true となるユーザを見つけます let soldiers = users.filter(army.canJoin, army); alert(soldiers.length); // 2 alert(soldiers[0].age); // 20 alert(soldiers[1].age); // 23
関数を直接渡すのではなく、緩衝材として関数を作り、そこでメソッド実行してやるとよい。 呼び出し users.filter(army.canJoin, army) は users.filter(user => army.canJoin(user)) に置き換え可能である。 多くの人にとってより理解しやすいので、後者の方がよく利用されます。
41
配列の各要素に対して ||や&&演算子と同様のやり方で真偽値チェックを行うメソッドはなにか?
arr.some(fn)/arr.every(fn) は配列の各要素に対してチェックを行い、真偽値を返す。 引数の関数には各要素のvalueとindexが引数としてとることができる。 valueの値をチェックして真偽値を返す関数を定義する。 someは|| every は&&として使える。 もし fn が真の値を返す場合、arr.some() はすぐに true を返し、残りの項目に対するイテレーションを停止します。 偽なら次のイテレーションへ。 fn が偽の値を返す場合は、arr.every() はすぐに false を返し、同様に残りの項目のイテレーションは停止します。 every は配列を比較するのに使えます: function arraysEqual(arr1, arr2) { // 配列の長さが同じであること、 arr1の値が、arr2の同じインデックスの値と全ての要素で同じなら trueを返す。 一個でも違えばfalseを返す。 return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); } alert( arraysEqual([1, 2], [1, 2])); // true
42
https://ja.javascript.info/array-methods#ref-384 拡張可能な” 計算機オブジェクトを作る かな?
良い問題なのでやれ コツはプロパティの配列でメソッドを管理している。 この配列にはメソッドを追加できるので、拡張可能としている。 キャルクレートメソッドを起動することで、引数に合わせたメソッドを配列から起動する感じ
43
アロー関数でオブジェクトを返却する場合、以下の構文で問題ないか? const res = [1,2,3].map(i => {res: i}) console.log(res);
問題あり、上記の問題はreturnを省略しており、さらに、オブジェクトリテラルのカッコは処理部分の省略されたカッコであると解釈されてしまう。 なので、結果はこうなる。 const res = [1,2,3].map(i => {res: i}) console.log(res) [ undefined, undefined, undefined ] 対策する場合は、returnを利用する const res = [1,2,3].map(i => { return { res: i } }) console.log(res) [ {res: 1}, {res: 2}, {res: 3} ] それか、丸括弧でオブジェクトリテラルをかこむ const res = [1,2,3].map(i => ({res: i})) console.log(res) [ {res: 1}, {res: 2}, {res: 3} ]
44
反復可能な(iterables) オブジェクトとはなにか?
for..of ループが使用できるオブジェクトのこと
45
以下のrangeオブジェクトをイテラブルにするためにどう実装するか? let range = { from: 1, to: 5 }; // for..of が動作するようにしたい: // for(let num of range) ... num=1,2,3,4,5
range を 反復可能(iterable) にするために (for..of を動作させるために)は、Symbol.iterator というメソッドをオブジェクトに追加する必要がある。 以下の特性がある。 for..of が始まると、そのメソッドを呼び出します(なければエラーになります)。 メソッドは iterator (メソッド next をもつオブジェクト)を返さなければいけません。 for..of が次の値を必要とするとき、そのオブジェクトの next() を呼びます。 next() の結果は {done: Boolean, value: any} の形式でなければなりません。 そして done=true は繰り返しが終わったことを示します。 そうでない場合は、value は新しい値である必要があります。 これは range の完全な実装です: let range = { from: 1, to: 5 }; // 1. for..of の呼び出しは、最初にこれを呼び出します range[Symbol.iterator] = function() { // ...これは iterator オブジェクトを返します: // 2. 以降、for..of はこのイテレータでのみ機能し、次の値を要求します return { current: this.from, last: this.to, // 3. for..of ループにより、各繰り返しで next() が呼ばれます next() { // 4. オブジェクト {done:.., value :...} を返す必要があります if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; }; // これで動作します! for (let num of range) { alert(num); // 1, 2, 3, 4, 5 } 反復可能(iterables)の中心的な機能に注目してください。関心の分離です。 range 自身は next() メソッドを持っていません。 代わりに、別のオブジェクト、いわゆる “イテレータ” は range[Symbol.iterator]() の呼び出しで生成され、反復を処理します。 従って、反復オブジェクトは反復処理されるオブジェクトから分離されています。
46
rangeとイテレーターは分離せず、range自体にイテレーターの機能を実装する場合は?
rangeオブジェクトにnext()を実装する。 同時に[Symbol.iterator]メソッドを定義し、自身を返す。 let range = { from: 1, to: 5, [Symbol.iterator]() { this.current = this.from; // 自身を返す。 return this; }, next() { if (this.current <= this.to) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; for (let num of range) { alert(num); // 1, そして 2, 3, 4, 5 }
47
イテラブルなオブジェクト自体にイテレータを実装するデメリットはなにか?
欠点は、オブジェクトに対して同時に2つの for..of ループを実行することは不可能だということです。 イテレータ が1つしかないので、オブジェクトは繰り返し状態を共有します。 ですが、2つの並列 for-of はたとえ非同期のシナリオにおいてもまれです。
48
無限のイテレータを作成するにはどうするか? 前問のrangeオブジェクトを例に説明せよ
先ほどの例では、range.to = Infinity にすることで、range が無限大になります。 next でdone:trueにせず、valueの値を常に生成しつづけることも可能。 それで、擬似乱数の無限のシーケンスを生成する反復可能なオブジェクトを作ることができます。 もちろん、このような反復可能なオブジェクトに対する for..of ループはエンドレスですが、break を使っていつでも止めることができます。
49
配列以外にイテラブルな組み込みオブジェクトはなにか?
文字列 for (let char of "test") { // 文字ごとに1回、計4回実行します。 alert( char ); // t, 次に e, 次に s, そして t } そしてサロゲートペアも正しく動作します! let str = '𝒳😂'; for (let char of str) { alert( char ); // 𝒳, そして次は 😂 }
50
イテレータを明示的に扱い、for..ofの処理を実装するにはどうするか?
let str = "Hello"; // for (let char of str) alert(char); // と同じことをしています //イテレータを取得 let iterator = str[Symbol.iterator](); while (true) { // nextメソッドを呼び出し let result = iterator.next(); // doneがtrueならループを抜ける if (result.done) break; alert(result.value); // 1つずつ文字を出力します }
51
反復可能(Iterables) と 配列ライク(array-like)の違いを説明せよ
反復可能(Iterables) は上で説明したように、Symbol.iterator メソッドを実装したオブジェクトです。 配列ライク(Array-likes) は、インデックスと length を持ったオブジェクトです。なので、これらは配列のように見えます。 例えば、文字列は 反復可能(iterable) (for..of が動作する) であり、かつ 配列ライク(array-like) (数値インデックスと length を持っています) です。
52
反復可能(iterable) と 配列ライク(array-like) は両方とも通常の 配列ではなく、push や pop などのメソッドを持っていません。 こういったオブジェクトが、Arrayのメソッドを扱えるようにするにはどうすればよいか?
Array.from メソッドがあります。 これは 反復可能(iterable) または 配列ライク(array-like) な値を引数に取り、そこから “本当の” Array を作ります。 それ以降、配列のメソッドを呼べるようになります。 let arrayLike = { 0: "Hello", 1: "World", length: 2 }; let arr = Array.from(arrayLike); // (*) alert(arr.pop()); // World (メソッドが動作します) 行 (*) の Array.from はオブジェクトを取り出し、反復可能(iterable) か 配列ライク(array-like) なのか調べ、新しい配列を作りすべてのアイテムをコピーします。
53
Array.fromで配列作成時に各要素に関数を適用するにはどうするか?
2つ目の引数 mapFn を利用する。 Array.from の完全な構文では、オプションで “マッピング” 関数を指定できます: Array.from(obj[, mapFn, thisArg]) ↑[]の間は任意って意味 2つ目の引数 mapFn は、配列に追加する前に各要素に適用する関数であり、thisArg はそこでの this を指定できます。 // range は前問の例で利用したものと仮定 // 各数値の平方 let arr = Array.from(range, num => num * num); alert(arr); // 1,4,9,16,25 まーMap関数があるしあんまおぼえなくていいかも
54
サロゲートペアを考慮して、文字列を文字配列に変換するにはどうするか?
文字列を文字配列に変換するために Array.from を使います: let str = '𝒳😂'; // str を文字の配列に分割します let chars = Array.from(str); alert(chars[0]); // 𝒳 alert(chars[1]); // 😂 alert(chars.length); // 2 str.split とは違い、文字の反復可能な性質に依存するため、for..of のようにサロゲートペアでも正しく動作します。 技術的には、これは以下と同じことをしています: let str = '𝒳😂'; let chars = []; // Array.from は内部では同じループをします for (let char of str) { chars.push(char); } alert(chars); …が、より短く書けます。 これを活用することで、 サロゲートを意識した slice を実装することもできます!
55
Mapとオブジェクトの違いを説明せよ
Map は Object と同じように、キー付されたデータ項目の集まりです。主な違いは Map は任意の型のキーを許可することです。 もちろんオブジェクトもキーにできる。 オブジェクトとは違い、キーは文字列には変換されません。任意の型のキーが利用可能です。 let map = new Map(); map.set('1', 'str1'); // 文字列キー map.set(1, 'num1'); // 数値キー map.set(true, 'bool1'); // 真偽値キー // 通常のオブジェクトを覚えていますか?キーを文字列に変換していました。 // Map は型を維持します。なので、これらは別ものです: alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' alert( map.size ); // 3 繰り返しは値が挿入された順で行われます。通常の Object とは違い、Map はこの順番を保持しています
56
Mapの主なプロパティ、メソッドをあげよ
map.set(key, value) – キーで、値を格納します. map 自身を返すので、呼び出しを “チェーン” することができます: map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1'); map.get(key) – 指定されたキーの値を返却します。map に存在しない key の場合には undefined になります. map.has(key) – キーが存在する場合に true を返します。そうでなければ false になります. map.delete(key) – キーで値を削除します. map.clear() – map をクリアします. map.size – 現在の要素の数です.
57
Mapでgetやsetを利用せずに、 map[key]で、アクセス可能か?
できるけど非推奨 キーが 文字列とシンボルキーでしか使えない これは map を通常の JavaScript オブジェクトとして扱っているので、対応するすべての制限(文字列/シンボルキーのみなど)があることを意味します。
58
オブジェクトで、キーにオブジェクトを設定した場合どうなるか?
オブジェクトで、キーにオブジェクトは使えない。 なので、そのオブジェクトは自動で文字列に変換される。 つまり、 "[object Object]"がキーとなる。
59
Mapはどのようにキーを比較しているか
等価のテストをするために、Map は SameValueZero アルゴリズムを使います。大雑把には厳密等価 === と同じですが、違いは NaN は NaN と等しいとみなされる点です。なので、NaN も同様にキーとして使うことができます。
60
Mapでループする時に利用するメソッドを3つあげよ
map.keys() – キーに対する iterable を返します。 map.values() – 値に対する iterable を返します。 map.entries() – エントリ [key, value](配列) の iterable を返します。これは for..of でMapオブジェクトを指定すると暗黙的に使われます。 let recipeMap = new Map([ ['cucumber', 500], ['tomatoes', 350], ['onion', 50] ]); // キー(野菜)の反復 for (let vegetable of recipeMap.keys()) { alert(vegetable); // cucumber, tomateos, onion } // 値(量)の反復 for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } // [key, value] エントリーの反復 for (let entry of recipeMap) { // recipeMap.entries() と同じ alert(entry); // cucumber,500 (など) } ※ちなみに。。 Map は Array と同じように、組み込みの forEach メソッドを持っています。 // 各 (key, value) ペアに対して関数を実行 recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc });
61
Mapを初期値ありで生成する方法は?
new Mapで、 キー/値のペア(配列)をもつ、配列またはイテラブルを渡すと生成できる。 // [key, value] ペアの配列 let map = new Map([ ['1', 'str1'], [1, 'num1'], [true, 'bool1'] ]); alert( map.get('1') ); // str1
62
通常オブジェクトからMapオブジェクトを生成する方法は?
オブジェクトのキー/値のペアの配列を、その形式で返す組み込みのメソッド Object.entries(obj) を使用できます。 なので、次のようにオブジェクトから map の初期化をすることができます: let obj = { name: "John", age: 30 }; let map = new Map(Object.entries(obj)); alert( map.get('name') ); // John ここで、Object.entries はキー/値のペアの配列を返します: [ ["name","John"], ["age", 30] ]。これは Map が必要とするものです。
63
逆に、Mapからオブジェクトに変換する方法は?
Map から通常のオブジェクトを取得する際に Object.fromEntries が使えます。 これは、キー/値ペアの配列からオブジェクトを生成する。 map.entries()は、マップからキー/値ペアの配列を返すので、これをObject.fromEntriesで読み込むと、オブジェクトになる。 例 let map = new Map(); map.set('banana', 1); map.set('orange', 2); map.set('meat', 4); let obj = Object.fromEntries(map.entries()); // マップからキー/値ペアの配列を生成し、通常のオブジェクトを作成します (*) // 完了! // obj = { banana: 1, orange: 2, meat: 4 } alert(obj.orange); // 2 また、行 (*) をより短くすることもできます: let obj = Object.fromEntries(map); これでもいい。 なぜなら、Object.fromEntries は引数に反復可能なオブジェクトを期待するからです。つまり配列である必要はありません。 そして、map の標準のイテレーションは map.entries() と同じキー/値を返します。 したがって、map と同じキー/値を持つプレーンなオブジェクトが取得できます。
64
Setオブジェクトとはなにか?
Set は、特別タイプのコレクションで、 キーなしで、値のみ。 値は一意という特徴がある Set のイテレーションは常に挿入順で行われます let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // 訪問、何度も来るユーザもいます set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // set はユニークな値のみをキープします alert( set.size ); // 3 for (let user of set) { alert(user.name); // John (そして Pete と Mary) }
65
Setの主なメソッド、プロパティは?
new Set(iterable) – set を作ります。オプションで値の配列(任意の iterable が指定可能)からも可能です。 set.add(value) – 値を追加し、 set 自身を返します。 set.delete(value) – 値を削除し、value が呼び出し時に存在すれば true, そうでなければ false を返します。 set.has(value) – set の中に値が存在すれば true を返し、それ以外は false です。 set.clear() – set から全てを削除します。 set.size – set の要素数です。
66
Setでループの時に利用するメソッドは、何が意識されているか?
Mapとの互換性が意識されている。 主なメソッドは以下の通り set.keys() – 値に対する iterable なオブジェクトを返します。※キーではないので注意 set.values() – set.keys と処理内容は同じ、Map との互換性のために作られたもの。 set.entries() – [value, value] のエントリのための iterable なオブジェクトを返します。Map の互換性のために存在します。 Set の中の forEach 関数もMapとの互換性が意識されており、 3つの引数を持っています: 値(value), 次に 再び値(valueAgain), 次にターゲットのオブジェクトです。実際、引数には同じ値が2回出現します。 let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // forEach と同じ: set.forEach((value, valueAgain, set) => { alert(value); });
67
WeakMapオブジェクトについて説明せよ
オブジェクトをキーとして管理し、 キーは到達可能性を保有しないことが特徴。 WeakMap のキーはプリミティブな値ではなくオブジェクト限定。 オブジェクトをキーとして使用し、そのオブジェクトへの参照がWeakMap以外になかくなった場合、自動的にメモリ(と map)から削除されます。 let john = { name: "John" }; let weakMap = new WeakMap(); weakMap.set(john, "..."); john = null; // 参照を上書きします // john はメモリから削除されます! 通常のMapであれば、johnにnullが代入されたとしても、 Mapのキーでjohn を参照していので到達可能であり、ガベージコレクションの対象とはならないが WeakMapで扱うキーについては、 到達可能性を保有したことにならない! そのため、ガベージコレクションの対象となる
68
WeakMapがサポートするメソッドにおける制限事項は?
WeakMap は繰り返しと、メソッド keys(), values(), entries() をサポートしません。 そのため、すべてのキーや値を取得する方法はありません。 ガベージコレクションによるクリーンアップのタイミングがわからないので、到達可能性がなくても、まだエンティティか残ってる場合がある。 なので、エンティティが生きてるか消えてるか不定なのでサポートしてない。
69
WeakMapがサポートするメソッドは?
WeakMap は次のメソッドのみを持っています: weakMap.get(key) weakMap.set(key, value) weakMap.delete(key, value) weakMap.has(key)
70
WeakMapはどのようなケースで活用できるか?
ライブラリ側で、メイン側から渡されるオブジェクトを保持する場合、 WeakMapで保持しておくと、 メイン側で参照をなくした時に、ガベージコレクションの対象とさせることができる。 ライブラリ側 📁 visitsCount.js let visitsCountMap = new WeakMap(); // weakmap: user => visits count // 訪問数を増加 function countUser(user) { let count = visitsCountMap.get(user) || 0; //メイン側で渡されるオブジェクトをWeakMapで管理! visitsCountMap.set(user, count + 1); } メイン側 // 📁 main.js let john = { name: "John" }; countUser(john); // 訪問をカウント // 参照をなくす!=ガベージコレクションの対象となる。 john = null;
71
WeakMapを利用することで、どのような機能を実装することができるか?
キャッシュ機能など履歴や実績の管理を実装することができる。 関数からの結果を保持(“キャッシュ”)できるので、同じオブジェクトに対する将来の呼び出しで再利用することができます。 この時WeakMapを利用することによって、キャッシュした値のクリーンアップができるようになる。 // 📁 cache.js let cache = new WeakMap(); // 計算し結果を覚える function process(obj) { //計算済みかどうか判断 if (!cache.has(obj)) { let result = obj /* に対する計算結果 */; cache.set(obj, result); } //計算済みであれば、その時の計算結果を返す return cache.get(obj); } // 📁 main.js let obj = {/* some object */}; let result1 = process(obj); // キャッシュから取得した、記憶された結果が使われます let result2 = process(obj); // ...後ほど、オブジェクトがこれ以上は不要になったとき obj = null; // WeakMap なので cache.size は取得できません // が、0 あるいはすぐに 0 になります // オブジェクトがガベージコレクトされると、キャッシュされたデータも同様に削除されます。
72
WeakSetオブジェクトについて説明せよ
WeakMapのSet版 Set とちがい、WeakSet へはオブジェクトのみ追加できる。 WeakMapと同様に到達可能性を保持しない WeakMap と同様 WeakSet も、イテレーションの欠如と現在のすべてのコンテンツを取得することができない。 例えば、ユーザを WeakSet に追加して、サイトにアクセスしたユーザを追跡できます。: let visitedSet = new WeakSet(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; visitedSet.add(john); // John が訪問 visitedSet.add(pete); // 次に Pete visitedSet.add(john); // John 再び // visitedSet は 2 ユーザいます // John が訪問したかどうかをチェック alert(visitedSet.has(john)); // true // Mary が訪問したかをチェック alert(visitedSet.has(mary)); // false john = null;
73
通常オブジェクトの繰り返し処理で利用できるメソッドはなにか? またMapのメソッドとの違いは?
Mapと同様のものが用意されている。 しかし、メソッドではなく関数であることが異なる。 Object.keys(obj) – キーの配列を返します。 Object.values(obj) – 値の配列を返します。 Object.entries(obj) – [key, value] ペアの配列を返します。 map.keys()のように、obj.keys()ではないことに注意! 関数 Object.keys(obj)形式である。 また、それぞれの戻り値もiterable ではなく、“本当の” Arrayであることに注意
74
なぜObjectの繰り返し用の関数はMapのようにメソッド形式(map.keys()) で用意されなかったか?
主な理由は柔軟性です。JavaScript ではオブジェクトはすべての複雑な構造のベースであることを忘れないでください。 そのため、独自の order.values() メソッドを実装する order という独自のオブジェクトがあるかもしれません。 それでも Object.values(order) を呼ぶことができます。 ちなみに、 Object.* メソッドが単なる iterable ではなく “本当の” 配列オブジェクトを返すことに関しては、主に歴史的な理由らしい
75
オブジェクトで、配列がサポートしている map、filterなどのメソッドを利用するにはどうするか?
それらを適用したい場合は、Object.fromEntries に続いて、Object.entries が使用できます。: Object.entries(obj) を使用して obj からキー/値ペアの配列を取得します。 その配列で、配列のメソッドを使用します。例えば map 結果の配列で Object.fromEntries(array) を使用して、配列をオブジェクトに戻します。 let prices = { banana: 1, orange: 2, meat: 4, }; let doublePrices = Object.fromEntries( // 配列に変換して map を実行、その後 fromEntries でオブジェクトに戻します Object.entries(prices).map(([key, value]) => [key, value * 2]) ); alert(doublePrices.meat); // 8
76
配列を変数に分割する方法?
// 姓名の配列があります let arr = ["John", "Smith"] // 変数へ分割して代入 // 普通のパターン let firstName = arr[0]; let surname = arr[1]; // 上を短く書いたパターン これを分割代入という! let [firstName, surname] = arr; alert(firstName); // John alert(surname); // Smith
77
分割代入で、最初の要素を無視するにはどうするか?
配列の不要な要素は、余分なカンマをつけることで捨てることができます: // 1番目、2番目の要素が不要の場合 let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; alert( title ); // Consul 上のコードでは、最初の2つの要素がスキップされ、3つ目は title に代入され、残りもスキップされています。
78
let [a, b, c] = "abc"; // ["a", "b", "c"] 分割代入の右辺にはどんなオブジェクトが設定可能か? 具体的にどのような動作で代入が行われるか
右辺は任意の反復可能(iterable)に対して動作します …実際には配列だけでなく、任意の反復可能(iterable)に対して使うことができます: let [a, b, c] = "abc"; // ["a", "b", "c"] let [one, two, three] = new Set([1, 2, 3]); 内部的には分割代入は右辺の値に対してイテレーションすることで動作するため、これも動作します。 これは = の右側の値に対して for..of を呼び出し、値を代入するためのシンタックスシュガーの一種です。
79
分割代入がよく使われる例として、オブジェクトのループがある。 具体的にかけ
オブジェクトの key-value をループするのに、分割代入を一緒に使うこともできます: let user = { name: "John", age: 30 }; // key-value のループ for (let [key, value] of Object.entries(user)) { alert(`${key}:${value}`); // name:John, then age:30 }
80
分割代入を使用して、二つの変数を入れ替えるコードを記載しろ
let guest = "Jane"; let admin = "Pete"; // 値を入れかえましょう: guest=Pete, admin=Jane ちなみに右辺は配列を形成してるだけ [guest, admin] = [admin, guest]; alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!) ここでは、2つの変数の一時的な配列を作り、その直後、入れ替えた順番で分割しました。 この方法で2つ以上の変数を入れ替えることも可能です。
81
代入する変数の数よりも配列の要素数のほうが多い場合、“余分な” 項目はどうなるか? 余った項目を一度にすべて取得するにはどうするか?
省略されます。 続く項目もすべて取得したい場合は、3つのドット "..." を使用して “残り” を取得するパラメータを1つ追加します。: 配列で取得される let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; // rest は3つ目の項目からの配列です alert(rest[0]); // Consul alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 restの値は、残りの配列要素の配列です。 rest の代わりに他の変数名を使用できます。その前に3つのドットがあり、分割代入の最後にくるようにしてください。
82
分割代入で右辺の要素数が足りない場合、 左辺の要素はどうなるか? 値が足りない場合に、デフォルト値をどうやって設定するか デフォルト値の設定が動作するタイミングは?
値が足りない場合は、代入されないので、undefinedとなる。 値がなかった場合に “デフォルト” 値を使いたければ、= を使ってデフォルト値を指定することができます: デフォルト値はより複雑な式や関数呼び出しにすることもできます。それらは値が提供されなかったときのみ評価されます。 例えば、ここでは2つのデフォルトに対して prompt 関数を使っていますが、値がなかった場合のみ実行されます: // 姓のプロンプトのみを実行する let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"]; alert(name); // Julius (配列から) alert(surname); // プロンプトが得たもの ↑デフォルト値をの評価は値がなかったときのみ!、
83
オブジェクトで、分割代入は可能か?
let {var1, var2} = {var1:…, var2…} 右辺には、変数に分割したい既存のオブジェクトがあります。 左辺には該当するプロパティの “パターン” を指定します。 単純なケースでは、それは {...} に変数名を並べたものです。 例: let options = { title: "Menu", width: 100, height: 200 }; let {title, width, height} = options; alert(title); // Menu alert(width); // 100 alert(height); // 200 プロパティ options.title, options.width と options.height は、該当する変数に代入されます。
84
オブジェクトの分割代入で 左辺の変数の順序は関係あるか?
順番は関係ありません。これも動作します。: // let {...} 内のプロパティ順を変えた場合 let {height, width, title} = { title: "Menu", height: 200, width: 100 } 同名の変数名でマッピングされている!
85
オブジェクトの分割代入で、 プロパティを別の名前の変数に代入したい場合、例えば、options.width を変数名 w にしたい場合はどうするか?
コロンを使うことでセットすることができます: let options = { title: "Menu", width: 100, height: 200 }; // { 元のプロパティ: ターゲットとなる変数 } let {width: w, height: h, title} = options; // width -> w // height -> h // title -> title alert(title); // Menu alert(w); // 100 alert(h); // 200 コロンは “何を: どこに” を示します。
86
オブジェクトの分割代入で、 左辺の変数名をかえたい、かつデフォルト値を設定したい場合はどうするか?
コロンと等号の両方を組み合わせることもできます。: let options = { title: "Menu" }; let {width: w = 100, height: h = 200, title} = options; alert(title); // Menu alert(w); // 100 alert(h); // 200
87
オブジェクトの分割代入で残りの要素をまとめて代入するには?
配列といっしょ let options = { title: "Menu", height: 200, width: 100 }; // title = title と名前付けられたプロパティ // rest = オブジェクトのプロパティの残り let {title, ...rest} = options; // now title="Menu", rest={height: 200, width: 100} alert(rest.height); // 200 alert(rest.width); // 100
88
以下のコードがエラーとなる理由を述べよ let title, width, height; // この行はエラーです {title, width, height} = {title: "Menu", width: 200, height: 100};
オブジェクトリテラルとして右辺を書いているつもりだが、 JavaScriptによってコードブロックと認識されてしまいエラーとなってしまう。 コードブロックとは、以下のような 文をグループ化するために使われます。 { // a code block let message = "Hello"; // ... alert( message ); } コードブロックではないと JavaScript に示すためには、代入全体を括弧 (...) で囲む必要があります: let title, width, height; // これでOKです ({title, width, height} = {title: "Menu", width: 200, height: 100}); alert( title ); // Menu
89
複雑な配列やオブジェクトの分割代入はどうやるか?
左辺は右辺のパターンにあわせて、 配列なら[] オブジェクトなら{} を利用して分割代入していくことができる。 let options = { size: { width: 100, height: 200 }, items: ["Cake", "Donut"], extra: true }; // わかりやすくするために、複数の行での分割代入 let { size: { // ここにサイズを格納 width, height }, items: [item1, item2], // ここに items を割り当てる title = "Menu" // オブジェクトには存在しない (デフォルト値が使われます) } = options; alert(title); // Menu alert(width); // 100 alert(height); // 200 alert(item1); // Cake alert(item2); // Donut 左辺で言及されていなかった extra を除いた options オブジェクト全体が該当する変数に代入されます。 最終的には、width, height, item1, item2 と、デフォルト値から title を得ます。 代わりに中身を取得しているので、size と items の変数はないことに注意してください。
90
複雑なオブジェクトを分割代入することをなんというか
非構造化
91
非構造化が役立つ例をあげよ
ある関数が多くのパラメータを持っており、ほどんどがオプションであることがあります。特にユーザインタフェースのときに当てはまります。メニューを作る関数を想像してみてください。幅と高さ、タイトル、アイテムのリストなどを持っています。 ここに、良くない関数の書き方があります: function showMenu(title = "Untitled", width = 200, height = 100, items = []) { // ... } 現実の問題の1つは、どうやって引数の順番を覚えるか、です。コードがしっかりドキュメント化されていれば、通常は IDE が助けてくれます。しかし、他にも問題があります。ほとんどのパラメータがデフォルトでOKの場合の関数の呼び方です。 こうなりますか? // デフォルト値で良い場合は undefined にする showMenu("My Menu", undefined, undefined, ["Item1", "Item2"]) 非常に扱いづらい。。 このようなケースで非構造化を利用する! オブエジェクトとしてパラメータを渡し、関数はそれらを変数に分解します: let options = { title: "My menu", items: ["Item1", "Item2"] }; function showMenu({ title = "Untitled", width: w = 100, // width は w に height: h = 200, // height は h に items: [item1, item2] // items の最初の要素は item1 へ、次は item2 へ }) { alert( `${title} ${w} ${h}` ); // My Menu 100 200 alert( item1 ); // Item1 alert( item2 ); // Item2 } showMenu(options);
92
以下のコードではメソッド呼び出しの時に引数を設定していない。 これを実行するとどうなるか? function showMenu({ title = "Untitled", width: w = 100, // width は w に height: h = 200, // height は h に items: [item1, item2] // items の最初の要素は item1 へ、次は item2 へ }) { alert( `${title} ${w} ${h}` ); // My Menu 100 200 alert( item1 ); // Item1 alert( item2 ); // Item2 } showMenu();
分割代入は右辺になにもないとエラーとなる。 分割代入は showMenu() に引数があることを前提にしている点に注意してください。もしすべての値をデフォルトにしたい場合には、空のオブジェクトを指定する必要があります: showMenu({}); // OK, すべての値はデフォルト値になります showMenu(); // これはエラーになります
93
以下のコードについて、エラーとならないようにメソッドをかきかえよ function showMenu({ title = "Untitled", width: w = 100, // width は w に height: h = 200, // height は h に items: [item1, item2] // items の最初の要素は item1 へ、次は item2 へ }) { alert( `${title} ${w} ${h}` ); // My Menu 100 200 alert( item1 ); // Item1 alert( item2 ); // Item2 } showMenu();
非構造化対象全体のデフォルト値に {} を指定することで対応することができます: function showMenu({ title = "Menu", width = 100, height = 200 } = {}) { alert( `${title} ${width} ${height}` ); } showMenu(); // Menu 100 200 上のコードでは、全体の引数オブジェクトがデフォルトで {} なので常に分解する何かがあります。
94
Dateオブジェクトを生成するパターンをかけ
new Date() 引数なし – 現在の日付と時刻で Date オブジェクトを作ります: new Date(milliseconds) Jan 1st of 1970 UTC+0 (1970年 1月1日 UTC+0) からの経過したミリ秒(秒の1/1000)に等しい時間をもつ Date オブジェクトを作ります。 new Date(datestring) 1つの引数でそれが文字列の場合、自動でパースされます。Date.parse アルゴリズム(後述)でパースされます。 new Date(year, month, date, hours, minutes, seconds, ms) ローカルタイムゾーンで、与えられた要素で日付を作成します。最初の2つの引数は必須です。
95
1970年初めから経過したミリ秒の数値は なんとよばれるか?
タイムスタンプ
96
Dateのgetメソッドは色々あるが、その共通点をあげよ 例 getFullYear()※getYear()は非推奨 年を取得します(4桁) getMonth() 月を取得します, 0から11。
ローカルタイムゾーンで取得する タイムゾーン UTC+0 の日、月、年などを返す、UTCカウンターパートもあります:getUTCFullYear(), getUTCMonth(), getUTCDay().
97
UTC版メソッドをもたないDateのgetメソッドを二つあげよ
getTime() 日付のタイムスタンプを返します – それは、1970年 UTC+0 の 1月1日からの経過ミリ秒です。 getTimezoneOffset() ローカルタイムゾーンとUTCの差を、分で返します: // タイムゾーン UTC-1 にいる場合、60 を出力 // タイムゾーン UTC+3 にいる場合、-180 を出力 alert( new Date().getTimezoneOffset() );
98
以下ような日付範囲外のパラメータでDateを作成したとき、結果はどうなるか? また、どのような機能が動いているか? let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!? alert(date);
自動補正 は Date オブジェクトのとても便利な機能です。私たちが範囲外の値を指定した場合、それは自動的に調節されます。 2013/2/1が出力される。 はみ出した分は自動補正によって、計算される。 つまり、32日が自動補正によってくりあがり、次の月の次の日に補正された。 また、ゼロや負値をセットすることもできます。例えば: let date = new Date(2016, 0, 2); // 2 Jan 2016 date.setDate(1); // 月の1日を設定します alert( date ); date.setDate(0); // 0日はない。最小日は1なので、先月の最後の日になります alert( date ); // 31 Dec 2015
99
Dateオブジェクトが数値に変換されると何が返されるか?
Date オブジェクトが数値へ変換されるとき、date.getTime() と同じようにタイムスタンプになります: let date = new Date(); alert(+date); // ミリ秒の数値です, date.getTime() と同じです
100
Date型同士の差分を取得したい場合はどうするか?
現在のタイムスタンプを返す特別なメソッド Date.now() があります。 これは、意味的には new Date().getTime() と同じですが、中間の Date オブジェクトを作らないため、より速く、ガベージコレクションに負荷をかけません。 これはおそらくベターです: let start = Date.now(); // 1 Jan 1970 からのミリ秒 // なにかする for (let i = 0; i < 100000; i++) { let doSomething = i * i * i; } let end = Date.now(); // 終了 alert( `The loop took ${end - start} ms` ); // 日付ではなく、数値を減算する