Interview question 2: Talk about your understanding of for in and for of in JavaScript

for in

for...in The statement iterates over an object 's enumerable properties other than Symbol in any order , including inherited enumerable properties.

Before I have a deep understanding of the role of for in, I only stayed at the level that it can traverse object keys and array subscripts. Now let’s explore what is a Symbol type key in an object (newly added after ES6) and enumerable properties ,

Object types in JS include Object, Array, Function, Date, Math....

This article mainly uses Object and Array for practice.

First, let’s look at a piece of code:

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);
}
 

I think everyone is very clear about the output results. If it is traversing an array, the subscript corresponding to the array is output. If it is traversing an object, the key name of the object is output.

Continue to increase the difficulty and look at the following code:

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
}
 
 

As can be seen from the above output, whether it is an array element or a key value on an object, they are all enumerable properties, and it can be seen that although the Symbol type key is defined in the object, for in will skip the Symbol type. key traversal

And for in will also traverse the prototype chain, so why does it output 'myProto' when traversing obj and arr? That's because obj is an object, the default constructor is Object(), and arr is an array object, the default The constructor is Array(), but the prototype attribute of Array is essentially an object, so the prototype of the Array prototype is the prototype attribute on Object.
 

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

Therefore, the prototype chains of arr and obj have an intersection, which explains that they can both traverse to myProto.

We can also simply think that the property corresponding to the Symbol type key in an object is not an enumerable property, but in fact it is still an enumerable property, just skipped. This paragraph is a bit convoluted, but in the past It will be clear when you look down.

Since the traversal of for in cannot avoid enumerable attributes, what are enumerable attributes? MDN’s explanation is as follows:
 

Enumerable properties refer to those properties whose internal "enumerable" flag is set to true. For properties through direct assignment and property initialization, the identification value defaults to true. For properties defined through Object.defineProperty, etc., This flag value defaults to false. Enumerable properties can be traversed through a for...in loop (unless the property name is a Symbol). The ownership of a property is determined by determining whether the property belongs directly to an object, rather than being inherited through the prototype chain.

It can be concluded from the above that

Enumerable properties are those properties with the "enumerable" flag set to true inside the object. For properties
defined through literals, the enumerable flag defaults to true. For properties defined through Object.defineProperty, etc., the flag defaults to flase
for The in loop will only traverse the enumerable attributes in the object, and skip attributes whose attribute names are Symbol types.
for in will only traverse the enumerable attributes on the object itself
. Please see the following code:
 

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
}
 

 

 

In the above code, we first create an obj object with an initialization attribute mark, then define two new properties through literals, and define 2 string type and 2 symbol type key values ​​through Object.defineProperty, both of which Each has an enumerable and non-enumerable identifier

You can see that console.log prints obj, which only contains enumerable attributes. That is to say, the attributes whose enumerable flag is false will not appear in the printed content of the entire obj object, which also verifies that even if the Symbol type key The attribute can be enumerated and marked as true, which will also be skipped by for in.
Additional expansion

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
}

You can see that the subscript of the array is also an attribute of the array object. After setting subscript 0 to be non-enumerable and non-modifiable, for in cannot traverse subscript 0 and cannot modify the value of subscript 0, but the fun thing is to print The array will still print out each element in full.

for of 

for...ofstatement creates an iteration loop over an iterable object (including  Array , Map , Set , String , TypedArray , arguments  objects, and more), calls a custom iteration hook, and executes the statement for each distinct property value

for of is a new syntax in ES6 for traversing iterable objects (implementing the Iterator interface)

// 迭代数组
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
}

 Close iterator

For for...ofa loop, it can be terminated by  breakthrow or  return . In these cases, the iterator is closed.

return situation

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); 
}

 throw situation

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 for...inloops, the above interrupt method also applies

Summary
for...in is suitable for traversing enumerable properties on objects, and only traverses non-Symbol type keys and the object itself and enumerable properties on the prototype chain. for...
of is suitable for objects that implement the Iterator interface ( Also known as iterable objects), the traversal method is implemented by itself. For example, for an array, it traverses the value of the element corresponding to each subscript, and for a Map, the traversal value is an array composed of key-value pairs.
 

Dear celebrities, give me a thumbs up and support, thank you! ! !

Guess you like

Origin blog.csdn.net/2201_75705263/article/details/132816326