Help us understand the problem. What is going on with this article?

30 seconds of interviews 日本語訳【JavaScript初級中級編】

More than 1 year has passed since last update.

30 SECONDS OF INTERVIEWSの日本語訳です。
Webフロントエンドの技術に関して非常によくまとまっていたため、英語には疎いですが翻訳しました。

初級編

Q. 等価演算子=====の違いは何ですか?

:thinking:回答をみる
トリプルイコール(===)は厳密な等価性をチェックします。つまり、型と値の両方が同じでなければなりません。 一方、ダブルイコール(==)は、最初に型強制を実行して、両方のオペランドが同じ型であるように厳密な比較を適用します。


Q. 後置インクリメントi++と前置インクリメント++iの違いは何ですか?

:thinking:回答をみる

どちらも変数の値を1だけ増やします。違いは評価のされ方です。
let i = 0
i++ // 0
// i === 1

前置インクリメントはインクリメントされたに評価されます

let i = 0
++i // 1
// i === 1


Q. Promiseはどんな状態をとることができますか?

:thinking:回答をみる

Promiseは、次のいずれかの状態をとります
  • pending: 初期状態、完了も拒否もありません。
  • fulfilled:操作が正常に完了したことを意味します。
  • rejected:操作が失敗したことを意味します。

pendingのPromiseは値と一緒にfulfilledにすることも、理由(エラー)とともにrejectedにすることもできます。これらのオプションのいずれかが発生するとPromiseのthenメソッドによってキューに入れられたハンドラが呼び出されます


中級編

Q. レシピから最大の分量を返す関数batchesを作ってください

/**
引数として二つのオブジェクトをとり、最初のオブジェクトは食べもののレシピで、2つめのオブジェクトは手に入れられる原料です。
それぞれの原料の値は存在する個数を表す数値です。

`batches(recipe, available)`
*/

// 0 個作成可能
batches(
  { milk: 100, butter: 50, flour: 5 },
  { milk: 132, butter: 48, flour: 51 }
)
batches(
  { milk: 100, flour: 4, sugar: 10, butter: 5 },
  { milk: 1288, flour: 9, sugar: 95 }
)

// 1 個作成可能
batches(
  { milk: 100, butter: 50, cheese: 10 },
  { milk: 198, butter: 52, cheese: 10 }
)

// 2 個作成可能
batches(
  { milk: 2, sugar: 40, butter: 20 },
  { milk: 5, sugar: 120, butter: 500 }
)

:thinking:回答をみる

使用可能なレシピのすべての原料を、必要な単位数以上の量で入手する必要があります。 原料のうちの1つだけが入手できないか、必要量より少ない場合は、一つもバッチを作ることはできません。
Object.keys()を使用してレシピの原料を配列として返し、Array.prototype.map()を使用して、各原料をレシピに必要な量に対する使用可能な単位の比率にマップします。 レシピに必要な原料の1つが作成できない場合、比率はNaNと評価されるため、論理OR演算子を使用してこの場合は0にフォールバックすることができます。
スプレッド演算子...を使用して、すべての原料比の配列をMath.min()に送り、最も低い比率を決定します。 この結果全体をMath.floor()に渡すと、バッチ全体の最大数が返されます。

const batches = (recipe, available) =>
  Math.floor(
    Math.min(...Object.keys(recipe).map(k => available[k] / recipe[k] || 0))
  )


Q. Function.prototype.bindメソッドと機能的に同等なスタンドアロンの関数bindを作成してください。

function example() {
  console.log(this)
}
const boundExample = bind(example, { a: true })
boundExample.call({ b: true }) // logs { a: true }

:thinking:回答をみる

 任意の数の引数を受け入れる関数をrest...演算子で集めて返します。
その関数から、fnFunction.prototype.applyで呼び出した結果を返して、コンテキストと引数の配列を関数に適用します。

const bind = (fn, context) => (...args) => fn.apply(context, args)


Q. コールバックとは何ですか?例を示して下さい

:thinking:回答をみる

コールバックは、イベントが発生したとき、または特定のタスクが完了したときに実行される別の関数への引数として渡される関数で、非同期コードでよく使用されます。
コールバックはコードの後の方で呼び出されることが多いですが、呼び出しがなくても初期化時に宣言できます。

たとえば、イベントリスナーは、特定のイベントが発生したときにのみ実行される非同期コールバックです。

function onClick() {
  console.log("The user clicked on the page.")
}
document.addEventListener("click", onClick)

ときに、コールバックは同期的に呼び出すこともできます。次のmap関数は、ループ(配列要素)の各イテレーションごとに同期的に呼び出されるコールバックとなります。

const map = (arr, callback) => {
  const result = []
  for (let i = 0; i < arr.length; i++) {
    result.push(callback(arr[i], i))
  }
  return result
}
map([1, 2, 3, 4, 5], n => n * 2) // [2, 4, 6, 8, 10]


Q.JavaScriptではオブジェクトをどのようにクローンしますか?

:thinking:回答をみる

スプレッド演算子...を使用すると、オブジェクトの独自の列挙可能なプロパティを新しいオブジェクトにコピーできます。これにより、オブジェクトの浅いクローンが作成されます。
const obj = { a: 1, b: 2 }
const shallowClone = { ...obj }

この手法では、プロトタイプは無視されます。また、ネストされたオブジェクトは複製されず、その参照がコピーされるため、ネストされたオブジェクトは元のオブジェクトと同じオブジェクトを参照します。

ディープクローン作成は、オブジェクト内にネストされる可能性のある任意のタイプのオブジェクト(Date、RegExp、Function、Setなど)を効果的に複製するのはもう少し複雑です。

他の選択肢には、
- JSON.parse(JSON.stringify(obj)) は単純なオブジェクトをディープクローンするために使用できますが、CPUを大量に使用し、有効なJSONのみを受け入れます(したがって関数を削除し、循環参照を許可しません)。
- Object.assign({},obj)は別の方法です。
- Object.keys(obj).reduce((acc、key)=>(acc [key] = obj [key]、acc),{})は、概念をより深く示すもう一つのより冗長な選択肢です。


Q. JavaScriptで2つのオブジェクトを比較する方法は何ですか?

:thinking:回答をみる

2つの異なるオブジェクトが同じプロパティで同じ値を持っていたとしても、それらは==または===を使用して比較した場合、等しいとはみなされません。これは、値で比較されるプリミティブ値とは異なり、参照(メモリ内の位置)によって比較されているためです。
2つのオブジェクトの構造が等しいかどうかを調べるには、ヘルパー関数が必要です。ネストされたオブジェクトを含む同じ値を持つかどうかをテストするために、各オブジェクトの独自のプロパティを反復処理します。
オプションとして、第3引数にtrueを渡すことによって、オブジェクトのプロトタイプの等価性をテストすることもできます。
注:この手法では、単純なオブジェクト、配列、関数、日付、およびプリミティブ値以外のデータ構造の等価性をテストしようとはしません。
function isDeepEqual(obj1, obj2, testPrototypes = false) {
  if (obj1 === obj2) {
    return true
  }

  if (typeof obj1 === "function" && typeof obj2 === "function") {
    return obj1.toString() === obj2.toString()
  }

  if (obj1 instanceof Date && obj2 instanceof Date) {
    return obj1.getTime() === obj2.getTime()
  }

  if (
    Object.prototype.toString.call(obj1) !==
      Object.prototype.toString.call(obj2) ||
    typeof obj1 !== "object"
  ) {
    return false
  }

  const prototypesAreEqual = testPrototypes
    ? isDeepEqual(
        Object.getPrototypeOf(obj1),
        Object.getPrototypeOf(obj2),
        true
      )
    : true

  const obj1Props = Object.getOwnPropertyNames(obj1)
  const obj2Props = Object.getOwnPropertyNames(obj2)

  return (
    obj1Props.length === obj2Props.length &&
    prototypesAreEqual &&
    obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
  )
}


Q. CORSとは何ですか?

:thinking:回答をみる

クロスオリジンリソース共有(Cross-Origin Resource Sharing; CORS)は、追加のHTTPヘッダーを使用して、Webサイトのオリジンとは異なるオリジンのサーバーからリソースにアクセスするための許可をブラウザーに与える仕組みです。

クロスオリジンリクエストの例は、http://mydomain.comから提供されるWebアプリケーションで、AJAXを使用してhttp://yourdomain.comをリクエストします。

セキュリティ上の理由から、ブラウザはJavaScriptによって開始されたクロスオリジンHTTPリクエストを制限します。XMLHttpRequestfetchは同一オリジンポリシーに従います。つまり、これらのAPIを使用するWebアプリケーションは、別のオリジンからのレスポンスに正しいCORSヘッダーが含まれていない限り、アプリケーションがアクセスされたのと同じオリジンからのHTTPリソースしか要求できません。


Q. イベントの委任(event delegation)とは何ですか?それはなぜ有用ですか?それを使用する方法の例を表示できますか?

:thinking:回答をみる

イベントの委任は、イベントを単一の共通の祖先に委任する手法です。イベントのバブリングのために、イベントは、それをリッスンしている可能性のあるルートまで、各祖先要素に対して漸進的にハンドラを実行することによって、DOMツリーを「泡」(bubble)のように遡ります。

DOMイベントは、Event.targetを介してイベントを開始した要素に関する有益な情報を提供します。これにより、親要素または親自身のすべての子ではなく、対象要素がイベントをリッスンしているかのように、親要素が動作を処理できます。

これには2つの主な利点があります。

  • 潜在的に何千もの要素を処理するために単一のイベントリスナーを登録するだけで、パフォーマンスが向上し、メモリ消費量が削減されます。
  • 要素が親に動的に追加された場合、新しいイベントリスナーを登録する必要はありません。
document.querySelectorAll("button").forEach(button => {
  button.addEventListener("click", handleButtonClick)
})

の代わりに、イベントの委任では、条件を使用して、子ターゲットが希望の要素と一致するようにします。

document.addEventListener("click", e => {
  if (e.target.closest("button")) {
    handleButtonClick()
  }
})


Q. JavaScriptで式(Expression)と文(Statement)の違いは何ですか?

:thinking:回答をみる

JavaScriptには式(Expression)と文(Statement)の2つの主要なカテゴリがあります。 3つ目は、両方とも一緒に式文(Expression Statement)と呼ばれます。これらは大まかに以下のように要約されます

  • 式(Expression): 値を示す
  • 文(Statement): アクションを実行する
  • 式文(Expression Statement): 値を示しかつアクションを実行する

一般的な経験則として

それを表示(print)したり、変数に代入することができれば、それは式です。できなければ、それは文です。

let x = 0

function declaration() {}

if (true) {
}

文は、何かをするが値を生成しない命令として表示されます。

// yの絶対値をxに代入
var x
if (y >= 0) {
  x = y
} else {
  x = -y
}


式は値を生成します。インタプリタはそれらを解決する値で置き換えるので、関数に渡すことができます。

5 + 5 // => 10

lastCharacter("input") // => "t"

true === true // => true

式文
先ほど示した文と同等のものを三項演算子を用いて示したのがこれです。

// yの絶対値をxに代入
var x = y >= 0 ? y : -y

変数x(文)を評価(式)として宣言しているので、これは式と文の両方です。


Q. JavaScriptの真(Truthy)と偽(Falsy)の値は何ですか?

:thinking:回答をみる

ブール値のコンテクストでは値は真か偽のいずれかです。偽(Falsy)はfalse風で、真(Truthy)はtrue風を意味します。基本的には、trueまたはfalseに強制される値です。
JavaScriptでは以下の6つが偽として扱われます。
  • false
  • undefined
  • null
  • ""(空文字列)
  • NaN
  • 0(+0-0も含む)

他の値はすべて真とみなされます。
値の真偽値は、Boolean関数に渡すことで調べることができます。

Boolean("") // false
Boolean([]) // true

これのショートカットには論理NOT演算子!を用います。一度だけ!を使うと真偽値が反転したブール値に変換され、もう一度!を使うことで効果的に値をブール値に変換することができます。

!!"" // false
!![] // true


Q. n項目までのフィボナッチ配列を含む配列を作成してください。

:thinking:回答をみる

長さnの空の配列を初期化します。Array.prototype.reduce()を用いて、最初の2つを除く最後の2つの値の合計を使用して配列に値を追加します。
const fibonacci = n =>
  [...Array(n)].reduce(
    (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),
    []
  )


Q. 0.1 + 0.2 === 0.3はどのように評価されますか?

:thinking:回答をみる

JavaScriptはMathでIEEE 754標準を使用し、64ビット浮動小数点数を使用するため、falseと評価されます。要するに、基数2で作業するコンピュータと10進数が基数10であるため、これは小数点以下の計算を行うときに精度エラーを引き起こします。
0.1 + 0.2 // 0.30000000000000004

この問題の解決策は、2つの値の差が2より小さいはずの誤差マージン(ε; epsilon)値を定義することによって2つの数がほぼ等しいかどうかを決定する関数を使用することです。

const approxEqual = (n1, n2, epsilon = 0.0001) => Math.abs(n1 - n2) < epsilon
approxEqual(0.1 + 0.2, 0.3) // true


Q. 配列の関数map()forEach()の違いは何ですか?

:thinking:回答をみる

どちらのメソッドも配列の要素を反復処理します。 map()は、各要素に対してコールバック関数を呼び出し、新しい配列を返すことによって、各要素を新しい要素にマッピングします。
一方、forEach()は各要素のコールバック関数を呼び出しますが、新しい配列は返しません。 forEach()は一般に、各反復で副作用を引き起こす場合に使用されますが、map()は一般的な関数プログラミング手法です。


Q. この例のコンソールログは何ですか?

var foo = 1
var foobar = function() {
  console.log(foo)
  var foo = 2
}
foobar()

:thinking:回答をみる

巻き上げ(Hoisting)により、ローカル変数fooは、console.logが呼び出される前に宣言されます。つまり、ローカル変数fooは、関数の外部で宣言されたグローバル変数ではなく、console.log()への引数として渡されます。しかし、変数の宣言で値を巻き上げすることはないので、出力は2ではなくundefinedになります。


Q. JavaScriptで巻き上げ(Hoisting)はどのように機能しますか?

:thinking:回答をみる

巻き上げ(Hoisting)は、変数と関数宣言をコンパイル・フェーズ中にメモリに入れるJavaScriptの仕組みです。
これは、関数と変数がどこに宣言されても、スコープがグローバルかローカルかにかかわらずスコープの先頭に移動することを意味します。

しかし、その値は宣言では引き上げられません。

console.log(hoist)
var hoist = "value"

var hoist
console.log(hoist)
hoist = "value"

と同等になります。したがって、 出力は"value"ではなく、undefinedになります。

巻き上げは、関数宣言がプログラム内で宣言される前に呼び出されます。

myFunction() // No error; logs "hello"
function myFunction() {
  console.log("hello")
}

しかし、変数に代入される関数式には注意が必要です。

myFunction() // Error: `myFunction` is not a function
var myFunction = function() {
  console.log("hello")
}


Q. 即時関数でソースファイルの内容全体をラップする理由は何ですか?

:thinking:回答をみる

この手法は、JavaScriptライブラリでは非常に一般的です。これは、ファイルの内容全体にクロージャを作成してプライベートな名前空間を作成し、それによって異なるJavaScriptモジュールとライブラリ間の潜在的な名前の衝突を回避します。この関数はすぐに呼び出され、名前空間(ライブラリ名)に関数の戻り値が割り当てられます。
const myLibrary = (function() {
  var privateVariable = 2
  return {
    publicMethod: () => privateVariable
  }
})()
privateVariable // ReferenceError
myLibrary.publicMethod() // 2


Q. 静的スコープと動的スコープの違いは何ですか?

:thinking:回答をみる

席的スコープは、関数の定義の場所によってアクセス可能な変数が決まるときを指します。一方、動的スコープは、関数の呼び出しの場所を使用して、使用可能な変数を判別します


Q. 最後の4文字を除く残りの文字列を#でマスクする関数を作成してください。

mask("123456789") // "#####6789"

:thinking:回答をみる

この問題にはいくつかの解法があり、これは単なる一つの方法です。

String.prototype.slice()を使用し、-4を引数として渡すことで、文字列の最後の4文字を取得できます。次に、String.prototype.padStart()を使用して、文字列を元の長さに繰り返しマスク文字で埋め込むことができます。

const mask = (str, maskChar = "#") =>
  str.slice(-4).padStart(str.length, maskChar)


Q. MIME typeとは何で、何に使われるでしょうか?

:thinking:回答をみる

MIMEは、多目的インターネットメール拡張(Multi-purpose Internet Mail Extensions)の略語です。これは、インターネット経由でファイルタイプを分類する標準的な方法として使用されます。


Q. nullundefinedの違いは何ですか?

:thinking:回答をみる

JavaScriptでは、undefinednullは2種類の何もないを表しています。それらの具体的な違いは、nullが明示的であり、undefinedが暗黙的であることです。プロパティが存在しないか、変数に値が与えられていない場合、値はundefinedです。 「値なし」を明示的に示す値としてnullが設定されます。本質的に、無いことすら知らないときはundefinedが使用され、無いことを知っているときはnullが使用されます。


Q. オブジェクトを作成するさまざまな方法を記述してください。いつ他の方法よりその方法が優先されるべきですか?

:thinking:回答をみる

オブジェクトリテラル
1つのデータの発生を格納するためによく使用されます。
const person = {
  name: "John",
  age: 50,
  birthday() {
    this.age++
  }
}
person.birthday() // person.age === 51

コンストラクタ
多くの場合、オブジェクトの複数のインスタンスを作成する必要がある場合に使用され、それぞれは独自のデータを持ち、クラスの他のインスタンスは影響を受けません。コンストラクタを呼び出す前にnew演算子を使用する必要があります。そうしないと、グローバルオブジェクトが変更されてしまいます。

function Person(name, age) {
  this.name = name
  this.age = age
}
Person.prototype.birthday = function() {
  this.age++
}
const person1 = new Person("John", 50)
const person2 = new Person("Sally", 20)
person1.birthday() // person1.age === 51
person2.birthday() // person2.age === 21

ファクトリメソッド
コンストラクタに似た新しいオブジェクトを作成しますが、クロージャを使用してプライベートデータを格納できます。関数またはthisキーワードを呼び出す前に、newを使う必要はありません。
ファクトリ関数は通常、プロトタイプのアイデアを破棄し、すべてのプロパティとメソッドをオブジェクトの独自のプロパティとして保持します。

const createPerson = (name, age) => {
  const birthday = () => person.age++
  const person = { name, age, birthday }
  return person
}
const person = createPerson("John", 50)
person.birthday() // person.age === 51

Object.create()
新しく作成されたオブジェクトのプロトタイプをセットします。

const personProto = {
  birthday() {
    this.age++
  }
}
const person = Object.create(personProto)
person.age = 50
person.birthday() // person.age === 51

2番目の引数は、新しいプロパティを定義するための記述子として機能するObject.create()にも渡すことができます。

Object.create(personProto, {
  age: {
    value: 50,
    writable: true,
    enumerable: true
  }
})


Q. パラメータと引数の違いは何ですか?

:thinking:回答をみる

パラメータは関数定義の変数名であり、引数は呼び出されたときに関数に与えられた値です。
function myFunction(parameter1, parameter2) {
  console.log(arguments[0]) // "argument1"
}
myFunction("argument1", "argument2")


Q. JavaScriptは値渡しか参照渡しですか?

:thinking:回答をみる

JavaScriptは常に値渡しです。ただし、オブジェクトの場合、値はオブジェクトへの参照です。


Q. Promiseとは何ですか?

:thinking:回答をみる

Promiseオブジェクトは、非同期操作の最終的な完了(または失敗)を表し、その結果の値です。
100ms後に結果文字列を標準出力に出力するスニペットを例に挙げることができます。また、エラー処理に使用できるcatchについても注意してください。Promiseはチェーン可能です。
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("result")
  }, 100)
})
  .then(console.log)
  .catch(console.error)

今回はここまでです

意訳が可能な箇所は意訳をしましたが、google翻訳大先生の力をお借りしたため、読みづらい箇所などあるかもしれません。編集リクエスト等いただけると幸いです。
訳者はJavaScriptは約1年ですが、意外と知らなかったようないい問題もあり、非常に勉強になりました。量が増えてきたので前後編に分けたいと思います。また、業務でReactを使用しているためReact編も予定しています。

弊社は福岡のECコンサルを行なっているスタートアップです。もし興味ございましたらDM等お待ちしております。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away