私は毎日、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"]]