目次
質問 1: ブラウザのコンソールには何が表示されますか? " >質問 1: ブラウザのコンソールには何が表示されますか?
質問 2: var の代わりに let または const を使用した場合、出力は同じですか" >質問 2: var の代わりに let または const を使用した場合、出力は同じですか
質問 3: 内容は何ですか「newArray」要素? " >質問 3: 内容は何ですか「newArray」要素?
質問 4: ブラウザ コンソールで「foo」関数を実行すると、スタック オーバーフロー エラーが発生しますか? " > 質問 4: ブラウザ コンソールで「foo」関数を実行すると、スタック オーバーフロー エラーが発生しますか?
質問 5: コンソールで次の関数を実行した場合、ページ (タブ) の UI は引き続き応答しますか?" >質問 5: コンソールで次の関数を実行した場合、ページ (タブ) の UI は引き続き応答しますか?
質問 6: 型エラーを発生させずに、次のステートメントのスプレッド操作を何とか使用できますか " >質問 6: 型エラーを発生させずに、次のステートメントのスプレッド操作を何とか使用できますか
質問 7: 次のコード スニペットを実行すると、コンソールに何が表示されますか? " > 質問 7: 次のコード スニペットを実行すると、コンソールに何が表示されますか?
質問 8: xGetter() はどのような値を出力しますか? " >質問 8: xGetter() はどのような値を出力しますか?
回答" >回答
分析:
質問 2: ReferenceError: a unknown
質問 4: オーバーフローなし
步骤" >步骤
问题5 : 不会响应
解析:
问题6 : 会导致TypeError错误
问题7 : a, b, c
问题8 : 10
ホームページ ウェブフロントエンド jsチュートリアル JavaScript の基本をテストするための 8 つの質問

JavaScript の基本をテストするための 8 つの質問

Jun 20, 2020 am 09:53 AM
html javascript フロントエンド

JavaScript は、その性質上、私たち全員に愛される楽しい言語です。ブラウザは主に JavaScript が実行される場所であり、この 2 つはサービス内で連携して動作します。 JS には、人々が軽視する傾向があり、時には無視する可能性のある概念がいくつかあります。プロトタイプ、クロージャ、イベント ループなどの概念は、ほとんどの JS 開発者が遠回りしてしまうあいまいな領域の 1 つです。ご存知のとおり、無知は危険であり、間違いを引き起こす可能性があります。

もっと質の高い記事を読みたい場合は、GitHub ブログをクリックしてください。毎年数百の質の高い記事があなたを待っています。

次に、いくつかの質問を見てみましょう。質問について考えてから回答することもできます。

質問 1: ブラウザのコンソールには何が表示されますか?

var a = 10;
function foo() {
    console.log(a); // ??
    var a = 20;
}
foo();
ログイン後にコピー

質問 2: var の代わりに let または const を使用した場合、出力は同じですか

var a = 10;
function foo() {
    console.log(a); // ??
    let a = 20;
}
foo();
ログイン後にコピー

質問 3: 内容は何ですか「newArray」要素?

var array = [];
for(var i = 0; i <3; i++) {
 array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // ??
ログイン後にコピー

質問 4: ブラウザ コンソールで「foo」関数を実行すると、スタック オーバーフロー エラーが発生しますか?

function foo() {
  setTimeout(foo, 0); // 是否存在堆栈溢出错误?
};
ログイン後にコピー

質問 5: コンソールで次の関数を実行した場合、ページ (タブ) の UI は引き続き応答しますか?

function foo() {
  return Promise.resolve().then(foo);
};
ログイン後にコピー
ログイン後にコピー

質問 6: 型エラーを発生させずに、次のステートメントのスプレッド操作を何とか使用できますか

var obj = { x: 1, y: 2, z: 3 };
[...obj]; // TypeError
ログイン後にコピー

質問 7: 次のコード スニペットを実行すると、コンソールに何が表示されますか?

var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, {c: 3});
Object.defineProperty(obj, 'd', { value: 4, enumerable: false });

// what properties will be printed when we run the for-in loop?
for(let prop in obj) {
    console.log(prop);
}
ログイン後にコピー

質問 8: xGetter() はどのような値を出力しますか?

var x = 10;
var foo = {
  x: 90,
  getX: function() {
    return this.x;
  }
};
foo.getX(); // prints 90
var xGetter = foo.getX;
xGetter(); // prints ??
ログイン後にコピー

回答

それでは、各質問に最初から最後まで答えてみましょう。これらの動作をわかりやすく説明し、いくつかの参考文献を示しながら簡単に説明します。

質問 1: 未定義

分析:

var キーワードを使用して宣言された変数は JavaScript でプロモートされ、代入されます。メモリ内の値 未定義。ただし、初期化は変数に値を代入した場所で行われます。さらに、var で宣言された変数は関数スコープですが、letconst はブロックスコープです。したがって、プロセスは次のようになります。

var a = 10; // 全局使用域
function foo() {
// var a 的声明将被提升到到函数的顶部。
// 比如:var a

console.log(a); // 打印 undefined

// 实际初始化值20只发生在这里
   var a = 20; // local scope
}
ログイン後にコピー

質問 2: ReferenceError: a unknown

分析:

let および const 宣言により、変数のスコープを、それが使用されているブロック、ステートメント、または式に制限できます。 .モード。 var とは異なり、これらの変数は昇格されず、いわゆる 一時デッド ゾーン (TDZ) があります。 TDZ 内のこれらの変数にアクセスしようとすると、ReferenceError が発生します。これらの変数は、実行が宣言に到達した場合にのみアクセスできるためです。

var a = 10; // 全局使用域
function foo() { // TDZ 开始

// 创建了未初始化的'a'
    console.log(a); // ReferenceError

// TDZ结束,'a'仅在此处初始化,值为20
    let a = 20;
}
ログイン後にコピー

質問 3: [3, 3, 3]

分析:

for

ループ内の ヘッダーで var キーワードを使用して変数を宣言すると、その変数に対する単一のバインディング (ストレージ スペース) が作成されます。閉鎖について詳しくはこちらをご覧ください。もう一度 for ループを見てみましょう。

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

let を使用してブロックレベルのスコープを持つ変数を宣言すると、ループの反復ごとに新しいバインディングが作成されます。

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

この問題を解決するもう 1 つの方法は、クロージャを使用することです。

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]
ログイン後にコピー

質問 4: オーバーフローなし

分析:

JavaScript 同時実行モデルは、「イベント ループ」に基づいています。 「ブラウザは JS の本拠地である」と言うとき、私が実際に言いたいのは、ブラウザが JS コードを実行するためのランタイム環境を提供するということです。

ブラウザの主なコンポーネントには、コール スタックイベント ループ、タスク キューWeb APIが含まれます。 setTimeoutsetIntervalPromise などのグローバル関数は JavaScript の一部ではなく、Web API の一部です。

JS 呼び出しスタックは後入れ先出し (LIFO) です。エンジンは一度に 1 つの関数をスタックから取り出し、コードを上から下に順番に実行します。 setTimeout のような非同期コードに遭遇すると、それを Web API に渡します (矢印 1)。したがって、イベントがトリガーされるたびに、callback がタスク キューに送信されます (矢印 2)。

イベント ループタスク キューを継続的に監視し、キューに入れられた順序でコールバックを一度に 1 つずつ処理します。 コール スタックが空の場合は常に、イベント ループがコールバックを取得し、処理のためにコールバックを スタック(矢印 3)に置きます。コール スタックが空でない場合、 イベント ループはコールバックをスタックにプッシュしないことに注意してください。

この知識を踏まえて、前述の質問に答えてみましょう:

步骤

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

问题5 : 不会响应

解析:

大多数时候,开发人员假设在事件循环图中只有一个任务队列。但事实并非如此,我们可以有多个任务队列。由浏览器选择其中的一个队列并在该队列中处理回调

在底层来看,JavaScript中有宏任务和微任务。setTimeout回调是宏任务,而Promise回调是微任务

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

现在,当你在控制台中运行以下代码段

function foo() {
  return Promise.resolve().then(foo);
};
ログイン後にコピー
ログイン後にコピー

每次调用'foo'都会继续在微任务队列上添加另一个'foo'回调,因此事件循环无法继续处理其他事件(滚动,单击等),直到该队列完全清空为止。 因此,它会阻止渲染。


问题6 : 会导致TypeError错误

解析:

展开语法 和 for-of 语句遍历iterable对象定义要遍历的数据。ArrayMap 是具有默认迭代行为的内置迭代器。对象不是可迭代的,但是可以通过使用iterable和iterator协议使它们可迭代。

Mozilla文档中,如果一个对象实现了@@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]
ログイン後にコピー

还可以使用 generator 函数来定制对象的迭代行为:

var obj = {x:1, y:2, z: 3}
obj[Symbol.iterator] = function*() {
  yield 1;
  yield 2;
  yield 3;
}
[...obj]; // 打印 [1, 2, 3]
ログイン後にコピー

问题7 : a, b, c

解析:

for-in循环遍历对象本身的可枚举属性以及对象从其原型继承的属性。 可枚举属性是可以在for-in循环期间包含和访问的属性。

var obj = { a: 1, b: 2 };
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor.enumerable); // true
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }
ログイン後にコピー

现在你已经掌握了这些知识,应该很容易理解为什么我们的代码要打印这些特定的属性

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

// 将{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 (let prop in obj) {
  console.log(prop);
}
// 打印
// a
// b
// c
ログイン後にコピー

问题8 : 10

解析:

在全局范围内初始化x时,它成为window对象的属性(不是严格的模式)。看看下面的代码:

var x = 10; // global scope
var foo = {
  x: 90,
  getX: function() {
    return this.x;
  }
};
foo.getX(); // prints 90
let xGetter = foo.getX;
xGetter(); // prints 10
ログイン後にコピー

咱们可以断言:

window.x === 10; // true
ログイン後にコピー

this 始终指向调用方法的对象。因此,在foo.getx()的例子中,它指向foo对象,返回90的值。而在xGetter()的情况下,this指向 window对象, 返回 window 中的x的值,即10

要获取 foo.x的值,可以通过使用Function.prototype.bindthis的值绑定到foo对象来创建新函数。

let getFooX = foo.getX.bind(foo);
getFooX(); // 90
ログイン後にコピー

就这样! 如果你的所有答案都正确,那么干漂亮。 咱们都是通过犯错来学习的。 这一切都是为了了解背后的“原因”。


推荐教程:《JS教程

以上がJavaScript の基本をテストするための 8 つの質問の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

HTMLの表の境界線 HTMLの表の境界線 Sep 04, 2024 pm 04:49 PM

HTML の表の境界線に関するガイド。ここでは、HTML でのテーブルの境界線の例を示しながら、テーブル境界線を定義する複数の方法について説明します。

HTML のネストされたテーブル HTML のネストされたテーブル Sep 04, 2024 pm 04:49 PM

これは、HTML でのネストされたテーブルのガイドです。ここでは、テーブル内にテーブルを作成する方法をそれぞれの例とともに説明します。

HTML 左マージン HTML 左マージン Sep 04, 2024 pm 04:48 PM

HTML マージン左のガイド。ここでは、HTML margin-left の概要とその例、およびそのコード実装について説明します。

HTML テーブルのレイアウト HTML テーブルのレイアウト Sep 04, 2024 pm 04:54 PM

HTML テーブル レイアウトのガイド。ここでは、HTML テーブル レイアウトの値と例および出力について詳しく説明します。

HTML入力プレースホルダー HTML入力プレースホルダー Sep 04, 2024 pm 04:54 PM

HTML 入力プレースホルダーのガイド。ここでは、コードと出力とともに HTML 入力プレースホルダーの例について説明します。

HTML 内のテキストの移動 HTML 内のテキストの移動 Sep 04, 2024 pm 04:45 PM

HTML でのテキストの移動に関するガイド。ここでは、概要、マーキー タグが構文でどのように機能するか、および実装例について説明します。

HTML 順序付きリスト HTML 順序付きリスト Sep 04, 2024 pm 04:43 PM

HTML 順序付きリストのガイド。ここでは、HTML 順序付きリストと型の導入とその例についても説明します。

HTML の onclick ボタン HTML の onclick ボタン Sep 04, 2024 pm 04:49 PM

HTML オンクリック ボタンのガイド。ここでは、それらの紹介、動作、例、およびさまざまなイベントでの onclick イベントについてそれぞれ説明します。

See all articles