私は毎日、JavaScriptに関する選択問題を Instagramに投稿していますが、ここにも投稿します。
初級から上級まで: JavaScriptの知識のテストを行ったり、知識を少し深めたり、コーディング面接の準備をしてください。:muscle: :rocket: 私はこのレポを毎週新しい質問で更新します。
答えは質問の下の折りたたまれたセクションにあります、クリックすればそれを広げられます。幸運を祈ります。:heart:
中文版本
Русский
Western Balkan
Deutsch
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
。関数はオブジェクトなので、"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"
を返します。