JavaScript

イベントデリゲーションを説明してください

イベントデリゲーションは、子孫要素にイベントリスナーを追加する代わりに、親要素にイベントリスナーを追加する技術です。イベントがDOMをバブリングすることによって子孫要素でトリガーされるたびに、リスナーが発火します。この技術の利点は次のとおりです。

  • 各子孫にイベントハンドラをアタッチする必要がなく、親要素に1つのハンドラだけで済むため、メモリフットプリントが減少します。
  • 削除された要素からハンドラのバインドを解除したり、新しい要素にイベントをバインドしたりする必要がありません。

JavaScriptにおける`this`の仕組みを説明してください

thisには単純な説明がなく、JavaScriptで最も紛らわしい概念の1つです。大雑把な説明としては、thisの値は関数がどのように呼び出されるかに依存します。私はthisに関する多くの説明をオンラインで読みましたが、[Arnav Aggrawal]の説明が最も明確だと感じました。以下のルールが適用されます。

  1. 関数を呼び出すときにnewキーワードが使用されている場合、関数内のthisはまったく新しいオブジェクトです。
  2. applycall、またはbindが関数を呼び出す/作成するために使用されている場合、関数内のthisは引数として渡されるオブジェクトです。
  3. obj.method()のように関数がメソッドとして呼び出される場合、thisはその関数がプロパティであるオブジェクトです。
  4. 関数が自由関数呼び出しとして呼び出された場合、つまり上記の条件のいずれもなしに呼び出された場合、thisはグローバルオブジェクトです。ブラウザではwindowオブジェクトです。厳格モード('use strict')の場合、thisはグローバルオブジェクトではなくundefinedになります。
  5. 上記の複数のルールが適用される場合、上位のルールが優先され、this値を設定します。
  6. 関数がES2015の矢印関数である場合、上記のすべてのルールを無視し、作成時に周囲のスコープのthis値を受け取ります。

詳細な説明については、彼の[Mediumの記事]を参照してください。

ES6でthisの扱いがどのように変わったか、例を挙げて説明できますか?

ES6では、[囲む字句スコープ]を使用する[矢印関数]を使用できます。これは通常便利ですが、呼び出し元が.call.applyを介してコンテキストを制御することを妨げます。その結果、jQueryのようなライブラリがイベントハンドラ関数内でthisを適切にバインドできなくなります。したがって、大規模なレガシーアプリケーションをリファクタリングする際には、この点に留意することが重要です。

プロトタイプ継承がどのように機能するかを説明してください

すべてのJavaScriptオブジェクトは、Object.create(null)で作成されたオブジェクトを除いて、他のオブジェクトへの参照である__proto__プロパティを持ち、これはオブジェクトの「プロトタイプ」と呼ばれます。オブジェクトのプロパティにアクセスしたときにそのオブジェクトにプロパティが見つからない場合、JavaScriptエンジンはオブジェクトの__proto__、そしてその__proto____proto__と次々に調べていき、いずれかの__proto__でプロパティが見つかるか、プロトタイプチェーンの終端に達するまで検索します。この動作は古典的な継承をシミュレートしますが、実際には継承というよりも[委譲]に近いものです。

プロトタイプ継承の例

// 親オブジェクトのコンストラクタ。 function Animal(name) { this.name = name; } // 親オブジェクトのプロトタイプにメソッドを追加。 Animal.prototype.makeSound = function () { console.log('The ' + this.constructor.name + ' makes a sound.'); }; // 子オブジェクトのコンストラクタ。 function Dog(name) { Animal.call(this, name); // 親のコンストラクタを呼び出す。 } // 子オブジェクトのプロトタイプを親のプロトタイプに設定。 Object.setPrototypeOf(Dog.prototype, Animal.prototype); // 子オブジェクトのプロトタイプにメソッドを追加。 Dog.prototype.bark = function () { console.log('Woof!'); }; // Dogの新しいインスタンスを作成。 const bolt = new Dog('Bolt'); // 子オブジェクトのメソッドを呼び出す。 console.log(bolt.name); // "Bolt" bolt.makeSound(); // "The Dog makes a sound." bolt.bark(); // "Woof!"

注意すべき点は次のとおりです。

  • .makeSoundDogで定義されていないため、エンジンはプロトタイプチェーンを遡り、継承されたAnimalから.makeSoundを見つけます。
  • 継承チェーンを構築するためにObject.createを使用することは推奨されなくなりました。代わりにObject.setPrototypeOfを使用してください。

AMDとCommonJSについてどう思いますか?

どちらもモジュールシステムを実装する方法ですが、ES2015が登場するまでJavaScriptにはネイティブに存在しませんでした。CommonJSは同期型であるのに対し、AMD(Asynchronous Module Definition)は明らかに非同期型です。CommonJSはサーバーサイド開発を念頭に設計されており、モジュールの非同期読み込みをサポートするAMDはブラウザ向けに意図されています。

AMDの構文はかなり冗長だと感じますし、CommonJSは他の言語でimport文を記述するスタイルに近いものです。ほとんどの場合、すべてのJavaScriptを1つの連結されたバンドルファイルに提供する場合、非同期読み込みの恩恵を受けられないため、AMDは不要だと感じます。また、CommonJSの構文はNodeのモジュール記述スタイルに近く、クライアントサイドとサーバーサイドのJavaScript開発を切り替える際のコンテキストスイッチングのオーバーヘッドが少なくなります。

ES2015モジュールが同期と非同期の両方の読み込みをサポートしているおかげで、最終的に1つのアプローチに固執できることを嬉しく思います。ブラウザやNodeでまだ完全に展開されていませんが、常にトランスパイラを使用してコードを変換できます。

次のコードがIIFEとして機能しない理由を説明してください: `function foo(){ }();`。適切にIIFEにするには、何を変更する必要がありますか?

IIFEは即時実行関数式(Immediately Invoked Function Expressions)の略です。JavaScriptパーサーはfunction foo(){ }();function foo(){ }();として読み取ります。前者は_関数宣言_であり、後者(括弧のペア)は関数を呼び出そうとする試みですが、名前が指定されていないため、Uncaught SyntaxError: Unexpected token )がスローされます。

これを修正するには、括弧を追加する2つの方法があります: (function foo(){ })()(function foo(){ }())functionで始まる文は_関数宣言_と見なされます。この関数を()で囲むことで、_関数式_となり、その後の()で実行できます。これらの関数はグローバルスコープに公開されず、関数本体内でそれ自体を参照する必要がない場合は、名前を省略することもできます。

void演算子を使用することもできます: void function foo(){ }();。残念ながら、このアプローチには1つの問題があります。与えられた式の評価は常にundefinedになるため、IIFE関数が何かを返しても、それを使用できません。例:

const foo = void (function bar() { return 'foo'; })(); console.log(foo); // undefined

`null`、`undefined`、未宣言の変数の違いは何ですか?これらの状態を確認するにはどうしますか?

**未宣言(Undeclared)**変数は、varlet、またはconstを使用して以前に作成されていない識別子に値を割り当てたときに作成されます。未宣言変数は現在のスコープ外でグローバルに定義されます。厳格モードでは、未宣言変数に割り当てようとするとReferenceErrorがスローされます。未宣言変数はグローバル変数と同様に悪影響があります。何としても避けてください!それらをチェックするには、try/catchブロックでその使用を囲みます。

function foo() { x = 1; // 厳格モードではReferenceErrorがスローされます } foo(); console.log(x); // 1

undefinedの変数は、宣言はされているが値が割り当てられていない変数です。型はundefinedです。関数が実行結果として何も値を返さず、それが変数に割り当てられた場合、その変数もundefinedの値を持ちます。これをチェックするには、厳密等価演算子(===)またはtypeof'undefined'文字列を返します)を使用します。値がnullの場合もtrueを返すため、抽象等価演算子を使用してチェックすべきではないことに注意してください。

var foo; console.log(foo); // undefined console.log(foo === undefined); // true console.log(typeof foo === 'undefined'); // true console.log(foo == null); // true. 間違い、これを使用してチェックしないでください! function bar() {} var baz = bar(); console.log(baz); // undefined

nullの変数は、明示的にnull値が割り当てられたものです。これは値を表し、明示的に割り当てられたという意味でundefinedとは異なります。nullをチェックするには、厳密等価演算子を使用して比較するだけです。上記と同様に、値がundefinedの場合もtrueを返すため、抽象等価演算子(==)を使用してチェックすべきではないことに注意してください。

var foo = null; console.log(foo === null); // true console.log(typeof foo === 'object'); // true console.log(foo == undefined); // true. 間違い、これを使用してチェックしないでください!

個人的な習慣として、私は変数を未宣言のままにしたり、値を割り当てないままにしたりすることは決してありません。まだ使用するつもりがない場合は、宣言後に明示的にnullを割り当てます。ワークフローでリンターを使用している場合、通常は未宣言の変数を参照していないかどうかもチェックできます。

クロージャとは何ですか?また、どのように/なぜ使用しますか?

クロージャとは、関数とその関数が宣言された字句環境の組み合わせです。「字句」とは、字句スコープがソースコード内で変数が宣言された場所を使用して、その変数がどこで利用可能かを決定するという事実を指します。クロージャとは、外側の(囲む)関数が返された後でも、その外側関数の変数(スコープチェーン)にアクセスできる関数です。

なぜそれを使用するのですか?

  • データプライバシー/クロージャによるプライベートメソッドのエミュレーション。一般的に[モジュールパターン]で使用されます。
  • [部分適用またはカリー化]。

`.forEach`ループと`.map()`ループの主な違いは何ですか?また、どちらをどのように選択しますか?

この2つの違いを理解するために、それぞれの関数が何をするのかを見てみましょう。

forEach

  • 配列の要素を反復処理します。
  • 各要素に対してコールバックを実行します。
  • 値を返しません。
const a = [1, 2, 3]; const doubled = a.forEach((num, index) => { // numやindexに対して何かを行います。 }); // doubled = undefined

map

  • 配列の要素を反復処理します。
  • 各要素に対して関数を呼び出すことで、各要素を新しい要素に「マップ」し、結果として新しい配列を作成します。
const a = [1, 2, 3]; const doubled = a.map((num) => { return num * 2; }); // doubled = [2, 4, 6]

.forEach.map()の主な違いは、.map()が新しい配列を返すことです。結果が必要だが元の配列を変更したくない場合は、.map()が明確な選択肢です。単に配列を反復処理する必要がある場合は、forEachで十分です。

匿名関数の典型的な使用例は何ですか?

IIFEで使用して、一部のコードをローカルスコープ内にカプセル化し、宣言された変数がグローバルスコープに漏洩しないようにすることができます。

(function () { // ここに何らかのコード。 })();

一度だけ使用され、他にどこでも使用する必要がないコールバックとして。ハンドラが呼び出しているコードの内部で直接定義されている場合、コードはより自己完結型で読みやすくなり、関数本体を他で検索する必要がありません。

setTimeout(function () { console.log('Hello world!'); }, 1000);

関数型プログラミング構造やLodashへの引数(コールバックに似ています)。

const arr = [1, 2, 3]; const double = arr.map(function (el) { return el * 2; }); console.log(double); // [2, 4, 6]

どのようにコードを整理しますか?(モジュールパターン、古典的継承など)

以前は、Backboneをモデルに使用しており、これはよりOOP的なアプローチを奨励し、Backboneモデルを作成し、それにメソッドをアタッチしていました。

モジュールパターンは依然として優れていますが、最近ではReact/Reduxを使用しており、Fluxアーキテクチャに基づいた単一方向のデータフローを利用しています。私はアプリのモデルをプレーンオブジェクトで表現し、これらのオブジェクトを操作するためのユーティリティ純粋関数を記述します。状態は、他のReduxアプリケーションと同様に、アクションとリデューサーを使用して操作されます。

古典的継承は可能な限り避けます。もし使用するとしても、[これらのルール]に従います。

ホストオブジェクトとネイティブオブジェクトの違いは何ですか?

ネイティブオブジェクトとは、ECMAScript仕様で定義されているJavaScript言語の一部であるオブジェクトです。例えば、StringMathRegExpObjectFunctionなどです。

ホストオブジェクトとは、実行環境(ブラウザまたはNode)によって提供されるオブジェクトです。例えば、windowXMLHTTPRequestなどです。

`function Person(){}`、`var person = Person()`、`var person = new Person()`の違いは何ですか?

この質問はかなり曖昧です。意図しているのはJavaScriptのコンストラクタについて尋ねていると推測するのが最善でしょう。技術的に言えば、function Person(){}は単なる通常の関数宣言です。慣例として、コンストラクタとして使用されることを意図した関数にはPascalCaseを使用します。

var person = Person()Personを関数として呼び出し、コンストラクタとしては呼び出しません。このように呼び出すのは、関数がコンストラクタとして使用されることを意図している場合によくある間違いです。通常、コンストラクタは何も返さないため、コンストラクタを通常の関数のように呼び出すとundefinedが返され、それがインスタンスとして意図された変数に割り当てられます。

var person = new Person()は、new演算子を使用してPersonオブジェクトのインスタンスを作成します。これはPerson.prototypeから継承します。代替案として、Object.create(Person.prototype)のようにObject.createを使用することもできます。

function Person(name) { this.name = name; } var person = Person('John'); console.log(person); // undefined console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined var person = new Person('John'); console.log(person); // Person { name: "John" } console.log(person.name); // "john"

`.call`と`.apply`の違いは何ですか?

.call.applyはどちらも関数を呼び出すために使用され、最初のパラメータは関数内のthisの値として使用されます。ただし、.callは次の引数としてカンマ区切りの引数を取りますが、.applyは次の引数として引数の配列を取ります。これを覚える簡単な方法は、callのCは「comma-separated」のC、applyのAは「an array of arguments」のAと覚えることです。

function add(a, b) { return a + b; } console.log(add.call(null, 1, 2)); // 3 console.log(add.apply(null, [1, 2])); // 3

`Function.prototype.bind`を説明してください。

[MDN]から逐語的に引用します。

bind()メソッドは、新しい関数を作成します。この関数は、呼び出されたときに、そのthisキーワードが指定された値に設定され、新しい関数が呼び出されたときに提供される任意の引数の前に、指定された引数のシーケンスが続きます。

私の経験では、これは他の関数に渡したいクラスのメソッドでthisの値をバインドするのに最も役立ちます。これはReactコンポーネントで頻繁に行われます。

`document.write()`をいつ使用しますか?

document.write()は、document.open()で開かれたドキュメントストリームにテキスト文字列を書き込みます。ページが読み込まれた後にdocument.write()が実行されると、document.openが呼び出され、ドキュメント全体(<head><body>が削除されます!)がクリアされ、コンテンツが指定されたパラメーター値に置き換えられます。そのため、通常は危険であり、誤用されやすいと考えられています。

オンラインには、document.write()が分析コードで使用されたり、[JavaScriptが有効な場合にのみ機能するスタイルを含めたい場合]に使用されたりすると説明する回答もあります。HTML5ボイラープレートでも、[スクリプトを並列に読み込み、実行順序を維持する]ために使用されています!しかし、これらの理由は時代遅れであり、現代ではdocument.write()を使用せずに達成できるのではないかと私は考えています。もし私が間違っていたら訂正してください。

機能検出、機能推論、UA文字列の使用の違いは何ですか?

機能検出(Feature Detection)

機能検出は、ブラウザが特定のコードブロックをサポートしているかどうかを判断し、サポートしているかどうかに応じて異なるコードを実行することで、一部のブラウザでクラッシュ/エラーが発生するのではなく、常に動作するエクスペリエンスを提供できるようにします。例えば:

if ('geolocation' in navigator) { // navigator.geolocationを使用できます } else { // 機能不足を処理します }

[Modernizr]は機能検出を処理するための素晴らしいライブラリです。

機能推論(Feature Inference)

機能推論は機能検出と同様に機能をチェックしますが、それも存在すると仮定して別の関数を使用します。例:

if (document.getElementsByTagName) { element = document.getElementById(id); }

これはあまり推奨されません。機能検出の方が確実です。

UA文字列(UA String)

これはブラウザが報告する文字列で、ネットワークプロトコルのピアが、要求しているソフトウェアユーザーエージェントのアプリケーションタイプ、オペレーティングシステム、ソフトウェアベンダー、またはソフトウェアバージョンを識別できるようにします。navigator.userAgentを介してアクセスできます。ただし、この文字列は解析が難しく、偽装される可能性があります。例えば、ChromeはChromeとSafariの両方として報告します。したがって、Safariを検出するには、Safari文字列が存在し、Chrome文字列が存在しないことをチェックする必要があります。この方法は避けてください。

Ajaxについてできるだけ詳しく説明してください。

Ajax(Asynchronous JavaScript and XML)は、クライアント側で多くのウェブ技術を使用して非同期ウェブアプリケーションを作成するための一連のウェブ開発技術です。Ajaxを使用すると、ウェブアプリケーションは既存のページの表示や動作を妨げることなく、非同期的に(バックグラウンドで)サーバーにデータを送信したり取得したりできます。データ交換層をプレゼンテーション層から切り離すことにより、Ajaxはウェブページ、ひいてはウェブアプリケーションが、ページ全体をリロードする必要なしにコンテンツを動的に変更することを可能にします。実際には、JSONがJavaScriptにネイティブであるという利点があるため、最新の実装ではXMLの代わりにJSONが一般的に使用されます。

XMLHttpRequest APIは非同期通信によく使用されるか、最近ではfetch() APIが使用されます。

Ajaxを使用する利点と欠点は何ですか?

利点

  • インタラクティビティの向上。サーバーからの新しいコンテンツは、ページ全体をリロードする必要なしに動的に変更できます。
  • スクリプトとスタイルシートは一度だけ要求すればよいので、サーバーへの接続を減らすことができます。
  • ページの状態を維持できます。メインのコンテナページがリロードされなかったため、JavaScript変数とDOMの状態が保持されます。
  • 基本的にSPAのほとんどの利点。

欠点

  • 動的なウェブページはブックマークしにくいです。
  • ブラウザでJavaScriptが無効になっている場合は機能しません。
  • 一部のウェブクローラーはJavaScriptを実行しないため、JavaScriptによってロードされたコンテンツを表示できません。
  • Ajaxを使用してデータを取得するウェブページは、取得したリモートデータをクライアントサイドのテンプレートと組み合わせてDOMを更新する必要がある可能性があります。これを行うには、ブラウザでJavaScriptを解析して実行する必要があり、低スペックのモバイルデバイスではこれに苦労する可能性があります。
  • 基本的にSPAのほとんどの欠点。

JSONPの仕組み(そしてそれがAjaxではないこと)を説明してください。

JSONP(JSON with Padding)は、Webブラウザのクロスドメインポリシーを回避するためによく使用されるメソッドです。現在のページからクロスオリジンドメインへのAjaxリクエストは許可されていないためです。

JSONPは、<script>タグを介してクロスオリジンドメインにリクエストを行い、通常callbackクエリパラメータを伴って機能します。例: https://example.com?callback=printData。サーバーはデータをprintDataという関数でラップしてクライアントに返します。

<script> function printData(data) { console.log(`My name is ${data.name}!`); } </script> <script src="[https://example.com?callback=printData](https://example.com?callback=printData)"></script>
printData({ name: 'Yang Shun' });

クライアントはグローバルスコープにprintData関数を持っている必要があり、クロスオリジンドメインからの応答を受信すると、クライアントによって関数が実行されます。

JSONPは安全でない場合があり、いくつかのセキュリティ上の問題があります。JSONPは実質的にJavaScriptであるため、JavaScriptができる他のすべてを実行できます。したがって、JSONPデータの提供元を信頼する必要があります。

最近では、[CORS]が推奨されるアプローチであり、JSONPはハックと見なされています。

JavaScriptテンプレートを使ったことはありますか?もしあれば、どのライブラリを使いましたか?

はい。Handlebars、Underscore、Lodash、AngularJS、JSXです。AngularJSのテンプレートは、ディレクティブで文字列を多用するため、タイプミスが見過ごされがちで、あまり好きではありませんでした。JSXはJavaScriptに近く、学ぶべき構文がほとんどないため、私のお気に入りです。最近では、サードパーティのコードに依存することなく、ES2015のテンプレート文字列リテラルをテンプレート作成の簡単な方法として使用することもできます。

const template = `<div>My name is: ${name}</div>`;

ただし、上記の記述方法では内容がエスケープされないため、テンプレートライブラリとは異なり、XSSの潜在的な危険性があることに注意してください。

「巻き上げ(Hoisting)」を説明してください。

巻き上げ(Hoisting)は、コード内の変数宣言の動作を説明するために使われる用語です。varキーワードで宣言または初期化された変数は、その宣言がモジュール/関数レベルのスコープの最上部に「移動」すると解釈され、これを巻き上げと呼びます。ただし、宣言のみが巻き上げられ、割り当て(もしあれば)は元の場所に残ります。

宣言が実際に移動するわけではないことに注意してください。JavaScriptエンジンはコンパイル中に宣言を解析し、宣言とそのスコープを認識します。宣言がスコープの最上部に巻き上げられると視覚化することで、この動作を理解しやすくなります。いくつかの例で説明しましょう。

console.log(foo); // undefined var foo = 1; console.log(foo); // 1

関数宣言は本体が巻き上げられますが、関数式(変数宣言の形式で記述される)は変数宣言のみが巻き上げられます。

// 関数宣言 console.log(foo); // [Function: foo] foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); } console.log(foo); // [Function: foo] // 関数式 console.log(bar); // undefined bar(); // Uncaught TypeError: bar is not a function var bar = function () { console.log('BARRRR'); }; console.log(bar); // [Function: bar]

letconstで宣言された変数も巻き上げられます。ただし、varfunctionとは異なり、これらは初期化されず、宣言前にアクセスするとReferenceError例外が発生します。変数はブロックの開始から宣言が処理されるまでの間、「一時的なデッドゾーン」にあります。

x; // undefined y; // Reference error: y is not defined var x = 'local'; let y = 'local';

イベントバブリングを説明してください。

DOM要素でイベントがトリガーされると、リスナーがアタッチされていればイベントを処理しようとし、その後、イベントは親要素にバブリングされ、同じことが起こります。このバブリングは、要素の祖先をdocumentまで遡って発生します。イベントバブリングはイベントデリゲーションの背後にあるメカニズムです。

「属性(attribute)」と「プロパティ(property)」の違いは何ですか?

属性はHTMLマークアップで定義されますが、プロパティはDOMで定義されます。違いを説明するために、HTMLに次のようなテキストフィールドがあると想像してください: <input type="text" value="Hello">

const input = document.querySelector('input'); console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello

しかし、テキストフィールドの値を「World!」を追加して変更すると、次のようになります。

console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello World!

JavaScriptの組み込みオブジェクトを拡張するのが良くないのはなぜですか?

JavaScriptの組み込み/ネイティブオブジェクトを拡張するとは、そのprototypeにプロパティ/関数を追加することを意味します。これは最初は良いアイデアに見えるかもしれませんが、実際には危険です。コードがいくつかのライブラリを使用していて、両方がArray.prototypeを同じcontainsメソッドを追加して拡張していると想像してください。実装が互いに上書きされ、これら2つのメソッドの動作が同じでない場合、コードが壊れます。

ネイティブオブジェクトを拡張したい唯一のケースは、ポリフィルを作成したい場合です。これは、JavaScriptの仕様の一部であるものの、古いブラウザであるためにユーザーのブラウザに存在しない可能性があるメソッドに対して、独自の実装を提供することを意味します。

ドキュメントの`load`イベントとドキュメントの`DOMContentLoaded`イベントの違いは何ですか?

DOMContentLoadedイベントは、最初のHTMLドキュメントが完全に読み込まれ、解析されたときに発生します。スタイルシート、画像、サブフレームの読み込みが完了するのを待ちません。

windowloadイベントは、DOMとすべての依存リソースおよびアセットが読み込まれた後にのみ発生します。

`==`と`===`の違いは何ですか?

==は抽象等価演算子であり、===は厳密等価演算子です。==演算子は、必要な型変換を行った後に等価性を比較します。===演算子は型変換を行わないため、2つの値の型が異なる場合、===は単純にfalseを返します。==を使用すると、次のような奇妙なことが起こる可能性があります。

1 == '1'; // true 1 == [1]; // true 1 == true; // true 0 == ''; // true 0 == '0'; // true 0 == false; // true

私の助言は、==演算子は決して使用しないことです。ただし、nullまたはundefinedと比較する際の利便性として、a == nullanullまたはundefinedの場合にtrueを返す場合を除きます。

var a = null; console.log(a == null); // true console.log(a == undefined); // true

JavaScriptにおける同一オリジンポリシーについて説明してください。

同一オリジンポリシーは、JavaScriptがドメインの境界を越えてリクエストを行うことを防ぎます。オリジンは、URIスキーム、ホスト名、ポート番号の組み合わせとして定義されます。このポリシーは、あるページの悪意のあるスクリプトが、そのページのDocument Object Modelを介して、別のウェブページの機密データにアクセスするのを防ぎます。

これを動作させてください:

duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
function duplicate(arr) { return arr.concat(arr); } duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

またはES6で:

const duplicate = (arr) => [...arr, ...arr]; duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

なぜ三項演算子と呼ばれるのですか?「Ternary」という言葉は何を意味しますか?

「Ternary」は3を意味し、三項演算子はテスト条件、"then"式、"else"式の3つのオペランドを受け入れます。三項演算子はJavaScriptに固有のものではなく、なぜこのリストにあるのか分かりません。

`"use strict";`とは何ですか?使用する利点と欠点は何ですか?

'use strict'は、スクリプト全体または個々の関数に厳格モードを有効にするために使用されるステートメントです。厳格モードは、JavaScriptの制限されたバリアントを選択する方法です。

利点:

  • 誤ってグローバル変数を作成するのを不可能にします。
  • そうでなければ黙って失敗する代入を例外としてスローさせます。
  • 削除できないプロパティを削除しようとすると例外をスローさせます(以前は単に効果がありませんでした)。
  • 関数パラメータ名が一意であることを要求します。
  • グローバルコンテキストではthisは未定義になります。
  • いくつかの一般的なコーディングのうっかりミスを捕捉し、例外をスローします。
  • 紛らわしい機能やうまく考えられていない機能を無効にします。

欠点:

  • 一部の開発者が慣れているかもしれない多くの機能が不足しています。
  • function.callerfunction.argumentsへのアクセスがなくなります。
  • 異なる厳格モードで書かれたスクリプトの連結が問題を引き起こす可能性があります。

全体として、私は利点が欠点を上回ると考えており、厳格モードがブロックする機能に頼る必要はありませんでした。厳格モードを使用することをお勧めします。

`100`まで反復処理し、`3`の倍数で**「fizz」**、`5`の倍数で**「buzz」**、`3`と`5`の倍数で**「fizzbuzz」**を出力するforループを作成してください。

[Paul Irish]によるFizzBuzzのこのバージョンをチェックしてください。

for (let i = 1; i <= 100; i++) { let f = i % 3 == 0, b = i % 5 == 0; console.log(f ? (b ? 'FizzBuzz' : 'Fizz') : b ? 'Buzz' : i); }

ただし、面接中に上記を書くことはお勧めしません。長くて明確なアプローチに固執してください。FizzBuzzのより奇抜なバージョンについては、以下の参照リンクを確認してください。

一般的に、ウェブサイトのグローバルスコープをそのままにして、決して触らないのが良いアイデアなのはなぜですか?

すべてのスクリプトはグローバルスコープにアクセスでき、全員がグローバル名前空間を使用して変数を定義すると、衝突が発生する可能性が高くなります。モジュールパターン(IIFE)を使用して、変数をローカル名前空間内にカプセル化してください。

`load`イベントのようなものを使用するのはなぜですか?このイベントには欠点がありますか?代替手段を知っていますか?なぜそれらを使用しますか?

loadイベントは、ドキュメントの読み込みプロセスの最後に発生します。この時点で、ドキュメント内のすべてのオブジェクトがDOM内にあり、すべての画像、スクリプト、リンク、およびサブフレームの読み込みが完了しています。

DOMイベントのDOMContentLoadedは、ページのDOMが構築された後に発生しますが、他のリソースの読み込みが完了するのを待ちません。これは、初期化する前にページ全体が読み込まれる必要がない場合に、特定の場合に好まれます。

シングルページアプリケーションとは何か、またそれをSEOフレンドリーにする方法を説明してください。

以下は、偶然にも私が書いた素晴らしい[Grab Front End Guide]からの抜粋です。

今日のWeb開発者は、作成する製品をウェブサイトではなく、ウェブアプリと呼ぶ傾向があります。この2つの用語に厳密な違いはありませんが、ウェブアプリは非常にインタラクティブで動的であり、ユーザーがアクションを実行し、そのアクションへの応答を受け取ることができます。従来、ブラウザはサーバーからHTMLを受け取ってレンダリングしていました。ユーザーが別のURLに移動すると、ページ全体の更新が必要になり、サーバーは新しいページに新しいHTMLを送信しました。これをサーバーサイドレンダリングと呼びます。

しかし、最新のSPAでは、代わりにクライアントサイドレンダリングが使用されます。ブラウザは、サーバーから最初のページを、アプリ全体に必要なスクリプト(フレームワーク、ライブラリ、アプリコード)とスタイルシートとともに読み込みます。ユーザーが他のページに移動しても、ページの更新はトリガーされません。ページのURLは[HTML5 History API]を介して更新されます。新しいページに必要な新しいデータは、通常JSON形式で、[AJAX]リクエストを介してブラウザによってサーバーから取得されます。その後、SPAは、最初のページロードですでにダウンロードされているJavaScriptを介して、データでページを動的に更新します。このモデルは、ネイティブモバイルアプリの動作に似ています。

利点:

  • アプリがより応答性が高く感じられ、ページ全体の更新によるページ遷移間のちらつきがユーザーには見えません。
  • 各ページロードで同じアセットを再度ダウンロードする必要がないため、サーバーへのHTTPリクエストが少なくなります。
  • クライアントとサーバー間の懸念事項が明確に分離されています。API契約が破られない限り、サーバーコードを変更することなく、異なるプラットフォーム(例:モバイル、チャットボット、スマートウォッチ)用の新しいクライアントを簡単に構築できます。また、API契約が破られない限り、クライアントとサーバーの技術スタックを独立して変更することもできます。

欠点:

  • 複数のページに必要なフレームワーク、アプリコード、およびアセットの読み込みにより、最初のページロードが重くなります。
  • サーバーで追加の手順を行う必要があります。それは、すべてのリクエストを単一のエントリポイントにルーティングするように設定し、そこからクライアントサイドルーティングが引き継ぐことを許可することです。
  • SPAはコンテンツをレンダリングするためにJavaScriptに依存していますが、すべての検索エンジンがクロール中にJavaScriptを実行するわけではなく、ページに空のコンテンツが表示される場合があります。これは、アプリの検索エンジン最適化(SEO)を不注意に損ないます。ただし、ほとんどの場合、アプリを構築している場合、すべてのコンテンツが検索エンジンによってインデックス可能である必要はないため、SEOは最も重要な要素ではありません。これを克服するには、アプリをサーバーサイドレンダリングするか、[Prerender]などのサービスを使用して「ブラウザでJavaScriptをレンダリングし、静的HTMLを保存し、それをクローラーに返す」ことができます。

Promiseやそのポリフィルに関するあなたの経験の範囲はどのくらいですか?

実用的な知識を持っています。Promiseは、将来のある時点で単一の値を生成する可能性があるオブジェクトです。解決された値か、解決されない理由(例:ネットワークエラーが発生した)のいずれかです。Promiseは、fulfilled(成功)、rejected(失敗)、pending(保留中)の3つの状態のいずれかにあります。Promiseの利用者は、fulfilledされた値またはrejectedの理由を処理するためにコールバックをアタッチできます。

一般的なポリフィルには$.deferred、Q、Bluebirdがありますが、すべてが仕様に準拠しているわけではありません。ES2015はPromiseをそのままサポートしており、最近では通常ポリフィルは必要ありません。

コールバックの代わりにPromiseを使用する利点と欠点は何ですか?

利点

  • 読みにくいコールバック地獄を回避できます。
  • .then()を使用して、読みやすい逐次的な非同期コードを簡単に書くことができます。
  • Promise.all()を使用して、並列の非同期コードを簡単に書くことができます。
  • Promiseを使用すると、コールバックのみのコーディングに存在する以下のシナリオは発生しません。
    • コールバックを早すぎるタイミングで呼び出す
    • コールバックを遅すぎるタイミングで呼び出す(またはまったく呼び出さない)
    • コールバックを呼び出す回数が少なすぎる、または多すぎる
    • 必要な環境/パラメータを渡さない
    • 発生する可能性のあるエラー/例外を飲み込む

欠点

  • コードが少し複雑になる(議論の余地あり)。
  • ES2015がサポートされていない古いブラウザでは、使用するためにポリフィルを読み込む必要があります。

JavaScriptにコンパイルされる言語でJavaScriptコードを記述することの利点/欠点は何ですか?

JavaScriptにコンパイルされる言語の例には、CoffeeScript、Elm、ClojureScript、PureScript、TypeScriptなどがあります。

利点:

  • JavaScriptの長年の問題の一部を修正し、JavaScriptのアンチパターンを阻止します。
  • JavaScriptの上に構文糖衣を提供することで、より短いコードを書くことができます。ES5にはそれが不足していると思いますが、ES2015は素晴らしいです。
  • 静的型は、長期的に保守する必要がある大規模プロジェクトにとって素晴らしいものです(TypeScriptの場合)。

欠点:

  • ブラウザはJavaScriptしか実行せず、コードをブラウザに提供する前にJavaScriptにコンパイルする必要があるため、ビルド/コンパイルプロセスが必要です。
  • ソースマップがプリコンパイルされたソースにうまくマッピングされていない場合、デバッグが困難になる可能性があります。
  • ほとんどの開発者はこれらの言語に慣れていないため、学習する必要があります。プロジェクトで使用する場合、チームに立ち上げコストがかかります。
  • コミュニティが小さい(言語による)ため、リソース、チュートリアル、ライブラリ、ツールを見つけるのが難しくなります。
  • IDE/エディターのサポートが不十分な場合があります。
  • これらの言語は常に最新のJavaScript標準に遅れをとります。
  • 開発者は、自分のコードが何にコンパイルされるのかを認識している必要があります。なぜなら、それが実際に実行されるものであり、それが最終的に重要だからです。

実際には、ES2015はJavaScriptを大幅に改善し、より書きやすくしました。最近ではCoffeeScriptの必要性はあまり感じません。

JavaScriptコードのデバッグにはどのようなツールや技術を使用していますか?

  • ReactとRedux
    • [React Devtools]
    • [Redux Devtools]
  • Vue
    • [Vue Devtools]
  • JavaScript
    • [Chrome Devtools]
    • debugger
    • 古き良きconsole.logデバッグ

オブジェクトのプロパティと配列の項目を反復処理するために、どのような言語構成を使用しますか?

オブジェクトの場合:

  • for-inループ - for (var property in obj) { console.log(property); }。ただし、これは継承されたプロパティも反復処理するため、使用する前にobj.hasOwnProperty(property)チェックを追加します。
  • Object.keys() - Object.keys(obj).forEach(function (property) { ... })Object.keys()は、渡されたオブジェクトのすべての列挙可能なプロパティをリストする静的メソッドです。
  • Object.getOwnPropertyNames() - Object.getOwnPropertyNames(obj).forEach(function (property) { ... })Object.getOwnPropertyNames()は、渡されたオブジェクトのすべての列挙可能および非列挙可能なプロパティをリストする静的メソッドです。

配列の場合:

  • forループ - for (var i = 0; i < arr.length; i++)。ここでの一般的な落とし穴は、varが関数スコープでありブロックスコープではないため、ほとんどの場合、ブロックスコープのイテレータ変数が必要になることです。ES2015はブロックスコープを持つletを導入しており、代わりにそれを使用することが推奨されます。したがって、これはfor (let i = 0; i < arr.length; i++)になります。
  • forEach - arr.forEach(function (el, index) { ... })。この構成は、配列要素のみが必要でindexを使用しない場合、より便利な場合があります。また、反復処理を早期に終了できるeveryおよびsomeメソッドもあります。
  • for-ofループ - for (let elem of arr) { ... }。ES6は、StringArrayMapSetなどのイテラブルプロトコルに準拠するオブジェクトをループできる新しいループ、for-ofループを導入しています。これはforループとforEach()メソッドの利点を兼ね備えています。forループの利点は、そこからbreakできることであり、forEach()の利点は、カウンター変数が必要ないため、forループよりも簡潔であることです。for-ofループを使用すると、ループから抜け出す機能と、より簡潔な構文の両方を得ることができます。

ほとんどの場合、私は.forEachメソッドを好みますが、それは実際に行いたいことに依存します。ES6より前は、breakを使用してループを早めに終了する必要がある場合にforループを使用していました。しかし、ES6ではfor-ofループでそれができるようになりました。ループごとにイテレータを複数回インクリメントするなど、さらに柔軟性が必要な場合はforループを使用します。

また、for-ofループを使用する際に、各配列要素のインデックスと値の両方にアクセスする必要がある場合は、ES6のArray entries()メソッドと分割代入を使用できます。

const arr = ['a', 'b', 'c']; for (let [index, elem] of arr.entries()) { console.log(index, ': ', elem); }

ミュータブルオブジェクトとイミュータブルオブジェクトの違いを説明してください。

イミュータビリティは関数型プログラミングの核となる原則であり、オブジェクト指向プログラミングにも多くのものを提供します。ミュータブルオブジェクトとは、作成後に状態を変更できるオブジェクトです。イミュータブルオブジェクトとは、作成後に状態を変更できないオブジェクトです。

JavaScriptにおけるイミュータブルオブジェクトの例は何ですか?

JavaScriptでは、一部の組み込み型(数値、文字列)はイミュータブルですが、カスタムオブジェクトは一般的にミュータブルです。

JavaScriptの組み込みイミュータブルオブジェクトには、MathDateなどがあります。

JavaScriptのプレーンオブジェクトにイミュータビリティを追加/シミュレートする方法をいくつか示します。

オブジェクト定数プロパティ

writable: falseconfigurable: falseを組み合わせることで、本質的にオブジェクトプロパティとして定数(変更、再定義、削除できない)を作成できます。次のように記述します。

let myObject = {}; Object.defineProperty(myObject, 'number', { value: 42, writable: false, configurable: false, }); console.log(myObject.number); // 42 myObject.number = 43; console.log(myObject.number); // 42

拡張の禁止

オブジェクトに新しいプロパティが追加されるのを防ぎたいが、オブジェクトの残りのプロパティはそのままにしておきたい場合は、Object.preventExtensions(...)を呼び出します。

var myObject = { a: 2, }; Object.preventExtensions(myObject); myObject.b = 3; myObject.b; // undefined

非厳格モードでは、bの作成は黙って失敗します。厳格モードでは、TypeErrorがスローされます。

Seal

Object.seal()は「シールされた」オブジェクトを作成します。これは、既存のオブジェクトを取得して、本質的にそれにObject.preventExtensions()を呼び出すだけでなく、既存のすべてのプロパティをconfigurable: falseとマークします。

したがって、新しいプロパティを追加できないだけでなく、既存のプロパティを再構成したり削除したりすることもできません(ただし、その値は変更できます)。

Freeze

Object.freeze()は凍結されたオブジェクトを作成します。これは、既存のオブジェクトを取得して、本質的にそれにObject.seal()を呼び出すだけでなく、すべての「データアクセサー」プロパティをwritable:falseとマークし、その値を変更できないようにします。

このアプローチは、オブジェクト自体で達成できる最高のレベルのイミュータビリティです。これにより、オブジェクトまたはその直接のプロパティへの変更が防止されます(ただし、上記のように、参照されている他のオブジェクトの内容は影響を受けません)。

var immutable = Object.freeze({});

オブジェクトを凍結すると、新しいプロパティをオブジェクトに追加できなくなり、既存のプロパティの削除や変更を防ぎます。Object.freeze()は、オブジェクトの列挙可能性、設定可能性、書き込み可能性、およびプロトタイプを保持します。渡されたオブジェクトを返し、凍結されたコピーは作成しません。

イミュータビリティの利点と欠点は何ですか?

利点

  • 変更検出が容易 - オブジェクトの等価性は、参照等価性を通じて効率的かつ簡単に判断できます。これはReactやReduxでオブジェクトの違いを比較するのに役立ちます。
  • イミュータブルなオブジェクトを持つプログラムは、オブジェクトが時間とともにどのように進化するかを心配する必要がないため、考えるのが簡単です。
  • イミュータブルなオブジェクトが関数から返されたり渡されたりしても、イミュータブルなオブジェクトが変更される可能性がないため、防御的なコピーは不要です。
  • 参照による簡単な共有 - オブジェクトの1つのコピーは別のコピーと同じくらい優れているため、オブジェクトをキャッシュしたり、同じオブジェクトを複数回再利用したりできます。
  • スレッドセーフ - イミュータブルなオブジェクトは、マルチスレッド環境でスレッド間で安全に使用できます。他の並行して実行されているスレッドで変更されるリスクがないためです。
  • ImmutableJSのようなライブラリを使用すると、オブジェクトは構造共有を使用して変更され、似た構造を持つ複数のオブジェクトのメモリ消費が少なくなります。

欠点

  • イミュータブルなデータ構造とその操作を素朴に実装すると、新しいオブジェクトが毎回作成されるため、非常にパフォーマンスが低下する可能性があります。構造共有を活用する効率的なイミュータブルなデータ構造と操作には、ライブラリを使用することをお勧めします。
  • 既存のオブジェクトを変更するのではなく、多くの小さなオブジェクトを割り当てる(および解放する)と、パフォーマンスに影響を与える可能性があります。アロケータまたはガベージコレクタの複雑さは、通常ヒープ上のオブジェクトの数に依存します。
  • グラフなどの循環データ構造は構築が困難です。初期化後に変更できない2つのオブジェクトがある場合、どのようにしてそれらを互いに指すようにできますか?

自分のコードでイミュータビリティを達成するにはどうすればよいですか?

代替案は、const宣言と上記の作成方法を組み合わせることです。「ミューテーション」するオブジェクトの場合は、スプレッド演算子、Object.assignArray.concat()などを使用して、元のオブジェクトを変更するのではなく、新しいオブジェクトを作成します。

例:

// 配列の例 const arr = [1, 2, 3]; const newArr = [...arr, 4]; // [1, 2, 3, 4] // オブジェクトの例 const human = Object.freeze({ race: 'human' }); const john = { ...human, name: 'John' }; // {race: "human", name: "John"} const alienJohn = { ...john, race: 'alien' }; // {race: "alien", name: "John"}

同期関数と非同期関数の違いを説明してください。

同期関数はブロッキングしますが、非同期関数はそうではありません。同期関数では、次のステートメントが実行される前に、現在のステートメントが完了します。この場合、プログラムはステートメントの順序どおりに正確に評価され、いずれかのステートメントに非常に時間がかかる場合、プログラムの実行は一時停止されます。

非同期関数は通常、コールバックをパラメータとして受け入れ、非同期関数が呼び出された直後に次の行で実行が続行されます。コールバックは、非同期操作が完了し、呼び出しスタックが空になったときにのみ呼び出されます。Webサーバーからのデータの読み込みやデータベースへのクエリなどの負荷の高い操作は、メインスレッドが他の操作の実行を続行できるように、非同期的に実行する必要があります(ブラウザの場合、UIがフリーズします)。

イベントループとは何ですか?コールスタックとタスクキューの違いは何ですか?

イベントループは、コールスタックを監視し、タスクキューに処理すべき作業があるかどうかをチェックするシングルスレッドのループです。コールスタックが空で、タスクキューにコールバック関数がある場合、関数はキューから取り出され、実行のためにコールスタックにプッシュされます。

まだPhilip Robert氏のイベントループに関する講演をチェックしていない場合は、ぜひ見てください。JavaScriptに関する最も視聴されているビデオの1つです。

`function foo() {}`と`var foo = function() {}`の間で`foo`の使用法の違いを説明してください。

前者は関数宣言であり、後者は関数式です。主な違いは、関数宣言はその本体が巻き上げられるのに対し、関数式の本体は巻き上げられないことです(変数と同じ巻き上げ動作を持ちます)。巻き上げに関する詳細は、上記の巻き上げに関する質問を参照してください。関数式が定義される前に呼び出そうとすると、Uncaught TypeError: XXX is not a functionエラーが発生します。

関数宣言

foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); }

関数式

foo(); // Uncaught TypeError: foo is not a function var foo = function () { console.log('FOOOOO'); };

`let`、`var`、`const`を使って作成された変数の違いは何ですか?

varキーワードを使って宣言された変数は、それらが作成された関数にスコープが限定されます。また、どの関数の外でも作成された場合は、グローバルオブジェクトにスコープが限定されます。letconstは_ブロックスコープ_であり、最も近い一組の波括弧(関数、if-elseブロック、またはforループ)内でのみアクセス可能です。

function foo() { // すべての変数は関数内でアクセス可能です。 var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; console.log(bar); // bar console.log(baz); // baz console.log(qux); // qux } console.log(bar); // ReferenceError: bar is not defined console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
if (true) { var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; } // varで宣言された変数は、関数スコープ内のどこからでもアクセス可能です。 console.log(bar); // bar // letおよびconstで定義された変数は、定義されたブロックの外からはアクセスできません。 console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined

varは変数を巻き上げることができ、宣言される前にコード内で参照することができます。letconstはこれを許可せず、代わりにエラーをスローします。

console.log(foo); // undefined var foo = 'foo'; console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization let baz = 'baz'; console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization const bar = 'bar';

varで変数を再宣言してもエラーはスローされませんが、letconstはスローします。

var foo = 'foo'; var foo = 'bar'; console.log(foo); // "bar" let baz = 'baz'; let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared

letconstの違いは、letが変数の値の再割り当てを許可するのに対し、constは許可しないことです。

// これは問題ありません。 let foo = 'foo'; foo = 'bar'; // これは例外を引き起こします。 const baz = 'baz'; baz = 'qux';

ES6のクラスとES5の関数コンストラクタの違いは何ですか?

まず、それぞれの例を見てみましょう。

// ES5 関数コンストラクタ function Person(name) { this.name = name; } // ES6 クラス class Person { constructor(name) { this.name = name; } }

単純なコンストラクタでは、非常に似ています。

コンストラクタの主な違いは、継承を使用する場合に現れます。Personをサブクラス化してstudentIdフィールドを追加するStudentクラスを作成する場合、上記に加えて次のようにする必要があります。

// ES5 関数コンストラクタ function Student(name, studentId) { // スーパークラスのコンストラクタを呼び出して、スーパークラスから派生したメンバーを初期化します。 Person.call(this, name); // サブクラス独自のメンバーを初期化します。 this.studentId = studentId; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; // ES6 クラス class Student extends Person { constructor(name, studentId) { super(name); this.studentId = studentId; } }

ES5で継承を使用するのは非常に冗長で、ES6のバージョンの方が理解しやすく、覚えやすいです。

新しいアロー関数構文のユースケースを提案できますか?この新しい構文は他の関数とどのように異なりますか?

アロー関数の明らかな利点の1つは、functionキーワードなしで関数を作成するために必要な構文を簡素化することです。アロー関数内のthisは、それを呼び出すオブジェクトによってthisが決定される通常の関数とは異なり、囲むスコープにバインドされます。字句的にスコープされたthisは、特にReactコンポーネントでコールバックを呼び出す場合に役立ちます。

コンストラクター内のメソッドにアロー構文を使用する利点は何ですか?

コンストラクター内でメソッドとしてアロー関数を使用する主な利点は、thisの値が関数作成時に設定され、その後変更できないことです。したがって、コンストラクターが新しいオブジェクトを作成するために使用される場合、thisは常にそのオブジェクトを参照します。たとえば、firstNameを引数として受け取るPersonコンストラクターがあり、その名前をconsole.logする2つのメソッドがあるとしましょう。1つは通常の関数、もう1つはアロー関数です。

const Person = function (firstName) { this.firstName = firstName; this.sayName1 = function () { console.log(this.firstName); }; this.sayName2 = () => { console.log(this.firstName); }; }; const john = new Person('John'); const dave = new Person('Dave'); john.sayName1(); // John john.sayName2(); // John // 通常の関数は'this'の値を変更できますが、アロー関数はできません john.sayName1.call(dave); // Dave ("this"がdaveオブジェクトになったため) john.sayName2.call(dave); // John john.sayName1.apply(dave); // Dave ("this"がdaveオブジェクトになったため) john.sayName2.apply(dave); // John john.sayName1.bind(dave)(); // Dave ("this"がdaveオブジェクトになったため) john.sayName2.bind(dave)(); // John var sayNameFromWindow1 = john.sayName1; sayNameFromWindow1(); // undefined ("this"がwindowオブジェクトになったため) var sayNameFromWindow2 = john.sayName2; sayNameFromWindow2(); // John

ここでの主なポイントは、通常の関数ではthisを変更できますが、アロー関数ではコンテキストが常に同じままであるということです。したがって、アロー関数をアプリケーションのさまざまな部分に渡しても、コンテキストが変わることを心配する必要はありません。

これはReactクラスコンポーネントで特に役立ちます。クリックハンドラーのようなクラスメソッドを通常の関数で定義し、そのクリックハンドラーを子コンポーネントにプロップとして渡す場合、親コンポーネントのコンストラクターでもthisをバインドする必要があります。代わりにアロー関数を使用すると、「this」をバインドする必要はありません。メソッドは自動的にその囲む字句コンテキストから「this」の値を取得するためです。

高階関数の定義は何ですか?

高階関数とは、1つ以上の関数を引数として受け取り、それを使用して何らかのデータを操作したり、結果として関数を返したりする関数です。高階関数は、繰り返し実行されるいくつかの操作を抽象化することを目的としています。その典型的な例がmapです。mapは配列と関数を引数として受け取ります。mapは、この関数を使用して配列内の各項目を変換し、変換されたデータを含む新しい配列を返します。JavaScriptの他の一般的な例には、forEachfilterreduceがあります。高階関数は配列を操作するだけでなく、別の関数から関数を返す多くのユースケースがあります。Function.prototype.bindはJavaScriptにおけるそのような例の1つです。

Map

各文字列を大文字に変換する必要がある名前の配列があるとします。

const names = ['irish', 'daisy', 'anna'];

命令的な方法は次のようになります。

const transformNamesToUppercase = function (names) { const results = []; for (let i = 0; i < names.length; i++) { results.push(names[i].toUpperCase()); } return results; }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

.map(transformerFn)を使用すると、コードが短くなり、より宣言的になります。

const transformNamesToUppercase = function (names) { return names.map((name) => name.toUpperCase()); }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

オブジェクトまたは配列の分割代入の例を挙げられますか?

分割代入はES6で利用可能な式で、オブジェクトや配列の値を簡潔かつ便利な方法で抽出し、個別の変数に配置できます。

配列の分割代入

// 変数の割り当て。 const foo = ['one', 'two', 'three']; const [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"
// 変数の交換 let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1

オブジェクトの分割代入

// 変数の割り当て。 const o = { p: 42, q: true }; const { p, q } = o; console.log(p); // 42 console.log(q); // true

ES6のテンプレートリテラルは、文字列を生成する上で多くの柔軟性を提供しますが、例を挙げられますか?

テンプレートリテラルは、文字列補間や文字列に変数を含めることを簡単にするのに役立ちます。ES2015より前は、次のようなことを行うのが一般的でした。

var person = { name: 'Tyler', age: 28 }; console.log( 'Hi, my name is ' + person.name + ' and I am ' + person.age + ' years old!', ); // 'Hi, my name is Tyler and I am 28 years old!'

テンプレートリテラルを使用すると、同じ出力を次のように作成できるようになりました。

const person = { name: 'Tyler', age: 28 }; console.log(`Hi, my name is ${person.name} and I am ${person.age} years old!`); // 'Hi, my name is Tyler and I am 28 years old!'

テンプレートリテラルを使用していることを示すために、引用符ではなくバックティックを使用し、${}プレースホルダー内に式を挿入できることに注意してください。

2番目の便利なユースケースは、複数行の文字列を作成することです。ES2015より前は、次のように複数行の文字列を作成できました。

console.log('This is line one.\nThis is line two.'); // This is line one. // This is line two.

または、長い文字列を読み取るためにテキストエディターを右にスクロールする必要がないように、コード内で複数行に分割したい場合は、次のように記述することもできました。

console.log('This is line one.\n' + 'This is line two.'); // This is line one. // This is line two.

しかし、テンプレートリテラルは、追加したスペースをそのまま保持します。たとえば、上記で作成したのと同じ複数行の出力を作成するには、次のようにするだけです。

console.log(`This is line one. This is line two.`); // This is line one. // This is line two.

テンプレートリテラルのもう1つのユースケースは、単純な変数補間のためのテンプレートライブラリの代わりに使用することです。

const person = { name: 'Tyler', age: 28 }; document.body.innerHTML = ` <div> <p>Name: ${person.name}</p> <p>Age: ${person.age}</p> </div> `;

.innerHTMLを使用すると、コードがXSSの影響を受けやすくなる可能性があることに注意してください。ユーザーからデータが来た場合は、表示する前にデータをサニタイズしてください!

カリー関数の例と、この構文が利点をもたらす理由を挙げられますか?

カリー化とは、複数のパラメーターを持つ関数が、一連で呼び出されたときに必要なすべてのパラメーターを1つずつ蓄積する複数の関数に分割されるパターンです。この手法は、関数型スタイルで記述されたコードをより読みやすく、構成しやすくするのに役立ちます。関数をカリー化するには、まず1つの関数として開始し、その後、それぞれ1つのパラメーターを受け入れる関数のシーケンスに分割する必要があることに注意してください。

function curry(fn) { if (fn.length === 0) { return fn; } function _curried(depth, args) { return function (newArgument) { if (depth - 1 === 0) { return fn(...args, newArgument); } return _curried(depth - 1, [...args, newArgument]); }; } return _curried(fn.length, []); } function add(a, b) { return a + b; } var curriedAdd = curry(add); var addFive = curriedAdd(5); var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

スプレッド構文を使用する利点は何ですか?また、レスト構文との違いは何ですか?

ES6のスプレッド構文は、Object.createslice、またはライブラリ関数に頼ることなく、配列やオブジェクトのコピーを簡単に作成できるため、関数型パラダイムでのコーディングに非常に役立ちます。この言語機能は、ReduxおよびRxJSプロジェクトで頻繁に使用されます。

function putDookieInAnyArray(arr) { return [...arr, 'dookie']; } const result = putDookieInAnyArray(['I', 'really', "don't", 'like']); // ["I", "really", "don't", "like", "dookie"] const person = { name: 'Todd', age: 29, }; const copyOfTodd = { ...person };

ES6のレスト構文は、関数に渡される任意の数の引数を含めるための省略形を提供します。これはスプレッド構文の逆のようなもので、データを配列に詰め込むのではなく、データの配列を展開するものであり、関数引数だけでなく、配列やオブジェクトの分割代入でも機能します。

function addFiveToABunchOfNumbers(...numbers) { return numbers.map((x) => x + 5); } const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15] const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4] const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4, }; // e: 1, f: 2, others: { g: 3, h: 4 }

ファイル間でコードを共有するにはどうすればよいですか?

これはJavaScriptの環境に依存します。

クライアント(ブラウザ環境)では、変数/関数がグローバルスコープ(window)で宣言されている限り、すべてのスクリプトがそれらを参照できます。あるいは、よりモジュール的なアプローチとしてRequireJSを介してAsynchronous Module Definition(AMD)を採用します。

サーバー(Node.js)では、CommonJSを使用するのが一般的な方法でした。各ファイルはモジュールとして扱われ、module.exportsオブジェクトにアタッチすることで変数や関数をエクスポートできます。

ES2015は、AMDとCommonJSの両方を置き換えることを目的としたモジュール構文を定義しています。これは最終的にブラウザとNodeの両方の環境でサポートされるでしょう。

静的クラスメンバーを作成したいのはなぜですか?

静的クラスメンバー(プロパティ/メソッド)は、クラスの特定のインスタンスに関連付けられておらず、どのインスタンスがそれを参照しても同じ値を持っています。静的プロパティは通常構成変数であり、静的メソッドは通常インスタンスの状態に依存しない純粋なユーティリティ関数です。