8つの古典的なJavaScriptの顔の質問

JavaScriptはどこ彼の主な走行ブラウザは、言語の非常に重要なフロントエンドの開発です。JavaScriptは非常に興味深い言語ですが、彼はコンセプトをたくさん持っている、それはしばしば無視されます。例えば、プロトタイプ、閉鎖、プロトタイプチェーン、イベントループ、およびので、これらの概念を、多くの開発者のJS少し研究。

だから、今日は、来て、次の質問を見て、あなたが最初に答えるためにしようと、考えることがあります。

八の顔質問

質問1:次のコードは、どのようなブラウザコンソールに出力しますか?
15205

質問2:私たちは聞かせてまたはその代わりにVARのconstのを使用する場合は、出力が同じです
15208

質問3:そこ要素「NEWARRAY」?
15213

質問4:私たちは、ブラウザのコンソールで「FOO」関数を実行すると、スタックオーバーフローエラーにつながるのだろうか?

15217
質問5:あなたは、コンソール、ページ(タブ)で次の関数を実行すると、応答があるかどう
15221

質問6:次の文は何らかの方法で私たちは型エラーを発生させずに事業を展開することができます
15224

質問7:あなたは次のコードを実行すると、コンソールに何を印刷するのだろうか?
15228
質問8:xGetter()どのような値を出力しますか?
15232

答え

私たちは、問題外の前の例を持って、我々は、これらの質問に対する私たちの答えを分析するために、一つ一つを最初から最後まであなたにいくつかの学習のアイデアを与えるだろう

質問1:
変数が数値型を定義することはできません何の割り当てがないため、varキーワードは、メモリ内のJavaScriptで推進し、オープンスペースされます使用して宣言したので、未定義のデフォルト値を割り当てます。初期の本当の価値は、あなたの中で起こっているVAR変数の宣言は、割り当て位置を決定します。それと同時に、私たちが知っている必要があり、変数宣言のvarスコープの関数である、つまり、私たちは、ローカルおよびグローバル変数を区別し、できるようにする必要があるとのconstブロックスコープです。だから我々は問題を解決するためのプロセスはこれです実行します。

var a = 10; // 全局作用域,全局变量。a=10
function foo() { // var a //的声明将被提升到到函数的顶部。 // 比如:var a console.log(a); // 打印 undefined // 实际初始化值20只发生在这里 var a = 20; // local scope }

下の図では、少し良く理解するために
15257
そう質問1の答えは:未定義

问题 2:
let和const声明可以让变量在其作用域上受限于它所在的块、语句或表达式中。和var不同的地方在于,这两个声明的变量,不会被提升。并且我们会有一个称为暂时死区(TDZ)。如果访问TDZ中的变量的话,就会报ReferenceError,因为他们的的作用域是在他们声明的位置的,不会有提升。所以必须在执行到声明的位置才能访问。

var a = 10; // 全局使用域
function foo() { // TDZ 开始 // 创建了未初始化的'a' console.log(a); // ReferenceError // TDZ结束,'a'仅在此处初始化,值为20 let a = 20; }

图解:
15287
问题2答案:ReferenceError: a is not defined

问题3:

这个问题,是循环结构会给大家带来一种块级作用域的误区,在for的循环的头部使用var声明的变量,就是单个声明的变量绑定(单个存储空间)。在循环过程中,这个var声明的i变量是会随循环变化的。但是在循环中执行的数组push方法,最后实际上是push了i最终循环结束的3这个值。所以最后push进去的全都是3。

// 误解作用域:认为存在块级作用域
var array = [];
for (var i = 0; i < 3; i++) { // 三个箭头函数体中的每个'i'都指向相同的绑定, // 这就是为什么它们在循环结束时返回相同的值'3'。 array.push(() => i); } var newArray = array.map(el => el()); console.log(newArray); // [3, 3, 3]

图解:
15306
如果想记录每一次循环的值下来,可以使用let声明一个具有块级作用域的变量,这样为每个循环迭代创建一个新的绑定。

// 使用ES6块级作用域
var array = [];
for (let i = 0; i < 3; i++) { // 这一次,每个'i'指的是一个新的的绑定,并保留当前的值。 // 因此,每个箭头函数返回一个不同的值。 array.push(() => i); } var newArray = array.map(el => el()); console.log(newArray); // [0, 1, 2]

还有解决这个问题的另外一种解决方案就是使用闭包就好了。

let array = [];
for (var i = 0; i < 3; i++) { array[i] = (function(x) { return function() { return x; }; })(i); } const newArray = array.map(el => el()); console.log(newArray); // [0, 1, 2] 

问题3答案:3,3,3

问题4
JavaScript的并发模式基于我们常说的”事件循环“。
浏览器是提供运行时环境来给我们执行JS代码的。浏览器的主要组成包括有调用堆栈,事件循环,任务队列和WEB API。像什么常用的定时器setTimeout,setInterval这些全局函数就不是JavaScript的一部分,而是WEB API给我们提供的。
15335
JS调用栈是后进先出(LIFO)的。引擎每次从堆栈中取出一个函数,然后从上到下依次运行代码。每当它遇到一些异步代码,如setTimeout,它就把它交给Web API(箭头1)。因此,每当事件被触发时,callback 都会被发送到任务队列(箭头2)。
事件循环(Event loop)不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。每当调用堆栈(call stack)为空时,Event loop获取回调并将其放入堆栈(stack )(箭头3)中进行处理。请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈。

好了,现在有了前面这些知识,我们可以看一下这道题的讲解过程:
实现步骤:

  1. 调用 foo()会将foo函数放入调用堆栈(call stack)。
  2. 在处理内部代码时,JS引擎遇到setTimeout。
  3. 然后将foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空
  4. 计时器被设置为0,因此foo将被发送到任务队列(箭头2)。
  5. 由于调用堆栈是空的,事件循环将选择foo回调并将其推入调用堆栈进行处理。
  6. 进程再次重复,堆栈不会溢出。

问题4答案:堆栈不会溢出。

问题5:
在很多时候,很多做前端开发的同学都是认为循环事件图中就只会有一个任务列表。但事实上不是这样的,我们是可以有多个任务列表的。由浏览器选择其中一个队列并在该队列进行处理回调。
从底层来看,JavaScript中是可以有宏认为和微任务的,比如说setTimeout回调是宏任务,而Promise回调是微任务。

他们有什么区别呢?
主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个低堆入堆栈,但是微任务队列总是在执行后返回到事件之前清空。所以,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面。

然后我们再回到我们前面讲的问题5中:

function foo() {
  return Promise.resolve().then(foo); }; 

我们这段代码,每次我们去调用【foo】的时候,都会在微任务队列上加另一个【foo】的回调,因此事件循环没办法继续去处理其他的事件了(比如说滚动,点击事件等等),直到该队列完全清空位置。因此,不会执行渲染,会被阻止。

问题5答案:不会响应。

问题6:
在我们做面试题的时候,展开语法和for-of语句去遍历iterable对象定义要遍历的数据。其中我们要使用迭代器的时候,Array和Map都是有默认迭代操作的内置迭代器的。
但是,对象是不可迭代的,也就是我们这道题里的,这是一个对象的集合。但是我们可以使用iterable和iterator协议来把它变成可以迭代的。
在我们研究对象的时候,如果一个对象他实现了@@iterator方法,那么它就是可以迭代的。这意味着这个对象(在他的原型链上的一个对象)必须是又@@iterator键的属性的,然后我们就可以利用这个键,通过常量Symbol.iterator获得。
下面是这道题的举例写法:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {
    // iterator 是一个具有 next 方法的对象, // 它的返回至少有一个对象 // 两个属性:value&done。 // 返回一个 iterator 对象 return { next: function() { if (this._countDown === 3) { const lastValue = this._countDown; return { value: this._countDown, done: true }; } this._countDown = this._countDown + 1; return { value: this._countDown, done: false }; }, _countDown: 0 }; }; [...obj]; // 打印 [1, 2, 3]

質問6回答:プログラムとして、あなたはTypeError例外を回避することができます。

質問7:
この問題を見て、我々は最初のためのインループ繰り返し処理自体列挙プロパティとオリジナルのプロトタイプのプロパティから継承されたオブジェクトを理解する必要があります。列挙プロパティは、プロパティがためにインループ中にアクセスすることが可能です。
我々は後に、この知識の前提を知ったとき、私たちはこの質問を見て、あなたは実際にのみこの質問が印刷され、それらの特定の属性を印刷することを知っているだろう。

var obj = { a: 1, b: 2 }; //a,b 都是可枚举属性

// 将{c:3}设置为'obj'的原型, // 并且我们知道for-in 循环也迭代 obj 继承的属性 // 从它的原型,'c'也可以被访问。 Object.setPrototypeOf(obj, { c: 3 }); // 我们在'obj'中定义了另外一个属性'd', // 但是将'enumerable'可枚举设置为false。 这意味着'd'将被忽略。 Object.defineProperty(obj, "d", { value: 4, enumerable: false }); //所以最后使用for-in遍历这个对象集合,那就是只能遍历出可枚举属性 for (let prop in obj) { console.log(prop); } // 也就是只能打印 // a // b // c

示す
15436
:問題7回答A、B、Cを

質問8:
まず、私たちが見ることができるのvar xはありませんstrictモードで、Xは、ウィンドウオブジェクトの直接プロパティであり、グローバルトラバーサルです。このコードでは、我々は、最も重要なことは、これは常に呼び出し元のメソッドのオブジェクトを指している、問題にこのオブジェクトのポイントを理解することである必要があります。したがって、オブジェクトにこのFOOポイントのFOO、xGetter()場合、FOOのx値のプロパティのリターンは90です。しかし)xGetter(の場合には、彼は、fooののgetX直接呼び出し()メソッドですが、この点はxGetter内のスコープである場合には、ウィンドウオブジェクトに指摘され、その後、ポイントがあることグローバル変数xであります値は10です。

var x = 10; // 全局变量
var foo = {
    x: 90,//foo对象的内部属性 getX: function() { return this.x; } }; foo.getX(); // 此时是指向的foo对象, //所以打印的是X属性 值就是90 let xGetter = foo.getX;//xGetter是在全局作用域, //这里的this就是指向window对象 xGetter(); // 打印 10

15466
質問8回答:10

おすすめ

転載: www.cnblogs.com/DeadBoy/p/11460628.html