ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript で最も一般的な 3 つの問題の詳細なコード説明

JavaScript で最も一般的な 3 つの問題の詳細なコード説明

黄舟
黄舟オリジナル
2017-03-23 14:49:031056ブラウズ

JavaScript は、最新のすべてのブラウザーの公式言語です。そのため、JavaScript に関する質問はさまざまな開発者インタビューで取り上げられます。この記事では主に次の内容を共有します。 JavaScript でよくある 3 つの面接の質問を一緒に見てみましょう

この記事は最新の JavaScript ライブラリに関するものではありません。 -日の開発実践や ES6 の新しい機能 代わりに、これら 3 つの質問は、私自身も面接でよく聞かれましたし、私の友人からも聞かれたことがあると聞きました

もちろん、そうすべきです。 JavaScript の面接前にこれら 3 つの質問を学習するだけではありません。次の面接に向けてより良い準備をする方法はたくさんあります。ただし、ここでは面接官が尋ねる可能性のある 3 つの質問を紹介します。JavaScript 言語の理解と DOM の習熟度を判断してください。

始めましょう!面接官は通常、サードパーティのライブラリを使用しているかどうかを確認したいと考えているため、以下の例ではネイティブ JavaScript を使用していることに注意してください ( を使用して JavaScript と DOM を理解する方法) jQuery

)

質問 #1: イベント委任

注: イベント委任、時間委任などとも呼ばれます

アプリケーションを構築する場合、イベント リスナーをボタンにバインドする必要がある場合があります。ユーザーが要素を操作するときに何らかのアクションを実行するためにページ上にテキストまたは画像を追加します

単純な ToDo リストを例に挙げると、担当者は、次のときに何らかのアクションを実行したいと伝えるかもしれません。ユーザーはリスト項目の 1 つをクリックします

HTML コードが次のようになっていると仮定して、この関数を JavaScript で実装してほしいと考えています:

<ul id="todo-app">
 <li class="item">Walk the dog</li>
 <li class="item">Pay bills</li>
 <li class="item">Make dinner</li>
 <li class="item">Code for one hour</li>
</ul>

次のように要素にイベント リスナーをバインドします。

document.addEventListener(&#39;DOMContentLoaded&#39;, function() {
 
 let app = document.getElementById(&#39;todo-app&#39;);
 let items = app.getElementsByClassName(&#39;item&#39;);
 
 // 将事件侦听器绑定到每个列表项
 for (let item of items) {
 item.addEventListener(&#39;click&#39;, function() {
 alert(&#39;you clicked on item: &#39; + item.innerHTML);
 });
 }
 
});
これは機能しますが、問題は、4 つの要素である各リスト項目にイベント リスナーを個別にバインドしていることです。ただし、10,000 個の項目が ToDo リストに追加された場合はどうなるでしょうか。おそらくやるべきことがたくさんあるでしょう) その場合、関数は 10,000 個の個別のイベント リスナーを作成し、それぞれを DOM にバインドします

面接では、最初に最大値を尋ねる方が良いでしょう。ただし、ユーザーが入力できる項目の数に制限はないため、アプリケーションに数百もの項目が入力される可能性がある場合は、より効率的なソリューションを使用する必要があります。イベント リスナーを使用する場合、より効率的な解決策は、実際に 1 つのイベント リスナーをコンテナ全体にバインドし、実際のクリックで各要素にアクセスできるようにすることです。これはイベント委任と呼ばれ、各要素を個別にバインドしてイベント処理をより効率的にします。

イベント委任を使用したコード:

document.addEventListener(&#39;DOMContentLoaded&#39;, function() {
 
 let app = document.getElementById(&#39;todo-app&#39;);
 
 // 事件侦听器绑定到整个容器上
 app.addEventListener(&#39;click&#39;, function(e) {
 if (e.target && e.target.nodeName === &#39;LI&#39;) {
 let item = e.target;
 alert(&#39;you clicked on item: &#39; + item.innerHTML);
 }
 });
 
});

質問 #2: ループ内でクロージャを使用する

クロージャは、面接官があなたの言語の知識を評価できるように、また、いつ使用するかを知ることができるように、面接で頻繁に登場します。閉鎖。 クロージャの本質は、

内部関数

がスコープ外の変数にアクセスすることです。クロージャを使用すると、プライベート変数やファクトリ関数の作成などを実装できます。クロージャの使用に関する一般的なインタビューの質問は次のとおりです: 整数のリストをループし、3 秒の遅延後に各要素のインデックスを出力する関数を作成します。

私がこの問題を最もよく見る (しかし間違っている) のは、次のような実装の場合です:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
 setTimeout(function() {
 console.log(&#39;The index of this number is: &#39; + i);
 }, 3000);
}

上記のコードを実行すると、3 秒の遅延の後、出力が実際には 4 であることがわかります。予想される 0、1、2、3。 なぜこれが起こるのかを正しく理解するには、JavaScript を使用することが役立ちます。これは、面接官が実際に意図したものです。

その理由は、setTimeout 関数がその外側のスコープ (つまり、私たちがよくクロージャと呼ぶもの) にアクセスできる関数を作成し、各ループにインデックス i が含まれるためです。

3 秒後、関数が実行され、i の値が出力されます。ループ サイクルは 0、1、2、3、4 を通過し、ループは最終的に 4 で停止するため、ループの最後では 4 になります。

この問題を解決するには、実際にはいくつかの正しい記述方法があります。以下に 2 つあります:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
 // 通过传递变量 i
 // 在每个函数中都可以获取到正确的索引
 setTimeout(function(i_local) {
 return function() {
 console.log(&#39;The index of this number is: &#39; + i_local);
 }
 }(i), 3000);
}
const arr = [10, 12, 15, 21];
for (let i = 0; i < arr.length; i++) {
 // 使用ES6的let语法,它会创建一个新的绑定
 // 每个方法都是被单独调用的
 // 更多详细信息请阅读: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
 setTimeout(function() {
 console.log(&#39;The index of this number is: &#39; + i);
 }, 3000);
}

問題 #3: 関数のデバウンス (デバウンス)


有一些浏览器事件可以在很短的时间内快速启动多次,例如调整窗口大小或向下滚动页面。例如,如果将事件侦听器绑定到窗口滚动事件上,并且用户继续非常快速地向下滚动页面,你的事件可能会在3秒的范围内被触发数千次。这可能会导致一些严重的性能问题。

如果你在面试中讨论构建应用程序和事件,如滚动,窗口调整大小,或键盘按下的事件时,请务必提及 函数防抖动(Debouncing) 和/或 函数节流(Throttling)来提升页面速度和性能。一个真实的案例,来自 guest post on css-tricks:

在2011年,一个问题在Twitter上被提出:当你滚动Twitter feed时,它会会变得非常慢甚至未响应。John Resig 就这个问题发布了一篇博文,它解释了直接绑定函数到scroll事件上是多么糟糕的事。

函数防抖动(Debouncing) 是解决这个问题的一种方式,通过限制需要经过的时间,直到再次调用函数。一个正确实现函数防抖的方法是:把多个函数放在一个函数里调用,隔一定时间执行一次。这里有一个使用原生JavaScript实现的例子,用到了作用域、闭包、this和定时事件:

// debounce函数用来包裹我们的事件
function debounce(fn, delay) {
 // 持久化一个定时器 timer
 let timer = null;
 // 闭包函数可以访问 timer
 return function() {
 // 通过 &#39;this&#39; 和 &#39;arguments&#39;
 // 获得函数的作用域和参数
 let context = this;
 let args = arguments;
 // 如果事件被触发,清除 timer 并重新开始计时
 clearTimeout(timer);
 timer = setTimeout(function() {
 fn.apply(context, args);
 }, delay);
 }
}

当这个函数绑定在一个事件上,只有经过一段指定的时间后才会被调用。

你可以像这样去使用这个函数:

// 当用户滚动时函数会被调用
function foo() {
 console.log(&#39;You are scrolling!&#39;);
}
 
// 在事件触发的两秒后,我们包裹在debounce中的函数才会被触发
let elem = document.getElementById(&#39;container&#39;);
elem.addEventListener(&#39;scroll&#39;, debounce(foo, 2000));

函数节流是另一个类似函数防抖的技巧,除了使用等待一段时间再调用函数的方法,函数节流还限制固定时间内只能调用一次。所以一个事件如果在100毫秒内发生10次,函数节流会每2秒调用一次函数,而不是100毫秒内全部调用。

总结

以上がJavaScript で最も一般的な 3 つの問題の詳細なコード説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。