【面接】中級フロントエンド面接質問録と回答概要

序文

最近、インターネット企業の中級フロントエンド開発エンジニアにインタビューしました。いい人、さあ、手書きの質問やアルゴリズムのテストなどを始めてください。

ここに試験問題を記録します。みんなに役立つかどうかを確認してください。いくつかの質問は忘れています。大まかに記録してみましょう。

目次

--手書きの質問--

1. let と var の違い

2. イベントループ実行結果

3. アルゴリズムに関する質問

4. お約束の検査

5. アルゴリズムの問​​題、有効な括弧の判断

--技術的な質問--

6. diff アルゴリズムについて話す

7. URLを入力してページ表示までの流れ

9. Webpack の作業プロセスと Webpack で使用されているもの

やっと


1. let と var の違い

for(let i = 0; i<5; i++) {

setTimeout(() => console.log(i),0)

}

実行結果とletをvarに変更した実行結果を聞く

私の答え:

そのとき、let ブロックレベルのスコープに依存していることがわかり、私は普段 for ループで let を使うのが好きなので、基本的には正解だったのですが、ループの数を間違えて入力してしまい、絶句しました。

正解:

しましょう: 0 1 2 3 4

だった: 5 5 5 5 5

解析:

var はグローバル変数を宣言し、setTimeout は非同期関数であり、ループが完了すると i が 5 になるため、5 つの 5 が出力されます。

そして、ブロックレベルで、各サイクルには独自のローカルスコープがあるため、出力0〜4

2. イベントループ実行結果

タイトルははっきり覚えていないのですが、おそらく以下の2つだと思います、おそらく私が書いたと思います

setTimeout(function() {
  console.log('1')
  new Promise(function() {
      console.log('2')
  }).then(
    console.log('3')
  )
},0)


new Promise(function() {
  console.log('4')
}).then(
  console.log('5')
)

setTimeout(function() {
  new Promise(function() {
      console.log('6')
  }).then(
    console.log('7')
  )
},0)

正解:

スクリプト開始 2 3 スクリプト終了 1 4

setTimeout(function() {
  console.log('1')
  new Promise(function() {
      console.log('2')
  }).then(
    console.log('3')
  )
},0)


new Promise(function() {
  console.log('4')
}).then(
  console.log('5')
)

setTimeout(function() {
  new Promise(function() {
      console.log('6')
  })
},0)

正解:

4 5 1 2 3 6  

私の答え:

最初の回答は非常にスムーズでしたが、2 番目の回答は行き詰まってしまいました。当時は少し混乱していました。最初の書き込みの結果は、すべてのマクロ タスクを実行してからマイクロ タスク キューに移動することでした。後で、それはマクロタスクを実行し、その後すべてのマイクロタスクを実行することであることに気づきました。

で、その時は最後に1263なのか1236なのかで迷ったんですが、幸いなことに最終的に修正されました。

3. アルゴリズムに関する質問

関数追加があります:

add(1)(1+2+3+4) を実行すると、結果は 11 になります。

add(2)(1+2+3+4) を実行すると、結果は 12 になります。

add(2)(1+2+3+4+1+1) を実行すると、結果は 14 になります。

add関数を書いてください

私の答え:

まずはこの関数の特徴を観察してみます。

しばらく観察すると、最初に入力した値に2回目に入力した値が加算されていることがわかります。

その後、関数は継続的に実行でき、add 関数は確実にメソッドを返す必要があります。

したがって、私の答えは次のとおりです。

初めて書き出します。

function add(num) {
  let result 
  return function(num,...rest){
    let result = rest.reduce((a,b) => a+b)+n
    return result
  }
}

最初の回答については、私の考え方に従って、後の値を前の値に加算します。これにより、後の値は残りのパラメータ ...rest とともに受け取られます。これは、2 回目に渡されるパラメータの数が必ずしも同じであるとは限らないためですが、インタビュアーは、2 回目の呼び出しでは num を渡さなかったことを思い出させました。だから、2回目は2回目の値を超えただけだと気づきました。

そこで、残りのパラメータを引数に置き換えることをすぐに考えました。その後、渡された最初の数値が、クロージャを構成する第1レベルの関数で宣言され、第2レベルの関数が直接使用できるようになりました〜いいですね

function add(num) {
  let n = num
  return function(){
    let arr = Array.from(arguments)
    let result = arr.reduce((a,b) => a+b)+n
    return result
  }
}

4. お約束の検査

var a = Promise.resolve()
var b = Promise.resolve('foo')
var c = Promise.resolve(() => null)
var d = Promise.resolve(() => undefined)
var e = Promise.resolve(() => 'foo')
var f = Promise.resolve(() => Promise.resolve('foo'))
var g = Promise.resolve(() => Promise.reject('err'))
var h = Promise.resolve(() => new Promise(() => {
  Error('err')
}))
console.log(a,b,c,d,e,f,g,h)

a、b、c、d、e、f、g、hの値を書いてください

私の答え:

この質問を見たとき、私は呆然として意味のないことを話しましたが、答えの半分は間違っていました。

正解:
Promise {<履行>: 未定義}

Promise {<履行>: 'foo'}

約束 {<履行>: f}

約束 {<履行>: f}

約束 {<履行>: f}

約束 {<履行>: f}

約束 {<履行>: f}

解析:

MDN の詳細な説明

Promise.resolve()メソッドは、 指定された値で解決されるPromiseオブジェクトを返します。値が Promise の場合、その Promise が返されます。値が thenable (つまり、  「then」メソッドがある) の場合、返された Promise はその thenable に「追従」し、最終状態を取得します。それ以外の場合、返された Promise はその値で満たされます。この関数は、Promise のようなオブジェクト (たとえば、何かに解決される Promise) のネストされたレイヤーを単一のレイヤーに平坦化します。

場合によっては、既存のオブジェクトを Promise オブジェクトに変換する必要があり、これを行うのが Promise.resolve() メソッドです。

Promise.resolve('foo')

に相当

新しい約束(解決 => 解決('foo'))

Promise.resolve() には 4 つのパラメータがあります

最初のタイプ: パラメータなし

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');
// one two three

解決状態の Promise オブジェクトと同等

2 番目のタイプ: 通常の変数または通常のオブジェクト

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

解決状態のPromiseオブジェクトと同等

3 番目: パラメータは Promise インスタンスです

引数が Promise インスタンスの場合、Promise.resolve は変更を加えずにインスタンスを返します。

4 番目のタイプ: パラメータは thenable オブジェクトです

//thenable对象指的是具有then方法的对象,比如下面这个对象

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
//Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
//thenable对象的then方法执行后,对象p1的状态就变为resolved,
//从而立即执行最后那个then方法指定的回调函数,输出 42

5. アルゴリズムの問​​題、有効な括弧の判断

メソッドを作成し、括弧のみを含む文字列を入力します ((){}[] [(),{}] {[()]} これらは true を返します

([)] (}) これは false を返します

私の答え:

この質問を見たとき、私はこう思いました: よかった、Likou でブラシをかけたら安定しました! もう一度考えてみると、やり方を忘れていました。ジュオ!

最初の答え:

私の考えは、まず空文字列か偶数かを判断し、特殊な場合を除外することです。

次に、文字列をたどり、要素 ( 、 )、{ 、 } 、[ 、 ] の 6 つの条件 (たとえば、左括弧の場合、その右側は右括弧である必要がある) をそれぞれ判断します。

しかし!インタビュアーは、(){}[] のみを判断できると示唆しました。では、{[()]} はどうでしょうか?

2 番目の答え:

私は、バックハンドで別の解決策を考えました。ダブル ポインタで、文字列の 2 つの層をトラバースし、最初の層が順方向、2 番目の層が逆方向で、たとえば、最初の層が左括弧の場合、2 番目の層は右括弧でなければなりません。

しかし!インタビュアーは、これは {[()]} の状況を判断することしかできないと示唆しました

3 回目の答え:

ジュオ!なんだろう、混乱してる。突然思いつき、一致する括弧を 1 つずつ見つけて、文字列から一致する括弧を削除し、最後に文字列に何が残っているかを確認することを考えました。

このとき、面接官も「データ構造の変更やスタックの利用を検討してみてもいいよ」と思い出させてくれました。実際、私のアイデアは基本的にスタックと同じですが、異なります。

正解:

var isValid = function(s) {
    const n = s.length;
    if (n % 2 === 1) {
        return false;
    }
    const pairs = new Map([
        [')', '('],
        [']', '['],
        ['}', '{']
    ]);
    const stk = [];
    for (let ch of s){
        if (pairs.has(ch)) {
            if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
                return false;
            }
            stk.pop();
        } 
        else {
            stk.push(ch);
        }
    };
    return !stk.length;
};


この時点で、手書きに関する質問は終了し、次にいくつかの技術的な質問が行われます。

6. diff アルゴリズムについて話す

私の答え:

1. Vue の差分アルゴリズムは、両側から中間ノードまでを比較します。また、比較しながら更新するにはダブルポインタを使用します。React の差分アルゴリズムは左から右に比較され、比較中に変更されたノードがパッチ ツリーに生成され、比較後にパッチが適用されます。

2. diff アルゴリズムは通常、ノードの削除またはソート後に diff 比較の精度を保証できるように、ノード キー、つまり一意の値を指定する必要があります。

3. vue2 と比較して、vue3 の差分アルゴリズムは最適化されており、コンパイル段階で各ノードが動的ノードであるかどうかを判断します。ノード上に v-on、:class などのシンボルがあるかどうかを確認して、動的ノードを収集してブロック ツリーを生成します。dom が変更された場合は、ブロック ツリーのみを比較します。vue3 では静的ノードに静的な改良が加えられているため、静的ノードは 1 回だけレンダリングされます。

4. vue3 の差分アルゴリズムも、patchFlag を使用して動的ノードをマークし、何が変更されたかをより効率的に判断します。

7. URLを入力してページ表示までの流れ

1. URLが完全に入力されていない場合は、ブックマークや履歴レコードなどを検索して、アドレスをインテリジェントに入力するよう求めて入力することができます。

2. 入力はドメイン名であるため、ドメイン名にマッピングされた IP アドレスを最初に解決する必要があります。したがって、DNS 解決を最初に実行する必要があります。最初にブラウザー キャッシュがあるかどうかを確認し、ない場合は、ルート DNS サーバー、次にトップレベル ドメイン ネーム サーバー、最後に権威ドメイン ネーム サーバーを要求する前に、ローカル DNS サーバー、つまりローカル オペレーターに要求します。見つかるまで。

3. IP アドレスを解析した後、TCP 接続を確立します。

4. ブラウザのネットワーク プロセスはリクエストを開始し、3 ウェイ ハンドシェイクでデータをリクエストし、データを取得した後、4 回ウェーブして TCP 接続を解放します。

5. ブラウザ ブラウザ プロセスはレンダリングを開始する準備ができており、準備が整うと、ipc プロセスが通信してレンダリング プロセスにレンダリングを開始するように通知します。

6. レンダリングプロセスは、レンダリングを開始し、最初に html を解析し、dom ツリーを生成し、次に css ルールを解析し、css ツリーを生成し、結合してレンダー ツリーを生成します。このとき、レンダー ツリーは head タグと display: none ノードを削除するため、dom ツリーの構造とまったく同じではありません。最終的なレイアウト、図面。

8. Webpack の作業プロセスと Webpack で使用されているもの

作業プロセスに関しては、当時私は正しく理解できていなかったかもしれませんが、ast 抽象構文ツリーについて何かを言いました。実際にはこうなるはずです:

  • compile コンパイルを開始する
  • make モジュールとその依存モジュールをエントリ ポイントから分析し、それらのモジュール オブジェクトを作成します
  • build-module ビルディングブロック
  • after-compile 完全なビルド
  • seal パッケージのビルド結果
  • emit 各チャンクを結果ファイルに出力します
  • after-emit 完全な出力

次に、私が言ったことを使用します。

1. svgなどのローダーの設定

2. オープンソースマップ

3. gzip圧縮を有効にする

4. クロスドメインの構成

5. 下請け

6. プラグインをインストールする

7. サードパーティのライブラリはオンデマンドでロードされます

やっと

間違っている点や追加すべき点がありましたら、修正または追加してください。

おすすめ

転載: blog.csdn.net/qq_38974163/article/details/124218610