私は毎日、JavaScriptに関する選択問題を Instagramに投稿していますが、ここにも投稿します。
初級から上級まで: JavaScriptの知識のテストを行ったり、知識を少し深めたり、コーディング面接の準備をしてください。:muscle: :rocket: 私はこのレポを毎週新しい質問で更新します。Last update: June 29th
答えは質問の下の折りたたまれたセクションにあります、クリックすればそれを広げられます。幸運を祈ります。:heart:
利用可能な言語リスト:
- 🇸🇦 العربية
- 🇪🇬 اللغة العامية
- 🇧🇦 Bosanski
- 🇩🇪 Deutsch
- 🇬🇧 English
- 🇪🇸 Español
- 🇫🇷 Français
- 🇮🇩 Indonesia
- 🇰🇷 한국어
- 🇳🇱 Nederlands
- 🇧🇷 Português Brasil
- 🇷🇺 Русский
- 🇹🇭 ไทย
- 🇹🇷 Türkçe
- 🇺🇦 Українська мова
- 🇻🇳 Tiếng Việt
- 🇨🇳 简体中文
- 🇹🇼 繁體中文
function sayHi() {
console.log(name);
console.log(age);
var name = "Lydia";
let age = 21;
}
sayHi();
- A:
Lydia
とundefined
- B:
Lydia
とReferenceError
- C:
ReferenceError
と21
- D:
undefined
とReferenceError
答え
関数内で、まず var
キーワードを使って name
変数を宣言します。これは、変数が定義されている行に実際に到達するまで、変数がデフォルト値の undefined
で初期化される(作成時にメモリ空間が設定される)ことを意味します。
name
変数をログ出力を実行している行では、まだ変数を定義していませんので、undefined
の値を保持しています。
let
キーワード(またはconst
)を持つ変数は持ち上げられますが、 var
とは異なり、初期化されません。それらを宣言(初期化)する行の前にはアクセスできません。これは"temporal dead zone"と呼ばれます。
宣言される前に変数にアクセスしようとすると、JavaScriptは ReferenceError
を投げます。
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
- A:
0 1 2
と0 1 2
- B:
0 1 2
と3 3 3
- C:
3 3 3
と0 1 2
答え
JavaScriptのイベントキューのため、setTimeout
コールバック関数はループが実行された後に呼び出されます。最初のループの変数 i
はvar
キーワードを使って宣言されているので、この値はグローバル変数となります。ループの間、単項演算子 ++
を使用して、毎回 i
の値を1
ずつインクリメントしました。 最初の例では setTimeout
コールバック関数が呼び出されるまでにi
は3
となりました。
2番目のループでは、変数 i
が let
キーワードを使って宣言されました。 let
(またはconst
)キーワードで宣言された変数はブロックスコープです(ブロックは {}
の間のものです)。それぞれの繰り返しの間、 i
は新しい値を持ち、それぞれの値はループの内側にあります。
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius
};
shape.diameter();
shape.perimeter();
- A:
20
と62.83185307179586
- B:
20
とNaN
- C:
20
と63
- D:
NaN
と63
答え
diameter
の値は正則関数であり、perimeter
の値はアロー関数です。
アロー関数では、this
キーワードは通常の関数とは異なり、現在の周囲の範囲を参照します。これは、perimeter
関数を呼ぶと、shapeオブジェクトではなく、その周囲の範囲(例えば window)を参照することを意味します。
そのオブジェクトにはradius
という値はなく、undefined
を返します。
+true;
!"Lydia";
- A:
1
とfalse
- B:
false
とNaN
- C:
false
とfalse
答え
単項プラスは、オペランドを数値に変換しようとします。true
は1
、false
は0
です
文字列「Lydia」は truthy valueです。ここで求めているのは、「このtruthy valueは、falsyなのか」ということです。これは false
を返します。
const bird = {
size: "small"
};
const mouse = {
name: "Mickey",
small: true
};
- A:
mouse.bird.size
is not valid - B:
mouse[bird.size]
is not valid - C:
mouse[bird["size"]]
is not valid - D: これらすべて有効です
答え
JavaScriptでは、すべてのオブジェクトキーは文字列です(Symbolでない限り)。たとえそれを文字列として入力していなくても、それらは常にフードの下で文字列に変換されます。
JavaScriptは、ステートメントを解釈(または、ボックス解除)します。大括弧表記を使用すると、最初の左大括弧 [
を見て、右大括弧 ]
が見つかるまで進みます。その時だけ、そのステートメントを評価します。
mouse [bird.size]
: まず最初に、bird.size
が評価されます。これは文字列の "small"
となります。 mouse["small"]
は、true
を返します。
しかし、ドット表記では、これは起こりません。 mouse
はbird
と呼ばれるキーを持っていません。 つまりmouse.bird
はundefined
となります。
また、ドット表記を使って size
を求めます: mouse.bird.size
。 mouse.birdは未定義なので、実際にはundefined.sizeを要求しています。これは有効ではないので、Cannot read property "size" of undefined
ような、エラーをスローします。
let c = { greeting: "Hey!" };
let d;
d = c;
c.greeting = "Hello";
console.log(d.greeting);
- A:
Hello
- B:
Hey
- C:
undefined
- D:
ReferenceError
- E:
TypeError
答え
JavaScriptでは、すべてのオブジェクトは互いに等しく設定すると参照によって相互作用します。
まず、変数c
は、オブジェクトに対する値を保持します。その後、c
オブジェクトに対して持っている値と同じ参照でd
に代入します。
1つのオブジェクトを変更すると、それらすべてが変更されます。
let a = 3;
let b = new Number(3);
let c = 3;
console.log(a == b);
console.log(a === b);
console.log(b === c);
- A:
true
false
true
- B:
false
false
true
- C:
true
false
false
- D:
false
true
true
答え
new Number()
は、組み込み関数のコンストラクタです。数字のように見えますが、実際には数字ではありません。たくさんの追加機能があり、それはオブジェクトとなります。
==
演算子を使うとき、同じ値を持っているかどうか? をチェックするだけとなります。それらは両方とも3
の値を持っているので、それはtrue
を返します。
しかし、===
演算子を使う時は、値と型は同じであるべきです。 そうでないので: new Number()
は数値ではなく、オブジェクトとなります。なので、両方ともfalseを返します。
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
return this.newColor;
}
constructor({ newColor = "green" } = {}) {
this.newColor = newColor;
}
}
const freddie = new Chameleon({ newColor: "purple" });
freddie.colorChange("orange");
- A:
orange
- B:
purple
- C:
green
- D:
TypeError
答え
colorChange
関数は静的です。静的メソッドは、それらが作成されたコンストラクタ上でのみ動作するように設計されており、どの子達にも受け継がれません。 freddie
は子となりますので、この関数は受け継がれず、freddie
インスタンスでは利用できません。
その結果、TypeError
が投げられます。
let greeting;
greetign = {}; // Typo!
console.log(greetign);
- A:
{}
- B:
ReferenceError: greetign is not defined
- C:
undefined
答え
グローバルオブジェクトに、空のオブジェクトを作成したばかりなので、オブジェクトはログ出力されます。greeting
をgreetign
と誤って入力した場合、JSインタプリタは実際にこれを global.greetign = {}
(またはブラウザの window.greetign = {}
)と見なします。
これを避けるために、"use strict"を使用する事ができます。これにより、変数を何かに設定する前に、変数宣言したことを確認できます。
function bark() {
console.log("Woof!");
}
bark.animal = "dog";
- A: 何も起こらない、これは全く問題ない!
- B:
SyntaxError
. この方法で関数にプロパティを追加することはできません。 - C:
undefined
- D:
ReferenceError
答え
関数はオブジェクトとなるので、これはJavaScriptで可能です。(プリミティブ型以外はすべてオブジェクトです。)
関数は特別な種類のオブジェクトです。自分で書いたコードは実際の機能ではありません。関数はプロパティを持つオブジェクトです。よって、このプロパティは呼び出し可能となります。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = function() {
return `${this.firstName} ${this.lastName}`;
};
console.log(member.getFullName());
- A:
TypeError
- B:
SyntaxError
- C:
Lydia Hallie
- D:
undefined
undefined
答え
通常のオブジェクトのようにコンストラクタにプロパティを追加することはできません。一度にすべてのオブジェクトに機能を追加したい場合は、代わりにプロトタイプを使用する必要があります。だからこの場合は、
Person.prototype.getFullName = function() {
return `${this.firstName} ${this.lastName}`;
};
で、member.getFullName()
が、機能するはずです。これはなぜ有益なのでしょうか。例えば、このメソッドをコンストラクタ自体に追加したとします。すべてのPerson
インスタンスがこのメソッドを必要としなかったのかもしれません。
その場合、多くのメモリスペースを浪費する事でしょう。なぜならそれらはまだその特性を持ち、それは各インスタンスのためにメモリスペースを消費するからです。
その代わりに、プロトタイプに追加するだけであれば、メモリ内の1箇所に配置するだけで、すべてのユーザーがアクセスできます。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const lydia = new Person("Lydia", "Hallie");
const sarah = Person("Sarah", "Smith");
console.log(lydia);
console.log(sarah);
- A:
Person {firstName: "Lydia", lastName: "Hallie"}
とundefined
- B:
Person {firstName: "Lydia", lastName: "Hallie"}
とPerson {firstName: "Sarah", lastName: "Smith"}
- C:
Person {firstName: "Lydia", lastName: "Hallie"}
と{}
- D:
Person {firstName: "Lydia", lastName: "Hallie"}
とReferenceError
答え
sarah
では、new
キーワードを使いませんでした。new
を使用した場合、作成した新しい空のオブジェクトを参照します。しかし、new
を追加しなければ、それはグローバルオブジェクトを参照することとなります。
this.firstName
に"Sarah"
を代入、this.lastName
に"Smith"
を代入したつもりでしたが、実際に行った事は、global.firstName = 'Sarah'
と、global.lastName = 'Smith'
を定義したのです。
sarah
自体は undefined
のままです。
- A: Target > Capturing > Bubbling
- B: Bubbling > Target > Capturing
- C: Target > Bubbling > Capturing
- D: Capturing > Target > Bubbling
- A: true
- B: false
答え
基本オブジェクトを除き、すべてのオブジェクトにプロトタイプがあります。ベースオブジェクトは.toString
のようないくつかのメソッドとプロパティにアクセスできます。
これが、組み込みのJavaScriptメソッドを使用できる理由です。このような方法はすべてプロトタイプで利用できます。
JavaScriptはそれをあなたのオブジェクト上で直接見つけることはできませんが、プロトタイプチェーンをたどり、見つけます。
function sum(a, b) {
return a + b;
}
sum(1, "2");
- A:
NaN
- B:
TypeError
- C:
"12"
- D:
3
答え
JavaScriptは、動的に型付けされた言語です。: 特定の変数がどんな型であるかは指定しません。知らないうちに、値が自動的に別の型に変換されることがあります。この事をimplicit type coercion
と呼ばれてます。 Coercionは、ある型から別の型に変換しています。
この例では、関数が意味を成して値を返すために、JavaScriptは数字の1
を文字列に変換します。数値型(1
)と 文字列型('2'
)の追加中は、数字は文字列として扱われます。
"Hello"+"World"
のように文字列を連結することができるので、ここで起こっているのは"1"+"2"
で、これは "12"
を返します。
let number = 0;
console.log(number++);
console.log(++number);
console.log(number);
- A:
1
1
2
- B:
1
2
2
- C:
0
2
2
- D:
0
1
2
答え
接尾辞 単項演算子 ++
:
1.値を返す(これは0
を返す)
2.値を増やす(numberは現在1
です)
接頭辞 単項演算子 ++
:
1.値を増やす(数値は2になります)
2.値を返す(これは2
を返します)
これは0 2 2
を返します。
function getPersonInfo(one, two, three) {
console.log(one);
console.log(two);
console.log(three);
}
const person = "Lydia";
const age = 21;
getPersonInfo`${person} is ${age} years old`;
- A:
"Lydia"
21
["", " is ", " years old"]
- B:
["", " is ", " years old"]
"Lydia"
21
- C:
"Lydia"
["", " is ", " years old"]
21
function checkAge(data) {
if (data === { age: 18 }) {
console.log("You are an adult!");
} else if (data == { age: 18 }) {
console.log("You are still an adult.");
} else {
console.log(`Hmm.. You don't have an age I guess`);
}
}
checkAge({ age: 18 });
- A:
You are an adult!
- B:
You are still an adult.
- C:
Hmm.. You don't have an age I guess
答え
等価性をテストするとき、プリミティブはそれらの値によって比較され、オブジェクトはそれらの参照によって比較されます。 JavaScriptは、オブジェクトがメモリ内の同じ場所への参照を持っているかどうかを確認します。
比較している2つのオブジェクトにはそれがありません。パラメータとして渡したオブジェクトが、等価性を確認するために使用したオブジェクトとは異なるメモリ内の場所を参照しています。
これが { age: 18 } === { age: 18 }
と、{ age: 18 } == { age: 18 }
の両方が、false
を返す理由です。
function getAge(...args) {
console.log(typeof args);
}
getAge(21);
- A:
"number"
- B:
"array"
- C:
"object"
- D:
"NaN"
function getAge() {
"use strict";
age = 21;
console.log(age);
}
getAge();
- A:
21
- B:
undefined
- C:
ReferenceError
- D:
TypeError
答え
"use strict"
を使うと、誤ってグローバル変数を宣言しないようにすることができます。変数age
を宣言したことは一度もありませんし、"use strict"
を使っているので参照エラーになります。
"use strict"
を使用しなかった場合は、プロパティage
がグローバルオブジェクトに追加されたことになるので、それは機能します。
const sum = eval("10*10+5");
- A:
105
- B:
"105"
- C:
TypeError
- D:
"10*10+5"
sessionStorage.setItem("cool_secret", 123);
- A: 永遠に、データが失われることはありません。
- B: ユーザーがタブを閉じる時
- C: ユーザーがタブだけでなくブラウザ全体を閉じる時。
- D: ユーザーが自分のコンピュータをシャットダウンした時。
答え
sessionStorage
に格納されたデータは、タブを閉じた後に削除されます。
localStorage
を使用した場合は、localStorage.clear()
などが呼び出されない限り、データは永久に存在しているでしょう。
var num = 8;
var num = 10;
console.log(num);
- A:
8
- B:
10
- C:
SyntaxError
- D:
ReferenceError
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
- A:
false
true
false
true
- B:
false
true
true
true
- C:
true
true
false
true
- D:
true
true
true
true
答え
すべてのオブジェクトキー(Symbolsを除く)は、文字列として自分で入力しなくても、内部では文字列です。これが、obj.hasOwnProperty('1')
もtrueを返す理由です。
setではそうはいきません。上記のsetには'1'
はありません: set.has('1')
は、false
を返します。数値型1
のset.has(1)
は、true
を返します。
const obj = { a: "one", b: "two", a: "three" };
console.log(obj);
- A:
{ a: "one", b: "two" }
- B:
{ b: "two", a: "three" }
- C:
{ a: "three", b: "two" }
- D:
SyntaxError
- A: true
- B: false
- C: 場合によりけり
for (let i = 1; i < 5; i++) {
if (i === 3) continue;
console.log(i);
}
- A:
1
2
- B:
1
2
3
- C:
1
2
4
- D:
1
3
4
String.prototype.giveLydiaPizza = () => {
return "Just give Lydia pizza already!";
};
const name = "Lydia";
name.giveLydiaPizza();
- A:
"Just give Lydia pizza already!"
- B:
TypeError: not a function
- C:
SyntaxError
- D:
undefined
答え
String
はプロパティを追加することができる組み込みコンストラクタです。プロトタイプにメソッドを追加しました。
プリミティブ文字列は、文字列プロトタイプ関数によって生成された文字列オブジェクトに自動的に変換されます。
つまり、すべての文字列(文字列オブジェクト)がそのメソッドにアクセスできます。
const a = {};
const b = { key: "b" };
const c = { key: "c" };
a[b] = 123;
a[c] = 456;
console.log(a[b]);
- A:
123
- B:
456
- C:
undefined
- D:
ReferenceError
答え
オブジェクトキーは自動的に文字列に変換されます。オブジェクトaのキーとして、値123で設定しようとしています。
しかし、オブジェクトを文字列化すると、それは"[object Object]"
になってしまいます。なので、ここで行っているのは、 a["object Object"] = 123
です。
その後、同じことをもう一度試みています。c
は暗黙のうちに文字列化している別のオブジェクトです。そのため、a["object Object"] = 456
となります。
その後、a[b]
でログ出力。実際にはa["object Object"]
です。これを 456
に設定しただけなので、456
を返します。
const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"));
const baz = () => console.log("Third");
bar();
foo();
baz();
- A:
First
Second
Third
- B:
First
Third
Second
- C:
Second
First
Third
- D:
Second
Third
First
答え
setTimeout
関数があり、それを最初に呼び出したのですが、それは最後にログ出力されました。
これは、ブラウザにはランタイムエンジンがあるだけでなく、WebAPI
と呼ばれるものもあるからです。WebAPI
は最初にsetTimeout
関数を与えてくれます。例えばDOMです。
callbackがWebAPIにプッシュされた後、setTimeout
関数自体(コールバックではありません!)がスタックからポップされます。
今、foo
が呼び出され、"First"
が、ログ出力されています。
foo
がスタックからポップされ、baz
が呼び出されます。"Third"
が、ログ出力されます。
WebAPIは、準備が整ったときにスタックに、なにかを追加することはできません。代わりに、コールバック関数をqueue
と呼ばれるものにプッシュします。
event loopが機能し始めるところです。 event loopはスタックとタスクキューを調べます。スタックが空の場合は、キューの最初のものを取り出し、それをスタックにプッシュします。
bar
が呼び出され、"Second"
がログ出力され、スタックからポップされます。
<div onclick="console.log('first div')">
<div onclick="console.log('second div')">
<button onclick="console.log('button')">
Click!
</button>
</div>
</div>
- A: 外側
div
- B: 内側
div
- C:
button
- D: ネストしたすべての要素の配列
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
- A:
p
div
- B:
div
p
- C:
p
- D:
div
答え
p
をクリックすると、p
とdiv
の2つのログが表示されます。イベント伝播中は、キャプチャ、ターゲット、バブリングの3つのフェーズがあります。
デフォルトでは、イベントハンドラはバブリング段階で実行されます(useCapture
をtrue
に設定しない限り)。最も深くネストした要素から外側に向かって進みます。
const person = { name: "Lydia" };
function sayHi(age) {
console.log(`${this.name} is ${age}`);
}
sayHi.call(person, 21);
sayHi.bind(person, 21);
- A:
undefined is 21
Lydia is 21
- B:
function
function
- C:
Lydia is 21
Lydia is 21
- D:
Lydia is 21
function
答え
両方とも、this
キーワードが参照したいオブジェクトを渡すことができます。しかし、.call
もすぐに実行されます。
.bind.
は関数のコピーを返しますが、コンテキストは束縛されています。すぐには実行されません。
function sayHi() {
return (() => 0)();
}
typeof sayHi();
- A:
"object"
- B:
"number"
- C:
"function"
- D:
"undefined"
答え
sayHi
関数は、即時呼び出し関数式(IIFE)の戻り値を返します。この関数は0
を返しました。それは"number"
型です。
参考:7つの組み込み型しかありません: null
, undefined
, boolean
, number
, string
, object
, symbol
, そして bigint
。関数はオブジェクトなので、"function"
型ではなく"object"
型です。
0;
new Number(0);
("");
(" ");
new Boolean(false);
undefined;
- A:
0
,''
,undefined
- B:
0
,new Number(0)
,''
,new Boolean(false)
,undefined
- C:
0
,''
,new Boolean(false)
,undefined
- D: これらすべてfalsy
答え
falsyの値は6つだけです。
undefined
null
NaN
0
''
(empty string)false
new Number
や、new Boolean
のような関数コンストラクタはtruthyです。
console.log(typeof typeof 1);
- A:
"number"
- B:
"string"
- C:
"object"
- D:
"undefined"
const numbers = [1, 2, 3];
numbers[10] = 11;
console.log(numbers);
- A:
[1, 2, 3, 7 x null, 11]
- B:
[1, 2, 3, 11]
- C:
[1, 2, 3, 7 x empty, 11]
- D:
SyntaxError
答え
配列の長さを超える値を配列内の要素に設定すると、JavaScriptでは、"empty slots"と呼ばれるものを作成します。これらは実際には、undefined
の値を持ちますが、あなたは以下のようなものを見るでしょう
[1, 2, 3, 7 x empty, 11]
実行場所によって異なります(browser、nodeなどによって異なります)。
(() => {
let x, y;
try {
throw new Error();
} catch (x) {
(x = 1), (y = 2);
console.log(x);
}
console.log(x);
console.log(y);
})();
- A:
1
undefined
2
- B:
undefined
undefined
undefined
- C:
1
1
2
- D:
1
undefined
undefined
答え
catch
ブロックは引数x
を受け取ります。これは引数を渡すときの変数と同じx
ではありません。この変数x
はブロックスコープです。
後に、このブロックスコープ変数を1
に設定し、変数y
の値を設定します。ここで、ブロックスコープ変数x
をログ出力します。これは1
となります。
catch
ブロック以外では、x
は未定義、y
は2です。 catch
ブロックの外側でconsole.log(x)
した場合は、undefined
を返し、y
は2
を返します。
- A: primitive か object
- B: function か object
- C: ひっかけ問題! objectsのみ
- D: number か object
答え
JavaScriptにはプリミティブ型とオブジェクトしかありません。
プリミティブ型は、boolean
, null
, undefined
, bigint
, number
, string
, そしてsymbol
です。
プリミティブとオブジェクトを区別するのは、プリミティブにはプロパティもメソッドもないということです。
ただし、'foo'.toUpperCase()
は'FOO'
と評価され、TypeError
にはなりません。これは、文字列のようなプリミティブのプロパティやメソッドにアクセスしようとすると、JavaScriptがラッパークラスの1つ、すなわちString
を使ってオブジェクトを暗黙的にラップし、式が評価された後ラッパーを直ちに破棄するためです。
null
とundefined
を除くすべてのプリミティブはこの振る舞いをします。
[[0, 1], [2, 3]].reduce(
(acc, cur) => {
return acc.concat(cur);
},
[1, 2]
);
- A:
[0, 1, 2, 3, 1, 2]
- B:
[6, 1, 2]
- C:
[1, 2, 0, 1, 2, 3]
- D:
[1, 2, 6]
答え
[1,2]
は初期値です。これが最初の値で、一番最初のacc
の値です。最初の周回の間、acc
は[1,2]
で、cur
は[0,1]
です。それらを連結すると、結果として[1、2、0、1]
となります。
そして、[1, 2, 0, 1]
のacc
と[2, 3]
のcur
を連結して[1, 2, 0, 1, 2, 3]
を得ます
!!null;
!!"";
!!1;
- A:
false
true
false
- B:
false
false
true
- C:
false
true
true
- D:
true
true
false
答え
null
はfalsyです。!null
はtrue
を返します。!true
はfalse
を返します。
""
はfalsyです。!""
はtrue
を返します。!true
はfalse
を返します。
1
はtruthyです。!1
はfalse
を返します。!false
はtrue
を返します。
setInterval(() => console.log("Hi"), 1000);
- A: ユニークid
- B: 指定されたミリ秒数
- C: 渡された関数
- D:
undefined
[..."Lydia"];
- A:
["L", "y", "d", "i", "a"]
- B:
["Lydia"]
- C:
[[], "Lydia"]
- D:
[["L", "y", "d", "i", "a"]]
function* generator(i) {
yield i;
yield i * 2;
}
const gen = generator(10);
console.log(gen.next().value);
console.log(gen.next().value);
- A:
[0, 10], [10, 20]
- B:
20, 20
- C:
10, 20
- D:
0, 10 and 10, 20
答え
通常の関数は、呼び出し後に途中で停止することはできません。ただし、ジェネレータ関数は途中で"停止"し、後で停止した場所から続行することができます。
ジェネレータ関数がyield
キーワードを見つけるたびに、その関数はその後に指定された値を返します。その場合のジェネレータ関数は、値を"返す"わけではないことに注意してください。値を生み出しています。
まず、i
に10
を指定してジェネレータ関数を初期化します。次にnext()
メソッドを使用してジェネレータ関数を呼び出します。
最初にジェネレータ関数を呼び出すと、i
は10
になり、最初のyield
キーワードに遭遇します。そこからi
の値が得られます。ジェネレータは"一時停止"され、10
がログ出力されます。
それから、next()
メソッドを使って関数を再度呼び出します。依然としてi
は10
のまま、以前に停止したところから継続し始めます。
それから次のyield
キーワードに遭遇し、そこからi * 2
の値が得られます。i
は10
のままなので、10 * 2
、つまり20
を返します。なので、10、20
が返る事になります。
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
- A:
"one"
- B:
"two"
- C:
"two" "one"
- D:
"one" "two"
答え
複数のプロミスをPromise.race
メソッドに渡した時、"resolves/rejects"は、"最初"のプロミスの"resolves/rejects"を行います。
setTimeout
メソッドには、タイマーを渡します: 最初のプロミスには500ms(firstPromise
)、2番目のプロミスには100ms(secondPromise
)。
これは、secondPromise
が最初に'two'
の値で解決されることを意味します。res
は'two'
の値を保持するようになり、ログ出力されます。
let person = { name: "Lydia" };
const members = [person];
person = null;
console.log(members);
- A:
null
- B:
[null]
- C:
[{}]
- D:
[{ name: "Lydia" }]
答え
まず、name
プロパティを持つオブジェクトの値を使って、変数person
を宣言します。
それから、members
という変数を宣言します。その配列の最初の要素に、変数person
の値を代入します。オブジェクトは、互いをイコールで設定すると、「参照」によって相互作用します。
ある変数から別の変数への"参照"を代入すると、その参照の"コピー"が作成されます。 (それらは、"同じ参照"を持っていないことに注意してください!)
そして、変数person
をnull
に設定します。
その要素はオブジェクトへの異なる(コピーされた)参照を持っているので、person
変数の値を変更するだけで配列の最初の要素は変更されません。 members
の最初の要素はまだ元のオブジェクトへの参照を保持しています。
members
配列をログ出力したとき、最初の要素はまだオブジェクトの値を保持しているので、それがログ出力されます。
const person = {
name: "Lydia",
age: 21
};
for (const item in person) {
console.log(item);
}
- A:
{ name: "Lydia" }, { age: 21 }
- B:
"name", "age"
- C:
"Lydia", 21
- D:
["name", "Lydia"], ["age", 21]
答え
この場合、for-in
ループを使うと、オブジェクトキーであるname
とage
の繰り返し処理できます。内部的には、オブジェクトキーは文字列です(シンボルではない場合)。
すべてのループで、item
の値は反復している現在のキーに設定されます。まず、item
はname
が代入され、ログに出力されます。その後、item
はage
が代入され、ログに出力されます。
console.log(3 + 4 + "5");
- A:
"345"
- B:
"75"
- C:
12
- D:
"12"
答え
演算子結合性は、コンパイラーが式を評価する順序(左から右または右から左)となります。これは、すべての演算子が同じ優先順位を持つ場合にのみ発生します。演算子の種類は1つだけです: +
。さらに、結合性は左から右です。
3 + 4
が最初に評価されます。これは数字の7
になります。
7 + '5'
は、強制的に"75"
になります。 JavaScriptでは、数字の7
を文字列に変換します。質問15を参照してください。2つの文字列を演算子の+
を使って連結することができます。よって、"7" + "5"
は、"75"
になります。
const num = parseInt("7*6", 10);
- A:
42
- B:
"42"
- C:
7
- D:
NaN
答え
文字列の最初の数字だけが返されます。"基数"(解析する数値の種類を指定するための2番目の引数: 基数10, 16進数, 8進数, 2進数など)に基づいて、parseInt
は文字列内の文字が有効かどうかをチェックします。基数の中で有効な数字ではない文字に出会うと、構文解析を停止して次の文字を無視します。
*
は、有効な数字ではありません。"7"
を、10進数の7
に解析するだけです。そのままnumは7
の値を保持します。
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});
- A:
[]
- B:
[null, null, null]
- C:
[undefined, undefined, undefined]
- D:
[ 3 x empty ]
答え
配列をマッピングするとき、num
の値に代入されるのは、ループで渡ってくる要素となります。この場合、要素は数値なので、ifステートメント typeof num === "number"
の条件はtrue
を返します。 map関数は新しい配列を作成して関数から返された値を挿入します。
ただし、値は返されません。関数から値を返さないと、関数はundefined
を返します。配列内のすべての要素に対して関数ブロックが呼び出されるので、各要素に対してundefined
を返します。
function getInfo(member, year) {
member.name = "Lydia";
year = 1998;
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
- A:
{ name: "Lydia" }, "1997"
- B:
{ name: "Sarah" }, "1998"
- C:
{ name: "Lydia" }, "1998"
- D:
{ name: "Sarah" }, "1997"
答え
値がオブジェクトでない限り、引数は"値"によって渡され、その後、"参照"によって渡されます。 birthYear
はオブジェクトではなく文字列なので、値で渡されます。引数を値で渡すと、その値の"コピー"が作成されます(質問46を参照)。
変数birthYear
は、値"1997"
への参照を持ちます。引数year
は、値"1997"
も参照していますが、それはbirthYear
が参照しているのと同じ値ではありません。year
に"1998"
を代入することによってyear
の値を更新したとしても、year
の値を更新するだけです。birthYear
はまだ"1997"
となります。
person
の値はオブジェクトです。引数member
は"同じ"オブジェクトへの(コピーされた)参照を持ちます。
member
が参照を持つオブジェクトのプロパティを変更すると、person
の値も変更されます。これらは両方とも同じオブジェクトへの参照を持つからです。person
のname
プロパティは、値の"Lydia"
となりました。
function greeting() {
throw "Hello world!";
}
function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error!", e);
}
}
sayHi();
- A:
"It worked! Hello world!"
- B:
"Oh no an error: undefined
- C:
SyntaxError: can only throw Error objects
- D:
"Oh no an error: Hello world!
答え
throw
ステートメントを使って、カスタムエラーを作ることができます。このステートメントで、あなたは例外を投げることができます。例外は、string, number, boolean, objectのいずれかとなります。上記の場合だと、例外は文字列'Hello world'
となります。
catch
ステートメントを使って、try
ブロックで例外が投げられた場合にどうするかを指定できます。例外がスローされます: 文字列'Hello world'
は、e
に代入されます。その結果'Oh an error: Hello world'
となります。
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}
const myCar = new Car();
console.log(myCar.make);
- A:
"Lamborghini"
- B:
"Maserati"
- C:
ReferenceError
- D:
TypeError
答え
プロパティを返すと、そのプロパティの値は、コンストラクタ関数で設定された値ではなく、"戻り値"となります。 "Maserati"
という文字列を返すので、myCar.make
は "Maserati"
となります。
(() => {
let x = (y = 10);
})();
console.log(typeof x);
console.log(typeof y);
- A:
"undefined", "number"
- B:
"number", "number"
- C:
"object", "number"
- D:
"number", "undefined"
答え
let x = y = 10;
is actually shorthand for:
y = 10;
let x = y;
y
に10
を代入すると、実際にはグローバルオブジェクトにプロパティy
が追加されます(ブラウザではwindow
、nodeではglobal
)。ブラウザでは、window.y
は10
となりました。
それから、変数x
を10
である値y
で宣言します。let
キーワードで宣言された変数は"ブロックスコープ"となり、宣言されたブロック内でのみ定義されます。この場合は即時関数(IIFE)となります。
typeof
演算子使用時、オペランドx
は定義されていません: 宣言されているブロックの外側でx
にアクセスしようとしています。これはx
が定義されていないことを意味します。
値が割り当てられていない、または宣言されていない値は"undefined"
型となります。なのでconsole.log(typeof x)
は"undefined"
を返します。
yに関しては、y
に10
を代入するときにグローバル変数y
を作成しました。この値は、コード内のどこからでもアクセスできます。y
が定義されていて、"number"
型の値を保持します。よってconsole.log(typeof y)
は"number"
を返します。
class Dog {
constructor(name) {
this.name = name;
}
}
Dog.prototype.bark = function() {
console.log(`Woof I am ${this.name}`);
};
const pet = new Dog("Mara");
pet.bark();
delete Dog.prototype.bark;
pet.bark();
- A:
"Woof I am Mara"
,TypeError
- B:
"Woof I am Mara"
,"Woof I am Mara"
- C:
"Woof I am Mara"
,undefined
- D:
TypeError
,TypeError
答え
プロトタイプでも、delete
キーワードを使ってオブジェクトからプロパティを削除できます。プロトタイプのプロパティを削除すると、プロトタイプチェーンでは使用できなくなります。
この場合、bark
関数は delete Dog.prototype.bark
の後のプロトタイプでは、もう利用できず、それでもアクセスし、関数ではない何かを呼び出そうとすると、TypeError
がスローされます。
関数ではない何かを呼び出そうとすると、pet.bark
はundefined
なので、TypeError
がスローされ、TypeError: pet.bark is not a function
となります。
const set = new Set([1, 1, 2, 3, 4]);
console.log(set);
- A:
[1, 1, 2, 3, 4]
- B:
[1, 2, 3, 4]
- C:
{1, 1, 2, 3, 4}
- D:
{1, 2, 3, 4}
答え
Set
オブジェクトは unique の値の集合です: 値は集合の中で一度だけ現れることができます
値1
が重複したイテラブル[1、1、2、3、4]
を渡しました。セット内に同じ値を2つ持つことはできないので、そのうちの1つが削除され{1、2、3、4}
となります。
// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from "./counter";
myCounter += 1;
console.log(myCounter);
- A:
10
- B:
11
- C:
Error
- D:
NaN
答え
インポートされたモジュールは読み取り専用です。: インポートされたモジュールを変更することはできません。エクスポートするモジュールだけがその値を変更できます。
myCounter
の値を増やそうとすると、error: myCounter
is read-only and cannot be modified. と、エラーがスローされます。
const name = "Lydia";
age = 21;
console.log(delete name);
console.log(delete age);
- A:
false
,true
- B:
"Lydia"
,21
- C:
true
,true
- D:
undefined
,undefined
答え
delete
演算子は、ブール値を返します: 正常に削除された場合はtrue、それ以外の場合はfalseを返します。var
, const
またはlet
キーワードで宣言された変数はdelete
演算子を使って削除することはできません。
name
変数はconst
キーワードで宣言されているので、削除は成功しません: false
が返されます。
age
を21
に設定すると、実際にはグローバルオブジェクトにage
というプロパティを追加されました。グローバルオブジェクトからもプロパティを削除することができますので、delete age
はtrue
を返します。
const numbers = [1, 2, 3, 4, 5];
const [y] = numbers;
console.log(y);
- A:
[[1, 2, 3, 4, 5]]
- B:
[1, 2, 3, 4, 5]
- C:
1
- D:
[1]
答え
配列から値を取り出したり、オブジェクトからプロパティを分解して取り出すことができます。 example:
[a, b] = [1, 2];
a
の値は1
となり、b
の値は2
となる。実際に問題で行った事は、
[y] = [1, 2, 3, 4, 5];
y
の値が配列の最初の値、つまり1
に等しいことを意味します。y
をログ出力すると、1
が返されます。
const user = { name: "Lydia", age: 21 };
const admin = { admin: true, ...user };
console.log(admin);
- A:
{ admin: true, user: { name: "Lydia", age: 21 } }
- B:
{ admin: true, name: "Lydia", age: 21 }
- C:
{ admin: true, user: ["Lydia", 21] }
- D:
{ admin: true }
答え
スプレッド演算子...
を使ってオブジェクトを結合することができます。あるオブジェクトのキーと値のペアのコピーを作成し、それらを別のオブジェクトに追加することができます。
上記の場合だと、user
オブジェクトのコピーを作成し、それらをadmin
オブジェクトに追加します。admin
オブジェクトはコピーされたキーと値のペアを含み、その結果{admin:true、name: "Lydia"、age:21}
となります。
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
- A:
{ name: "Lydia", age: 21 }
,["name", "age"]
- B:
{ name: "Lydia", age: 21 }
,["name"]
- C:
{ name: "Lydia"}
,["name", "age"]
- D:
{ name: "Lydia"}
,["age"]
答え
defineProperty
メソッドを使うと、オブジェクトに新しいプロパティを追加したり、既存のプロパティを修正することができます。 defineProperty
メソッドを使ってオブジェクトにプロパティを追加すると、それらはデフォルトでは 列挙できません。
Object.keys
メソッドはオブジェクトから全ての enumerable (列挙可能)なプロパティ名を返します。上記の場合は"name"
だけとなります。
defineProperty
メソッドを使って追加されたプロパティはデフォルトでは不変となります。 この動作はwritable
, configurable
, enumerable
プロパティを使って上書きすることができます。このように、defineProperty
メソッドは、オブジェクトに追加しようとしているプロパティをもっと細かく制御できます。
const settings = {
username: "lydiahallie",
level: 19,
health: 90
};
const data = JSON.stringify(settings, ["level", "health"]);
console.log(data);
- A:
"{"level":19, "health":90}"
- B:
"{"username": "lydiahallie"}"
- C:
"["level", "health"]"
- D:
"{"username": "lydiahallie", "level":19, "health":90}"
答え
JSON.stringify
の2番目の引数は replacer です。replacerは、関数または配列のいずれかにすることができ、値を文字列化する対象とその方法を制御できます。
replacerが array の場合、名前が配列に含まれるプロパティのみがJSON文字列に追加されます。上記の場合、"level"
と"health"
という名前のプロパティだけが含まれ、"username"
は除外されます。data
は"{" level ":19、" health ":90}"
となります。
replacerが function の場合、この関数は文字列化しているオブジェクト内のすべてのプロパティに対して呼び出されます。この関数から返される値は、JSON文字列に追加されたときのプロパティの値になり、値がundefined
の場合、このプロパティはJSON文字列から除外されます。
let num = 10;
const increaseNumber = () => num++;
const increasePassedNumber = number => number++;
const num1 = increaseNumber();
const num2 = increasePassedNumber(num1);
console.log(num1);
console.log(num2);
- A:
10
,10
- B:
10
,11
- C:
11
,11
- D:
11
,12
答え
単項演算子++
はオペランドの値を 最初に返し 、その後に インクリメント します。num1
の値は10
となります。 なぜならincrementNumber
関数は、最初にnum
の値10
を返し、その後にnum
の値をインクリメントするだけです。
num1
をincrePassedNumber
に渡したので、num2
は10
です。number
は10
(num1
の値です。繰り返しますが、単項演算子++
は、オペランドの値を 最初に返し、その後に インクリメント します。したがって、num2
は10
となります。
const value = { number: 10 };
const multiply = (x = { ...value }) => {
console.log((x.number *= 2));
};
multiply();
multiply();
multiply(value);
multiply(value);
- A:
20
,40
,80
,160
- B:
20
,40
,20
,40
- C:
20
,20
,20
,40
- D:
NaN
,NaN
,20
,40
答え
ES6では、パラメータをデフォルト値で初期化できます。値が関数に渡されていない場合やパラメータの値が "undefined"
の場合、パラメータの値はデフォルト値になります。上記の場合、value
オブジェクトのプロパティを新しいオブジェクトに分割代入されるので、x
のデフォルト値は{number:10}
になります。
デフォルトの引数は、呼び出し時 に評価されます。関数を呼び出すたびに、新しい オブジェクトが作成されます。
最初に値を渡さずに2回、multiply
関数を呼び出します: x
のデフォルト値は {number:10}
となり、その数の乗算された値、つまり 20
を出力します。
3回目のmultiplyを呼び出すとき、引数を渡します: value
というオブジェクトです。
*=
演算子はx.number = x.number * 2
の省略形となります: x.number
の値は乗算した値に修正され、20
を出力します。
4回目は、value
オブジェクトをもう一度渡します。x.number
は以前は20
に修正されているので、x.number *= 2
は40
を出力します。
[1, 2, 3, 4].reduce((x, y) => console.log(x, y));
- A:
1
2
and3
3
and6
4
- B:
1
2
and2
3
and3
4
- C:
1
undefined
and2
undefined
and3
undefined
and4
undefined
- D:
1
2
andundefined
3
andundefined
4
答え
reduce
メソッドが受け取る最初の引数は アキュムレータ となります。この場合はx
です。 2番目の引数は、現在の値 y
です。 reduceメソッドでは、配列内のすべての要素に対してコールバック関数を実行します。これにより、最終的に1つの値が得られます。
上記の例では、値を返していません。単にアキュムレータの値と現在の値を記録しています。
アキュムレータの値は、以前に返されたコールバック関数の値と同じです。オプションのinitialValue
引数をreduce
メソッドに渡さないと、アキュムレータは最初の呼び出しの最初の要素に等しくなります。
最初の呼び出しでは、アキュムレータ(x
)は1
であり、現在値(y
)は2
となります。コールバック関数からは戻らないので、アキュムレータと現在の値を出力します: 1
と2
が出力されます。
関数から値を返さなければ、undefined
を返します。次の呼び出しでは、アキュムレータはundefined
で、現在の値は3
です。undefined
と3
が出力されます。
4回目の呼び出しでも、コールバック関数からは戻りません。アキュムレータもまたundefined
であり、現在の値は4
となり、undefined
と4
が出力されます。
class Dog {
constructor(name) {
this.name = name;
}
};
class Labrador extends Dog {
// 1
constructor(name, size) {
this.size = size;
}
// 2
constructor(name, size) {
super(name);
this.size = size;
}
// 3
constructor(size) {
super(name);
this.size = size;
}
// 4
constructor(name, size) {
this.name = name;
this.size = size;
}
};
- A: 1
- B: 2
- C: 3
- D: 4
答え
派生クラスでは、super
を呼び出す前に、this
キーワードにアクセスすることはできません。そうしようとすると、ReferenceErrorがスローされます: 1と4は参照エラーをスローします。
super
キーワードを使って、与えられた引数で、その親クラスのコンストラクタを呼び出します。親のコンストラクタはname
引数を受け取るので、name
をsuper
に渡す必要があります。
Labrador
クラスは2つの引数、Dog
を拡張するためのname
と、Labrador
クラスの追加のプロパティとしてのsize
を受け取ります。
両方ともLabrador
のコンストラクタ関数に渡す必要があります。これはコンストラクタ2を使って正しく実行されます。
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
- A:
running index.js
,running sum.js
,3
- B:
running sum.js
,running index.js
,3
- C:
running sum.js
,3
,running index.js
- D:
running index.js
,undefined
,running sum.js
答え
import
キーワードを使うと、全てのインポートされたモジュールは 事前解析 されます。これは、インポートされたモジュールが 最初 に実行され、その後 モジュールをインポートしたファイル内のコードが実行されることを意味します。
これはCommonJSのrequire()
とimport
の違いです。require()
を使うと、コードが実行されている間に依存関係をオンデマンドでロードすることができます。
import
の代わりにrequire
を使用したとしたら、running index.js
, running sum.js
, 3
が出力されているはずです。
console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
- A:
true
,true
,false
- B:
false
,true
,false
- C:
true
,false
,true
- D:
true
,true
,true
答え
すべてのシンボルは完全にユニークです。シンボルに渡される引数の目的は、シンボルに説明を与えることです。Symbolの値は渡された引数に依存しません。
等価性をテストしているので、2つのまったく新しいシンボルを作成します: 最初のSymbol('foo')
と、2番目のSymbol('foo')
です。これら2つの値は一意であり、互いに等しくはありません、なのでSymbol('foo') === Symbol('foo')
はfalse
を返します。
const name = "Lydia Hallie"
console.log(name.padStart(13))
console.log(name.padStart(2))
- A:
"Lydia Hallie"
,"Lydia Hallie"
- B:
" Lydia Hallie"
," Lydia Hallie"
("[13x whitespace]Lydia Hallie"
,"[2x whitespace]Lydia Hallie"
) - C:
" Lydia Hallie"
,"Lydia Hallie"
("[1x whitespace]Lydia Hallie"
,"Lydia Hallie"
) - D:
"Lydia Hallie"
,"Lyd"
,
答え
padStart
メソッドを使うと、文字列の先頭にパディングを追加できます。このメソッドに渡される値は、パディングとともに文字列の長さの 合計 です。文字列"Lydia Hallie"
の長さは12
です。 name.padStart(13)
は、12 + 1が13であるため、文字列の先頭に1スペースを挿入されます。
padStart
メソッドに渡された引数が、配列の長さよりも小さい場合、パディングは追加されません。
console.log("🥑" + "💻");
- A:
"🥑💻"
- B:
257548
- C: A string containing their code points
- D: Error
function* startGame() {
const answer = yield "Do you love JavaScript?";
if (answer !== "Yes") {
return "Oh wow... Guess we're gone here";
}
return "JavaScript loves you back ❤️";
}
const game = startGame();
console.log(/* 1 */); // Do you love JavaScript?
console.log(/* 2 */); // JavaScript loves you back ❤️
- A:
game.next("Yes").value
andgame.next().value
- B:
game.next.value("Yes")
andgame.next.value()
- C:
game.next().value
andgame.next("Yes").value
- D:
game.next.value()
andgame.next.value("Yes")
答え
ジェネレータ関数は、yield
キーワードを見るとその実行を「一時停止」します。まず、関数に文字列 "Do you love JavaScript?" を返させる必要があります。これは game.next().value
を呼び出すことによって行うことができます。
最初のyield
キーワードが見つかるまで、すべての行が実行されます。関数内の最初の行にyield
キーワードがあります: 実行は最初のyieldで停止します! これは変数 answer
がまだ定義されていないことを意味します!
game.next("Yes").value
を呼び出すと、前のyield
はnext()
関数に渡されたパラメータの値、この場合は"Yes"
に置き換えられます。変数answer
の値は現在"Yes"
となります。
if-statemnetの条件はfalse
を返し、JavaScript loves you back ❤️
が、出力されます。
console.log(String.raw`Hello\nworld`);
- A:
Hello world!
- B:
Hello
world
- C:
Hello\nworld
- D:
Hello\n
world
答え
String.raw
はエスケープ(\n
, \v
, \t
など)を無視した文字列を返します。バックスラッシュは問題になる可能性があります:
const path = `C:\Documents\Projects\table.html`
これは次のようになります:
"C:DocumentsProjects able.html"
String.raw
は、単にエスケープを無視して出力するだけです:
C:\Documents\Projects\table.html
上記の場合、文字列はHello\nworld
と出力されます。
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
- A:
"I made it!"
- B:
Promise {<resolved>: "I made it!"}
- C:
Promise {<pending>}
- D:
undefined
答え
非同期関数は常に、promiseを返します。await
はpromiseが解決されるのを待たなければなりません: getData()
を呼び出すと、data
は保留中のpromiseが返されます。
解決した値"I made it"
にアクセスしたい場合は、data
に対して.then()
メソッドを使用することができます:
data.then(res => console.log(res))
これは"I made it!"
と出力するでしょう。
function addToList(item, list) {
return list.push(item);
}
const result = addToList("apple", ["banana"]);
console.log(result);
- A:
['apple', 'banana']
- B:
2
- C:
true
- D:
undefined
答え
.push()
メソッドは新しい配列の長さを返します。以前は、配列は1つの要素(文字列 " banana "
)を含み、長さは 1
でした。文字列 " apple "
を配列に追加した後、配列は2つの要素を含み、長さは 2
になります。これは addToList
関数から返されます。
The .push()
method returns the length of the new array! Previously, the array contained one element (the string "banana"
) and had a length of 1
. After adding the string "apple"
to the array, the array contains two elements, and has a length of 2
. This gets returned from the addToList
function.
push
メソッドは元の配列を修正します。配列の長さではなく関数から配列を返したい場合は、itemをプッシュした後にlistを返すべきです。
The push
method modifies the original array. If you wanted to return the array from the function rather than the length of the array, you should have returned list
after pushing item
to it.
const box = { x: 10, y: 20 };
Object.freeze(box);
const shape = box;
shape.x = 100;
console.log(shape);
- A:
{ x: 100, y: 20 }
- B:
{ x: 10, y: 20 }
- C:
{ x: 100 }
- D:
ReferenceError
答え
Object.freeze
は、オブジェクトのプロパティを追加、削除、変更することを不可能にします(プロパティの値が他のオブジェクトのものでない限り)。
変数shape
を作成し、フリーズしたオブジェクトbox
に代入すると、shape
はフリーズしたオブジェクトとなります。オブジェクトがフリーズしているかどうかは Object.isFrozen
を使って確認できます。
この場合、変数shape
はフリーズしたオブジェクトへの参照を持っているので、Object.isFrozen(shape)
はtrueを返します。
shape
はフリーズされており、x
の値はオブジェクトではないので、プロパティx
を変更することはできません。
x
は10
のままとなり、{ x: 10, y: 20 }
と出力されます。
const { name: myName } = { name: "Lydia" };
console.log(name);
- A:
"Lydia"
- B:
"myName"
- C:
undefined
- D:
ReferenceError
答え
右側のオブジェクトからプロパティname
をアンパックするとき、その値"Lydia"
をmyName
という名前の変数に代入します。
{name:myName}
を使って、右側の name
プロパティの値でmyName
という新しい変数を作りたいことをJavaScriptに伝えます。
定義されていない変数name
を出力しようとしているので、ReferenceErrorが投げられます。
function sum(a, b) {
return a + b;
}
- A: Yes
- B: No
答え
純粋な関数は、同じ引数が渡された場合、常に 同じ結果を返す関数です。
sum
関数は常に同じ結果を返します。1
と2
を渡すと、副作用なしに 常に 3
を返します。5
と10
を渡すと、常に 15
が返され、以下同様に続きます。これが純粋関数の定義です。
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};
const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));
- A:
Calculated! 20
Calculated! 20
Calculated! 20
- B:
Calculated! 20
From cache! 20
Calculated! 20
- C:
Calculated! 20
From cache! 20
From cache! 20
- D:
Calculated! 20
From cache! 20
Error
答え
add
関数は memoized 関数です。メモ化により、実行速度を上げるために関数の結果をキャッシュすることができます。上記の場合、以前に返された値を格納するcache
オブジェクトを作成します。
同じ引数を指定してもう一度addFunction
関数を呼び出すと、最初にキャッシュ内でその値がすでに取得されているかどうかを調べます。
この場合、cachesの値が返され、実行時間が短縮されます。そうでなくキャッシュされていなければ、値を計算した後にそれを格納します。
同じ値で3回addFunction
関数を呼び出します: 最初の呼び出しでは、num
に10
を代入した時、関数の値はまだキャッシュされていません。
ifステートメントのnum in cache
の条件はfalse
を返し、elseブロックが実行されます: Calculated! 20
が出力され、結果の値がキャッシュオブジェクトに追加されます。 cache
は現在 { 10: 20 }
となります。
2回目は、cache
オブジェクトは10
に対して返される値を含みます。 ifステートメントのnum in cache
の条件はtrue
となり、'From cache! 20'
を返します。 よって'From cache! 20'
が出力されます。
3回目は、10
に評価される関数に5 * 2
を渡します。cache
オブジェクトは10
に対して返される値を含みます。ifステートメントのnum in cache
の条件はtrue
となり、'From cache! 20'
を返します。 よって'From cache! 20'
が出力されます。
const myLifeSummedUp = ["☕", "💻", "🍷", "🍫"]
for (let item in myLifeSummedUp) {
console.log(item)
}
for (let item of myLifeSummedUp) {
console.log(item)
}
- A:
0
1
2
3
and"☕"
"💻"
"🍷"
"🍫"
- B:
"☕"
"💻"
"🍷"
"🍫"
and"☕"
"💻"
"🍷"
"🍫"
- C:
"☕"
"💻"
"🍷"
"🍫"
and0
1
2
3
- D:
0
1
2
3
and{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
答え
for-in ループを使うと、列挙可能なプロパティを繰り返し処理できます。配列では、列挙可能なプロパティは配列要素の「キー」です。これはそれらのインデックスとなり、配列は次のようになります:
{0: "☕", 1: "💻", 2: "🍷", 3: "🍫"}
キーが列挙可能なプロパティであるので、0
1
2
3
が出力されます。
for-of ループを使うと、反復可能オブジェクトを繰り返し処理できます。
配列はイテラブルです。配列を反復処理するとき、変数 "item"は、現在反復処理している要素となるので、"☕"
"💻"
"🍷"
"🍫"
が出力されます。
const list = [1 + 2, 1 * 2, 1 / 2]
console.log(list)
- A:
["1 + 2", "1 * 2", "1 / 2"]
- B:
["12", 2, 0.5]
- C:
[3, 2, 0.5]
- D:
[1, 1, 1]
答え
配列要素は任意の値を保持できます。数値、文字列、オブジェクト、その他の配列、null、ブール値、undefined、および日付、関数、計算などのその他の式。
要素は戻り値と等しくなります。1 + 2
は3
を返し、1 * 2
は2
を返し、1 / 2
は0.5
を返します。
function sayHi(name) {
return `Hi there, ${name}`
}
console.log(sayHi())
- A:
Hi there,
- B:
Hi there, undefined
- C:
Hi there, null
- D:
ReferenceError
答え
関数に値が渡されていない限り、引数はデフォルトでundefined
の値を持ちます。上記の場合、name
引数に値を渡さなかったので、name
はundefined
となり出力されます。
ES6では、このデフォルトのundefined
値を、デフォルトパラメータで上書きすることができます。例:
function sayHi(name = "Lydia") { ... }
上記の場合、値を渡さなかった場合や、undefined
を渡した場合は、name
は常に文字列Lydia
となります。
var status = "😎"
setTimeout(() => {
const status = "😍"
const data = {
status: "🥑",
getStatus() {
return this.status
}
}
console.log(data.getStatus())
console.log(data.getStatus.call(this))
}, 0)
- A:
"🥑"
and"😍"
- B:
"🥑"
and"😎"
- C:
"😍"
and"😎"
- D:
"😎"
and"😎"
答え
this
キーワードの値は使う場所に依存します。 メソッドの中では、getStatus
メソッドのように、this
キーワードは メソッドが属するオブジェクトを参照します 。
メソッドはdata
オブジェクトに属しているので、this
は data
オブジェクトを参照します。 this.status
をログ出力すると、data
オブジェクトのstatus
プロパティの"🥑"
がログ出力されます。
call
メソッドを使うと、this
キーワードが参照するオブジェクトを変更することができます。 関数では、this
キーワードは その関数が属するオブジェクトを参照します 。
グローバルオブジェクトで setTimeout
関数を宣言したので、setTimeout
関数内では、 this
キーワードは グローバルオブジェクト を参照します。
グローバルオブジェクト上には、値"😎"
を持つ status という変数があります。this.status
を出力すると、"😎"
が出力されます。
const person = {
name: "Lydia",
age: 21
}
let city = person.city
city = "Amsterdam"
console.log(person)
- A:
{ name: "Lydia", age: 21 }
- B:
{ name: "Lydia", age: 21, city: "Amsterdam" }
- C:
{ name: "Lydia", age: 21, city: undefined }
- D:
"Amsterdam"
答え
変数city
に、person
オブジェクトのcity
という名前のプロパティの値を代入します。このオブジェクトにはcity
という名前のプロパティはないので、変数city
はundefined
の値を持ちます。
我々はperson
オブジェクト自身を参照して いない ことに注意してください。person
オブジェクトのcity
プロパティを、変数city
に代入するだけです。
それから、city
に、文字列"Amsterdam"
を代入しますこれは personオブジェクトを変更しません: そのオブジェクトへの参照はありません。
person
オブジェクトをログ出力するとき、未修正のオブジェクトが返されます。
function checkAge(age) {
if (age < 18) {
const message = "Sorry, you're too young."
} else {
const message = "Yay! You're old enough!"
}
return message
}
console.log(checkAge(21))
- A:
"Sorry, you're too young."
- B:
"Yay! You're old enough!"
- C:
ReferenceError
- D:
undefined
答え
const
とlet
キーワードを持つ変数は ブロックスコープ です。ブロックは中括弧({ }
)で囲まれたものです。上記の場合、if/elseステートメントが中括弧となります。宣言されたブロックの外側で変数を参照することはできません。ReferenceError がスローされます。
fetch('https://www.website.com/api/user/1')
.then(res => res.json())
.then(res => console.log(res))
- A:
fetch
メソッドの結果 - B: 2回目の
fetch
メソッド呼び出しの結果 - C: 前の
.then()
でのコールバックの結果 - D: 常に undefined.
function getName(name) {
const hasName = //
}
- A:
!!name
- B:
name
- C:
new Boolean(name)
- D:
name.length
答え
!!name
を使って、name
の値が、truthyか falseyかを判断します。nameがtruthyであり、これをテストしたい場合、!name
はfalse
を返します。!false
(これは実際には!!name
です)はtrue
を返します。
hasName
にname
を代入することで、getName
関数に渡されたどんな値もhasName
に代入されます。ブール値true
は設定できません。
new Boolean(true)
は、ブール値そのものではなく、オブジェクトラッパーを返します。
name.length
は渡された引数の長さを返します。それがtrue
であるかどうかではありません。