面接の質問 2: JavaScript の for in と for of についての理解について話す

のために

for...in このステートメントは、継承された列挙可能なプロパティを含む、 Symbol以外のオブジェクトの列挙可能なプロパティを任意の順序で反復します

for in の役割を深く理解するまでは、オブジェクト キーと配列添字をトラバースできるというレベルに留まっていたので、オブジェクト (ES6 以降に新たに追加された) のSymbol 型キー列挙可能なプロパティとは何かを見てみましょう。 、

JS のオブジェクト タイプには、Object、Array、Function、Date、Math などがあります。

この記事では主にObjectとArrayを練習用に使用します。

まず、コードの一部を見てみましょう。

const arr = [5, 4, 3, 2, 1];
 
const obj = {
  mark: "mark-v",
  jack: "jack-v",
  amy: "amy-v",
};
 
for (const i in arr) {  // 输出 0 1 2 3 4
  console.log(i);
}
 
for (const i in obj) { // 输出 mark jack amy
  console.log(i);
}
 

出力結果は皆さんよく分かっていると思いますが、配列をトラバースしている場合は配列に対応する添字が出力され、オブジェクトをトラバースしている場合はオブジェクトのキー名が出力されます。

引き続き難易度を上げて、次のコードを見てください。

const arr = [1, 2, 3, 4, 5];
 
arr.value = "array"; // 给数组对象上添加一个value属性
 
const s1 = Symbol("symbol1"); // 定义两个symbo类型的值
const s2 = Symbol("symbol2");
 
const obj = {
  mark: "mark-v",
  jack: "jack-v",
  amy: "amy-v",
  [s1]: "symbol1-v", // 给obj初始化这两个symbol键值
  [s2]: "symbol2-v",
};
 
// 给obj原型上添加一个属性
Object.getPrototypeOf(obj).myProto = 'proto'
 
console.log(obj);
// 打印 {
//  mark: 'mark-v',
//  jack: 'jack-v',
//  amy: 'amy-v',
//  [Symbol(symbol1)]: 'symbol1-v',
//  [Symbol(symbol2)]: 'symbol2-v'
// }
 
for (const i in arr) {
  console.log(i); // 输出 0 1 2 3 4 value myProto
}
 
for (const i in obj) {
  console.log(i); // 输出 mark jack amy myProto
}
 
 

上記の出力からわかるように、配列要素であれ、オブジェクトのキー値であれ、それらはすべて列挙可能なプロパティであり、オブジェクトで Symbol 型のキーが定義されているにもかかわらず、for in はスキップされることがわかります。シンボルのタイプ。キートラバーサル

そして、for in はプロトタイプチェーンも走査するのに、obj と arr を走査するときに「myProto」が出力されるのはなぜでしょうか? それは、obj がオブジェクトであり、デフォルトのコンストラクターが Object() であり、arr がデフォルトのコンストラクターである配列オブジェクトであるためです。は Array() ですが、Array のプロトタイプ属性は本質的にオブジェクトであるため、Array プロトタイプのプロトタイプは Object のプロトタイプ属性になります。
 

Object.getPrototypeOf(obj) === Object.getPrototypeOf(Object.getPrototypeOf(arr)) // true

したがって、arr と obj のプロトタイプ チェーンには共通部分があり、両方とも myProto に到達できることがわかります。

オブジェクト内の Symbol 型キーに対応するプロパティは列挙可能なプロパティではないと単純に考えることもできますが、実際にはまだ列挙可能なプロパティであり、単にスキップされているだけです。この段落は少し複雑ですが、以前は次のようになります。下を見るとクリア。

for in の走査では列挙可能な属性を回避できないため、列挙可能な属性とは何でしょうか? MDN の説明は次のとおりです。
 

列挙可能なプロパティとは、内部の "enumerable" フラグが true に設定されているプロパティを指します。直接割り当ておよびプロパティの初期化によるプロパティの場合、識別値はデフォルトで true に設定されます。Object.defineProperty などを通じて定義されたプロパティの場合、このフラグ値はデフォルトで false に設定されます。列挙可能なプロパティは、for...in ループを介して走査できます (プロパティ名がシンボルでない場合)。プロパティの所有権は、そのプロパティがプロトタイプ チェーンを通じて継承されるのではなく、オブジェクトに直接属しているかどうかを判断することによって決定されます。

以上のことから、次のように結論付けることができます。

列挙可能なプロパティとは、オブジェクト内で "enumerable" フラグが true に設定されているプロパティです。
リテラルを通じて定義されたプロパティの場合、列挙可能なフラグはデフォルトで true に設定されます。Object.defineProperty などを通じて定義されたプロパティの場合、フラグはデフォルトで flase に設定されます
。ループは、オブジェクト内の列挙可能な属性のみを走査し、属性名が Symbol タイプである属性をスキップします。for
in は、オブジェクト自体の列挙可能な属性のみを走査します
。次のコードを参照してください。
 

const s1 = Symbol("symbol1");
const s2 = Symbol("symbol2");
// 以下字面量初始化和赋值的属性可枚举标识都为true
const obj = {          // 方式一
  mark: "mark-v",
};
obj.jack = "jack-v"     // 方式二
obj['amy'] = 'amy-v'    // 方式三
 
Object.defineProperty(obj, "kong", {
  value: "kong-v",
  enumerable: false,   // 可枚举标识,默认为 false
});
 
Object.defineProperty(obj, "john", {
  value: "john-v",
  enumerable: true,   // 设置为 true
});
// 通过Object.defineProperty设置Symbol类型键
Object.defineProperty(obj, s1, {
  value: "s1-v",
  enumerable: false,   
});
// 通过Object.defineProperty设置Symbol类型键,并且可枚举标识为true
Object.defineProperty(obj, s2, {
  value: "s2-v",
  enumerable: true,
});
 
console.log(obj);
// 打印 {
//   mark: 'mark-v',
//   jack: 'jack-v',
//   amy: 'amy-v',
//   john: 'john-v',
//   [Symbol(symbol2)]: 's2-v'
// }
 
for (const i in obj) {
  console.log(i);     // 输出 mark jack amy john
}
 

 

 

上記のコードでは、最初に初期化属性マークを持つ obj オブジェクトを作成し、次にリテラルを通じて 2 つの新しいプロパティを定義し、Object.defineProperty を通じて 2 つの文字列型と 2 つのシンボル型のキー値を定義します。どちらも列挙可能な値を持ちます。および列挙不可能な識別子

console.log では、列挙可能な属性のみを含む obj が出力されることがわかります。つまり、列挙可能なフラグが false である属性は、obj オブジェクト全体の出力内容には表示されません。これは、シンボル タイプがkey 属性を列挙して true としてマークすることができ、これも for in によってスキップされます。
追加拡張

const arr = [1, 2, 3, 4, 5];
// 设置arr对象属性0的值为100,不可枚举,不可修改
Object.defineProperty(arr, 0, {
  value: 100,
  enumerable: false,
  writable: false,
});
 
arr[0] = 1 // 尝试修改下标0的值
 
console.log(arr);  // 打印 [ 100, 2, 3, 4, 5 ]
 
for (const i in arr) {
  console.log(i);    // 输出 1 2 3 4
}

配列の添字も配列オブジェクトの属性であることがわかります。添字 0 を列挙不可および変更不可能に設定した後、for in は添字 0 を走査できず、添字 0 の値を変更できません。配列は各要素を完全に出力します。

の 

for...ofステートメントは、反復可能オブジェクトArrayMapSetStringTypedArray引数 オブジェクトなど)に対して反復ループを作成し、カスタム反復フックを呼び出して、個別のプロパティ値ごとにステートメントを実行します。

for of は、反復可能オブジェクトを走査するための ES6 の新しい構文です ( Iteratorインターフェイスを実装します)。

// 迭代数组
const array = [10, 20, 30];
for (const value of array) {
    console.log(value); // 输出 10 20 30
}
 
// 迭代String
const string = 'kong'
for (const s of string) {
    console.log(s); // 输出 k o n g
}
 
// 迭代map
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (const item of map) {
  console.log(item);  // 输出  [ 'a', 1 ]  [ 'b', 2 ]  [ 'c', 3 ]
}
 
// 迭代set
const set = new Set([1, 1, 2, 2, 3, 3]);
for (let item of set) {
  console.log(item);  // 输出 1 2 3
}

 反復子を閉じる

ループの場合は、 、 、  または for...ofで終了できます  。このような場合、反復子は閉じられます。breakthrowreturn

返品状況

const array = [10, 20, 30];
for (const value of array) {
    console.log(value); // 输出 10
    return
}
// 下面代码不会执行,前面已经return
const string = 'kong'
for (const s of string) {
    console.log(s); 
}

 投げる状況

const array = [10, 20, 30];
for (const value of array) {
  console.log(value); // 输出 10
  throw new Error()
}
// 不执行下面代码,上面已经抛错
const string = "kong";
for (const s of string) {
  console.log(s); 
}

ループの場合for...in、上記の割り込みメソッドも適用されます

概要
for...in は、オブジェクトの列挙可能なプロパティを走査するのに適しており、非 Symbol 型のキーとオブジェクト自体、およびプロトタイプ チェーン上の列挙可能なプロパティのみを走査します。for... of は、Iterator インターフェイスを実装するオブジェクトに適しています
(反復可能オブジェクトとも呼ばれます)、トラバーサル メソッドはそれ自体で実装されます。たとえば、配列の場合、各添字に対応する要素の値をトラバースし、マップの場合、トラバーサル値はキーと値で構成される配列になります。ペア。
 

親愛なる著名人の皆様、親指を立ててサポートしてください、ありがとう!

おすすめ

転載: blog.csdn.net/2201_75705263/article/details/132816326