FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

js中的of和in那些事儿 #184

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

一直以为of和in在js中是同一类东西。 of不能单独使用;可以与for组合成for...of,for await...of;还有一个Array.of()方法。 但其实in在js中是in operator。可以单独使用;也可以与for组合成for..in。

// 最基本的语法
const entries = [{target: div, contentRect: DOMRectReadOnly}, {target: div, contentRect: DOMRectReadOnly}, {target: div, contentRect: DOMRectReadOnly}];

// 这里的entry相当于hook,在花括号形成的块作用域内,对hook做if判断,从而做处理。
for (const entry of entries) {
  console.log(entry);
}
使用例子
遍历数组
const iterable = ['foo','bar','baz'];
for ( const value of iterable ){
    // do some thing
}

这里也可以是let value of iterable,取决于我们会不会对value重新赋值(reassign)。

遍历其他
type iterable value
String 'foo' 'f', 'o', 'o'
Array [{foo:1},{foo:2}] {foo:1},{foo:2}
TypedArray new Uint8Array([0x00, 0xff]); 0, 255
Map new Map([['foo', 1],['bar', 2],['baz', 3]]); ['foo', 1], ['bar', 2], ['baz', 3]
Set new Set([1,1, 2,2, 3,3]) 1,2,3
arguments function foo(){for(const argument of arguments){}; foo(1,3,{bar:2}); 1, 3, {bar: 2}
DOM collection const articleParagraphs = document.querySelectorAll('article > p'); for (const paragraph of articleParagraphs) { paragraph.classList.add('read');} <p class="read"></p> <p class="read"></p>
generator,async iterator,复杂iterable对象 高难度操作暂不涉猎 高难度操作暂不涉猎

其中Map数据类型可以再对value做一次解构,for ( const [key, value] of iterable ){} // ['foo', 1]->[key, value]

关闭iterator
const iterable = ['foo','bar','baz'];
for ( const value of iterable ){
    // do some thing
    console.log(value);
    if(value==='bar'){
        break;
    }
}
// foo,bar,由于break关闭迭代,所以'baz'不会打印

for-await...of

高难度操作暂不涉猎

Array.of()

Array.of(element0[, element1[, ...[, elementN]]]) elementN是生成数组的组成元素。返回一个新的Array实例。

in篇

单独使用in

prop in object

var man = {year: 1995};
console.log('year' in man);// true
man.prototype = {hair : 'enough'};
console.log('hair' in man);// false
console.log('hair' in man.prototype);// true

for...in

for (variable in object)
  statement

in与hasOwnProperty的区别

console.log("in check", "name" in foo); // true console.log("in prototype check", "age" in foo); // true

console.log("hasOwnProperty check", foo.hasOwnProperty("name")); // true console.log("hasOwnProperty prototype check", foo.hasOwnProperty("age")); // true


### in和of最直观的区别
```js
in直接遍历即可
// in
var obj = {a: 1, b: 2, c: 3}; 
for (const prop in obj) {
console.log(prop)
}
// a,b,c

of需要借助Object.entries()

// of
var obj = {a: 1, b: 2, c: 3}; 
for (const prop of Object.entries(obj)) {
    console.log(prop)
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

实践中遇到的使用场景

如何检测一个空对象?

解法一:Object.prototype.toString.call和JSON.stringify。

Object.prototype.toString.call(obj)==="[Object object]" && JSON.stringify({}) === "{}";

解法二:Object.keys(),Object.values() length===0

Object.keys(obj).length === 0 || Object.values(obj).length === 0

解法三:错误解法for...of,正确解法for...in。 错误解法:

function isObjEmpty(obj){
    for(key of obj){
        if(key) return false
    }
    return true;
}

正确解法:

function isObjEmpty(obj){
    for(key in obj){
        if(key) return false
    }
    return true;
}

为什么? of只能遍历iterable的对象,包括String,TypedArray,Map,Set,arguments,DOM collection 等等。 如果是一个最最普通的对象"const obj = {}"呢?不能。会抛出异常obj is not iterable。 引申:如何才能让它iterable呢?可以使用Object.defineProperty吗? 不行。 仍然会抛出obj is not iterable。况且都是空对象了,prop1属性根本就不存在的好吗。

const obj = {};
Object.defineProperty(obj, 'prop1', {
  value: 42,
  enumerable: true,
});

即使prop1属性存在,使用for...of仍然无法遍历。

如何遍历普通对象{foo: 1, bar: 2}?

由于普通的object直接用of是不可迭代的,因此需要通过Object.entries将其迭代化。 或者通过in进行迭代。

for (const [key, value] of Object.entries({foo: 1, bar: 2})) {
    console.log(key,value);
    // foo 1
    // bar 2
}
for (const prop in {foo: 1, bar: 2}) {
    console.log(prop);// 1, 2
}

通过in和of取到的单个条目,修改这个单个条目,会影响原对象吗?

不会影响。

修改in的条目:不会影响。

const obj = {foo: 1, bar: 2};
for (let prop in obj) {
    prop = 999;
}
console.log(obj)// {foo: 1, bar: 2}

修改of的条目:不会影响。

for (let [key, value] of Object.entries(obj)) {
     value = 999
};
console.log(obj); // {foo: 1, bar: 2}
for (let item of Object.entries(obj)) {
     item[1] = 999;
};
console.log(obj);// {foo: 1, bar: 2}

还是需要通过obj[xxx]去修改原对象,用of修改更合适

const obj = {foo: 1, bar: 2};
for (const [key, value] of Object.entries(obj)) {
     obj[key] = {newVal: 999, oldVal: value};
}
console.log(obj);
/*
{
  "foo": {
    "newVal": 999,
    "oldVal": 1
  },
  "bar": {
    "newVal": 999,
    "oldVal": 2
  }
}
*/

普通数组['abc', 'dd', 'foo']通过of迭代如何返回?

for (value of ['abc', 'dd', 'foo']){
    console.log(value);
}
// abc
// dd
// foo

特殊数组[mp: 13.914736, lp: c, Oq: 0, size: c]通过of迭代如何返回?

这个是一个特殊的数组,不能直接访问,需要在特定情况下访问。 这样的特殊数组本质上也是继承自Object,因此可以通过Object.entries遍历。

// temp [mp: 13.914736, lp: c, Oq: 0, size: c]
for (value of Object.entries(temp)){
     console.log(value);
}
/*
["mp", 13.914736]
["lp", c]
["Oq", 0]
["size", c]
*/
FrankKai commented 4 years ago

参考资料: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in