人の人生には必ず浮き沈みがあります。常に日の出のように昇るわけではありませんし、常に惨めな状態になるわけでもありません。アップダウンの繰り返しが人間の修行となる。したがって、上に浮いている人は誇る必要はありませんし、下に沈んでいる人は悲観する必要はありません。私たちは率直かつ謙虚、楽観的かつ進取的であり、前進しなければなりません。——松下幸之助
こんにちは、私の名前はジャン・チェンです。今日のインターネット環境では、誰もが多かれ少なかれ感じているはずです。この衝動的な社会において、常に自分の人格を維持することによってのみ、人はさまざまな利益を認識し、お互いを励まし合うことができます。
2023 年の最新の面接質問を集めたので、常に準備してください。
この記事は、WeChat パブリック アカウントで最初に公開されました: Wild Programmer Jiang Chen
誰でも「いいね!」「収集」「フォロー」大歓迎です
記事一覧
- 2023 年のフロントエンド面接の質問 - コーディングの章
- 2023 年のフロントエンド面接の質問 - CSS
- 2023 年のフロントエンド面接の質問 - HTML
- 2023 年のフロントエンド面接の質問 - React
- 2023 年のフロントエンド面接の質問 - Vue の章
これをJavaScriptで簡単に説明してください
JS はthis
比較的複雑な概念であり、数文で明確に説明することはできません。大まかに言えば、関数の呼び出し方法によって のthis
値が決まります。this
私はインターネット上で に関する記事をたくさん読みましたが、 Arnav Agrawal がより明確にそれを書いています。this
値は次の規則に従います。
function を呼び出すときにキーワードを使用するnew
と、関数の内部はthis
まったく新しいオブジェクトになります。またはメソッドを使用して関数を呼び出すか作成する
場合、関数内のはパラメータとしてこれらのメソッドに渡されるオブジェクトです。関数がオブジェクトのメソッドとして呼び出される場合、関数内には関数を呼び出したオブジェクトが含まれます。たとえば、 が呼び出されると、関数内でオブジェクトにバインドされます。呼び出し元の関数が上記の規則に準拠していない場合、 の値はグローバル オブジェクト ( ) を指します。ブラウザ環境では、の値はオブジェクトを指しますが、厳密モード ( ) では、の値が参照されます。上記のルールの複数が満たされる場合、上位のルール (数値 1 が最高、数値 4 が最低) によっての値が決定されます。関数が のアロー関数である場合、上記のルールはすべて無視され、関数が作成されたコンテキストに設定されます。apply、call
bind
this
this
obj.method()
this
obj
this
global object
this
window
'use strict'
this
undefined
this
ES2015
this
AMD と CommonJS について知っていることを教えてください。
これらはすべてモジュール システムを実装する方法ですが、 がES2015
登場するまでJavaScript
モジュール システムは存在しませんでした。CommonJS
は同期ですが、AMD(Asynchronous Module Definition)
完全名から非同期であることは明らかです。CommonJS
はサーバー側の開発を念頭に置いて設計されており、AMD
ブラウザーにより適したモジュールの非同期読み込みをサポートしています。
AMD
構文は非常に冗長で、他の言語の宣言ステートメントの使用法CommonJS
に近いと思います。すべてをファイルにバンドルすると、非同期ロードの利点が得られないため、import
ほとんどの場合、これを使用する必要はないと思います。また、構文がモジュールの記述スタイルに近くなっているため、フロントエンド開発とバックエンド開発を切り替える際のコンテキスト切り替えのオーバーヘッドが小さくなります。AMD
JavaScript
CommonJS
Node
JavaScript
ES2015
モジュールの読み込みスキームが同期と非同期の両方をサポートし、最終的に 1 つのスキームのみを使用できるようになったことを嬉しく思います。ブラウザや ではまだ完全に展開されていませんがNode
、トランスコーディング ツールを使用して変換できます。
次のコードが IIFE として使用できない理由を説明してください: function foo(){ }(); これを IIFE にするにはどのような変更を加える必要がありますか?
IIFE (Immediately Invoked Function Expressions) は、Immediately Invoked Function Expressions の略です。JavaScript
パーサーはfunction foo(){ }();
に解析しますfunction foo(){ }和();
。このうち、前者は関数宣言であり、後者 (括弧のペア) は名前を指定せずに関数を呼び出そうとしているため、エラーがスローされますUncaught SyntaxError: Unexpected token )
。
変更方法は次のとおりです。もう 1 組のかっこを追加します。形式は(function foo(){ })()
との 2 つがあります(function foo(){ }())
。上記関数はグローバルスコープには公開されませんが、関数内で自身を参照する必要がない場合は関数名を省略できます。
void
演算子:を使用できますvoid function foo(){ }();
。ただし、このアプローチには問題があります。式の値は であるundefined
ため、IIFE
戻り値がある場合は、このアプローチを使用しないでください。例えば
const foo = void (function bar() {
return 'foo';
})();
console.log(foo); // undefined
null、未定義、および未宣言の変数の違いは何ですか? これらのステータス値を確認、判断するにはどうすればよいでしょうか?
var、let
またはを使用して事前に宣言せずに変数に値を代入するとconst
、その変数は未宣言変数 ( undeclared variables
) になります。宣言されていない変数は現在のスコープを離れ、グローバル スコープで定義された変数になります。厳密モードでは、宣言されていない変数に値を割り当てるとReferenceError
エラーがスローされます。グローバル変数の使用と同様、宣言されていない変数の使用も非常に悪い習慣であり、可能な限り避けるべきです。それらを確認するには、それらを使用するコードをtry/catch
ステートメントに配置します。
function foo() {
x = 1; // 在严格模式下,抛出 ReferenceError 错误
}
foo();
console.log(x); // 1
変数が宣言されていても値が割り当てられていない場合、変数の値は です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(foo == undefined); // true. 错误,不要使用非严格相等!
個人的な習慣として、私は宣言されていない変数を決して使用しません。まだ使用されていない変数が定義されている場合は、次のように宣言した後、明示的に値を割り当てます。null
クロージャとは何ですか?なぜクロージャを使用するのでしょうか?
クロージャは、関数とその関数が宣言されている字句環境の組み合わせです。字句スコープで使用されるドメインは、コード内で変数が宣言されている場所によって決まります。クロージャは、たとえ外側の関数によって返されたとしても、外側の (囲んでいる) 関数のスコープにアクセスできる関数です。
なぜクロージャを使用するのでしょうか?
- クロージャを使用してデータをプライベート化するか、プライベート メソッドを模擬します。このアプローチはモジュール モード (
module pattern
) とも呼ばれます。 - 部分引数関数 (
partial applications
) カリー化 (currying
)。
.forEach ループと .map() ループの主な違いを説明してください。どのような状況で使用されますか?
2 つの違いを理解するために、それぞれが何をするのかを見てみましょう。
それぞれに
- 配列内の要素を反復処理します。
- 要素ごとにコールバックを実行します。
- 戻り値はありません。
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
// 执行与 num、index 相关的代码
});
// doubled = undefined
地図
- 配列内の要素を反復処理する
- 新しい配列は、各要素に対して関数を呼び出して、各要素を新しい要素に「マップ」することによって作成されます。
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]
.call と .apply の違いは何ですか?
.call
と.apply
の両方が関数の呼び出しに使用される場合、最初のパラメーターはthis
関数内の値として使用されます。ただし、.call
カンマ区切りのパラメータを次のパラメータとして受け入れ、.apply
パラメータの配列を次のパラメータとして受け入れます。簡単な記憶方法call
は、 C in をカンマ区切り ( comma-separated
) に関連付けapply
、 A in を配列 ( array
) に関連付けることです。
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
これはコンポーネントで行われることがよくあります。
Ajaxについてできるだけ詳しく説明してください。
Ajax (非同期 JavaScript および XML) は、クライアント上で多くの Web テクノロジーを使用して非同期 Web アプリケーションを作成する Web 開発テクノロジーです。Ajax を使用すると、Web アプリケーションは既存のページの表示や動作を妨げることなく、サーバーとの間で非同期 (バックグラウンド) でデータを送受信できます。Ajax では、データ交換層をプレゼンテーション層から分離することにより、Web ページおよび拡張 Web アプリケーションが、ページ全体を再ロードすることなくコンテンツを動的に変更できるようになります。実際、JavaScript が JSON をネイティブにサポートしているため、XML は現在 JSON に置き換えられることがよくあります。
XMLHttpRequest API
非同期通信によく使用されます。最近人気のものもございますfetch API
。
Ajax を使用する利点と欠点は何ですか?
アドバンテージ
- インタラクティブ性の向上。サーバーからの新しいコンテンツは、ページ全体を再ロードすることなく動的に変更できます。
- スクリプトとスタイルを要求する必要があるのは 1 回だけであるため、サーバーへの接続が減少します。
- ステータスはページ上で管理できます。メイン コンテナ ページはリロードされないため、JavaScript 変数と DOM 状態は維持されます。
- 基本的に SPA の利点のほとんどが含まれています。
欠点がある
- 動的な Web ページは収集が困難です。
- ブラウザで JavaScript が無効になっている場合は効果がありません。
- 一部の Web クローラーは JavaScript を実行しないため、JavaScript によってロードされたコンテンツを表示できません。
- 基本的に SPA の欠点のほとんどが含まれています
JSONP がどのように機能するのか、そしてなぜ JSONP が真の Ajax ではないのか説明してください。
JSONP (パディング付き JSON) は、Ajax がクロスドメイン要求を許可しないため、Web ブラウザーでクロスドメイン制限を回避するために一般的に使用される方法です。
JSONP は<script>
、通常、callback
次のようなクエリ パラメーターを使用して、タグを通じてクロスドメイン リクエストを送信しますhttps://example.com?callback=printData
。次に、サーバーは呼び出された関数でデータをラップしprintData
、クライアントに返します。
<!-- https://mydomain.com -->
<script>
function printData(data) {
console.log(`My name is ${data.name}!`);
}
</script>
<script src="https://example.com?callback=printData"></script>
// 文件加载自 https://example.com?callback=printData
printData({name: 'Yang Shun'});
クライアントはそのグローバル スコープに関数を持っている必要がありprintData
、その関数はクロスドメインから応答を受信したときにクライアントによって実行されます。
JSONP にはセキュリティ上の影響がある可能性があります。JSONP は純粋な JavaScript 実装であるため、JavaScript で実行できることはすべて実行できるため、JSONP データのプロバイダーは信頼される必要があります。
現在では、Cross-Origin Resource Sharing (CORS) が推奨される主流の方法であり、JSONP はよりハッキングな方法とみなされています。
変数のホイスティングについて説明してください。
変数ホイスティングは、コード内での変数宣言の動作を説明するために使用される用語です。キーワードを使用してvar
宣言または初期化された変数は、宣言ステートメントを現在のスコープの先頭に「raise」します。ただし、宣言のみがホイスティングをトリガーし、代入ステートメント (存在する場合) はそのまま残ります。いくつかの例を挙げて説明しましょう。
// 用 var 声明得到提升
console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1
// 用 let/const 声明不会提升
console.log(bar); // ReferenceError: bar is not defined
let bar = 2;
console.log(bar); // 2
関数宣言は関数本体をホイストしますが、関数式 (変数宣言の形式で記述された) では変数宣言のみがホイストされます。
// 函数声明
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]
イベントバブリングについて説明してください。
DOM 要素でイベントが発生すると、イベント リスナーがあればイベントを処理しようとし、その後イベントがその親要素にバブルアップして同じことが起こります。最後に、イベントが祖先要素に到達するまでです。イベント バブリングは、イベント委任 (イベント委任) を実装する原理です。
そして= はどう違いますか?
==
は抽象等価演算子ですが、===
は厳密な等価演算子です。==
演算子は、比較の前に必要な型変換を実行します。===
演算子は型変換を実行しないため、2 つの値が同じ型でない場合は、それらの値が直接返されますfalse
。を使用すると==
、次のような特別なことが起こる可能性があります。
1 == '1'; // true
1 == [1]; // true
1 == true; // true
0 == ''; // true
0 == '0'; // true
0 == false; // true
私のアドバイスは、 or と比較するとき、またはisまたはwill が返される場合==
に便利な場合を除いて、演算子を決して使用しないことです。null
undefined
a == null
a
null
undefined
true
var a = null;
console.log(a == null); // true
console.log(a == undefined); // true
JavaScriptの同一生成元ポリシーについて説明してください。
同一オリジン ポリシーにより、JavaScript がクロスオリジン リクエストを行うことができなくなります。ソースは、URI、ホスト名、ポート番号の組み合わせとして定義されます。このポリシーは、ページ上の悪意のあるスクリプトが、ページのドキュメント オブジェクト モデルを通じて別の Web ページ上の機密データにアクセスすることを防ぎます。
Promise とそのポリフィルについてどの程度精通していますか?
それがどのように機能するかを理解してください。Promise
将来のある時点で結果、つまり成功した操作の結果、またはその失敗の理由 (ネットワーク エラーが発生したなど) を生成する可能性のあるオブジェクトです。、 、のPromise
3 つの状態のいずれかになります。ユーザーはコールバック関数を追加して、成功した操作の結果や失敗の理由を処理できます。fulfilled
rejected
pending
Promise
一般的なものにはpolyfill
Polyfill $.deferred
、 Q、Bluebird などがありますが、すべての Polyfill が仕様に準拠しているわけではありません。ES2015 は Promise をサポートしており、現在ポリフィルは一般的に必要ありません。
コールバック関数ではなく Promise を使用するメリットとデメリットは何ですか?
アドバンテージ
- 判読不可能なコールバック地獄を回避します。
- .then() を使用して記述されたシーケンシャル非同期コードはシンプルで読みやすいです。
- Promise.all() を使用すると、並列非同期コードの作成が簡単になります。
欠点がある
- コードの複雑さがわずかに増加します (これについては議論の余地があります)。
- ES2015 をサポートしていない古いブラウザでは、ES2015 を使用するにはポリフィルを導入する必要があります。
同期関数と非同期関数の違いを説明してください。
同期関数はブロックしますが、非同期関数はブロックしません。同期関数では、ステートメントが完了すると、次のステートメントが実行されます。この場合、プログラムはステートメントの順序どおりに評価できますが、いずれかのステートメントに時間がかかると、プログラムの実行が長時間停止します。
非同期関数は通常、パラメータとしてコールバックを受け入れ、非同期関数を呼び出した直後に次の行に実行を継続します。コールバック関数は、非同期操作が完了し、コール スタックが空になった場合にのみ呼び出されます。Web サーバーからのデータのロードやデータベースのクエリなどの高負荷の操作は、時間のかかる操作が完了する (ブラウザーではインターフェイスがフリーズする) までメイン スレッドがブロックすることなく他の操作を実行し続けることができるように、非同期で完了する必要があります。
イベントループとは何ですか? コールスタックとタスクキューの違いは何ですか?
イベント ループは、コール スタックを監視し、タスク キュー内で完了しようとしている作業があるかどうかを確認するシングル スレッド ループです。コール スタックが空で、タスク キューにコールバック関数がある場合、コールバック関数はキューから取り出され、実行のためにコール スタックにプッシュされます。
let、var、constを使用して変数を作成する場合の違いは何ですか?
で宣言された変数のスコープvar
は、その現在の実行コンテキストであり、ネストされた関数または関数の外部で宣言された変数にすることができます。let
およびconst
はブロック スコープです。つまり、最も近い中括弧のセット内 (関数、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
変数をホイストします。これは、変数が宣言される前に使用できることを意味します。変数をプロモートしません。事前に使用するとエラーが報告されますlet
。const
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';
ステートメントを with で繰り返してもvar
エラーは発生しませんが、let
with と をconst
使用するとエラーが発生します。
var foo = 'foo';
var foo = 'bar';
console.log(foo); // "bar"
let baz = 'baz';
let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared
let
とconst
の違いは、let
複数の割り当てが許可されるのに対し、const
許可されるのは 1 つだけであることです。
// 这样不会报错。
let foo = 'foo';
foo = 'bar';
// 这样会报错。
const baz = 'baz';
baz = 'qux';
アロー関数の使用例と他の関数との違いを教えてください。
明らかな利点は、アロー関数を使用すると関数作成の構文を簡素化できることであり、function
アロー関数の前にキーワードを追加する必要がありません。また、アロー関数はthis
、通常の関数とは異なり、現在のスコープのコンテキストに自動的にバインドされます。通常の関数の値は、this
実行時にのみ決定できます。アロー関数のこの機能は、コールバック関数、特にコンポーネントに特に役立ちますReact
。
高階関数の定義は何ですか?
高階関数は、1 つ以上の関数をパラメータとして受け取り、データ処理に使用され、結果として関数を返す関数です。高階関数は、反復操作を抽象化するために使用されます。典型的な例は、map
配列と関数を引数として取ることです。map
この関数を使用して、配列内の各要素を変換し、変換された要素を含む新しい配列を返します。JavaScript のその他の一般的な例としてはforEach
、filter
と がありますreduce
。高階関数は、配列を操作する必要がある場合に使用されるだけでなく、関数が新しい関数を返すユースケースも数多くあります。Function.prototype.bind
ほんの一例です。
地図の例
名前の配列があり、各文字を大文字に変換する必要があるとします。
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
カリー関数の例を挙げていただけますか? その利点は何ですか?
カリー化とは、複数の引数を持つ関数を複数の関数に分割し、連続して呼び出すと、必要な引数をすべて 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]