for...in , for...of , forEach 详解

1.for in (可遍历对象和数组,不建议遍历数组)

1.1使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问,

  Object.prototype.say = "hello";  //修改Object.prototype
  var person = {age:18};
  for(var key in person){
    console.log(key,person[key]);///循环时用person.key得不到对象key的值,可用person[key] 或者 eval("person."+key);
  }

//test.html:31 age 18
//test.html:31 say hello

/

  Array.prototype.say = "u";
  var children = [1,3,"djn"];
  for(var key in children){
    console.log(key,children[key]);
  }

//test.html:37 0 1
//test.html:37 1 3
//test.html:37 2 djn
//test.html:37 say u

1.2.只遍历对象自身的属性,而不遍历继承于原型链上的属性,使用hasOwnProperty 方法过滤一下。

  Object.prototype.say = "hello";
    var person = { age: 18 };
    for (var key in person) {
      if (person.hasOwnProperty(key)) {
        console.log(key, person[key]);
      }
    }

// age 18

1.2 Object.keys(obj) 方法会返回一个由给定对象的自身可枚举属性组成的数组

数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环会枚举其原型链上的属性)。返回值是这个对象的所有可枚举属性组成的字符串数组。

Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。

// object对象没有length属性,可以通过Object.keys(person).length,来获取person的长度了。

    Object.prototype.say = "hello";
    var person = {age:18};
    console.log(Object.keys(person));

//

    // simple array
    var arr = ['a', 'b', 'c'];
    console.log(Object.keys(arr)); // console: ['0', '1', '2']

    // array like object
    var obj = { 0: 'a', 1: 'b', 2: 'c' };
    console.log(Object.keys(obj)); // console: ['0', '1', '2']

   
    // for(var j of anObj){
    //   console.log(j);
    // }//anObj is not iterable

    // getFoo is a property which isn't enumerable
    var myObj = Object.create({}, {
      getFoo: {
        value: function () { return this.foo; }
      }
    });
    myObj.foo = 1;
    console.log(Object.keys(myObj)); // console: ['foo']

针对for in 和 Object.keys()循环遍历的顺序,我觉得有必要追究一下,就查了些相关资料。其循环对象时,顺序不可靠,但是他是依据什么顺序来遍历的呢,我找到一篇文章总结了一句话:先遍历证书属性(按照升序),然后根据其他属性先后创建顺序遍历出来的。

案例如下:

   // array like object with random key ordering
    var anObj = { name: "nn", 100: 'a', 2: 'b', 7: 'c', age: "kk" };
    console.log(Object.keys(anObj)); // console: ["2", "7", "100", "name", "age"]

    for (var i in anObj) {
      console.log(i);
    }
    // test.html: 74 2
    // test.html: 74 7
    // test.html: 74 100
    // test.html: 74 name
    // test.html: 74 age
    // test.html: 74 say

2. for of (ES6)

for...of语句可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句.  不可迭代一个普通对象

但是可以循环一个拥有enumerable属性的对象。n如果我们按对象所拥有的属性进行循环,可使用内置的Object.keys()方法

  let obj = { a: '1', b: '2', c: '3', d: '4' }
    for (let o of Object.keys(obj)) {
      console.log(o) 
    }
    // a
    // b
    // c
    // d

如果我们按对象所拥有的属性值进行循环,可使用内置的Object.values()方法

 let obj = { a: '1', b: '2', c: '3', d: '4' }
    for (let o of Object.values(obj)) {
      console.log(o)
    }
    // test.html:103 1
    // test.html:103 2
    // test.html:103 3
    // test.html:103 4
let iterable = [10, 20, 30];
for (let value of iterable) {
    value += 1;
    console.log(value);
}
// 11
// 21
// 31

禁止修改 value 的值,可以将let 改为 const (不可修改的常数)

  let iterable = [10, 20, 30];
    for (const value of iterable) {
      console.log(value);
    }
    // 11
    // 21
    // 31
//迭代 String 

let iterable = "boo";
for (let value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"



//迭代TypeArray
let iterable = new Uint8Array([0x00, 0xff]);
for (let value of iterable) {
  console.log(value);
}
// 0
// 255

//迭代Map

  let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);
    for (let entry of iterable) {
      console.log(entry);
    }
    // ["a", 1]   ===> 0:"a",1:"1"
    // ["b", 2]   ===> 0:"b",1:"2"
    // ["c", 3]   ===> 0:"c",1:"3"

    for (let [key, value] of iterable) {
      console.log(key,value);
    }
    // a 1
    // b 2
    // c 3
迭代set

let iterable = new Set([1, 1, 2, 2, 3, 3]);
for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

/迭代arguments对象
(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

关闭迭代器:

对于for...of的循环,可以由break,throw,continue 或者 return 终止。在这些情况下,迭代器关闭。

   function* foo() {
      yield 1;
      yield 2;
      yield 3;
    };

    for (let o of foo()) {
      console.log(o);//1
      break; // closes iterator, triggers return
    }

function* 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个  Generator  对象。

生成器函数在执行时能暂停,后面又能从暂停处继续执行。

调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器(iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。

next()方法返回一个对象,这个对象包含两个属性:value 和 done,value 属性表示本次 yield 表达式的返回值,done 属性为布尔类型,表示生成器后续是否还有 yield 语句,即生成器函数是否已经执行完毕并返回。

调用 next()方法时,如果传入了参数,那么这个参数会作为上一条执行的  yield 语句的返回值,例如:

function *gen(){
    yield 10;
    y=yield 'foo';
    yield y;
}

var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(10));// 将 10 赋给上一条 yield 'foo' 的左值,即执行 y=10,返回 10
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true

不要重用生成器

生成器不应该重用,即使for...of循环的提前终止,例如通过break关键字。在退出循环后,生成器关闭,并尝试再次迭代,不会产生任何进一步的结果。

var gen = (function *(){
    yield 1;
    yield 2;
    yield 3;
})();
for (let o of gen) {
    console.log(o);
    break;//关闭生成器
} 

//生成器不应该重用,以下没有意义!
for (let o of gen) {
    console.log(o);
}

for in 和 for of 的区别:

for...in 语句以原始插入顺序迭代对象的可枚举属性

for...of 语句遍历可迭代对象定义要迭代的数据。

  Object.prototype.objCustom = function () { };
    Array.prototype.arrCustom = function () { };

    let iterable = [3, 5, 7];
    iterable.foo = 'hello';

    for (let i in iterable) {
      console.log(i); //  0, 1, 2, "foo", "arrCustom", "objCustom"
    }

    for (let i in iterable) {
      if (iterable.hasOwnProperty(i)) {
        console.log(i); //  0, 1, 2, "foo"
      }
    }

    for (let i of iterable) {
      console.log(i); //  3, 5, 7
    }

//  不可迭代一个普通对象
    Object.prototype.say = "hello";
    var person = { age: 18 };
    for (var key of person) {
      console.log(key, person[key]);
    }
//Uncaught TypeError: person is not iterable
    

每个对象将继承objCustom属性,并且作为Array的每个对象将继承arrCustom属性,因为将这些属性添加到Object.prototypeArray.prototype。由于继承和原型链,对象iterable继承属性objCustomarrCustom

for in 循环仅以原始插入顺序记录iterable 对象的可枚举属性。它不记录数组元素357 或hello,因为这些不是枚举属性。但是它记录了数组索引以及arrCustomobjCustom

使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是012foo,因为它们是自身的属性(不是继承的)。属性arrCustomobjCustom不会被记录,因为它们是继承的

for of 循环迭代并记录iterable作为可迭代对象定义的迭代值,这些是数组元素 357,而不是任何对象的属性

3. foreach    array.forEach(element => { });

方法对数组的每个元素执行一次提供的函数。

* forEach 方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。

callback 函数会被依次传入三个参数:

  • 数组当前项的值
  • 数组当前项的索引
  • 数组对象本身
 function logArrayElements(element, index, array) {
      console.log('a[' + index + '] = ' + element, array);
    }
    // 注意索引 2 被跳过了,因为在数组的这个位置没有项
    [2, 5, , 9].forEach(logArrayElements);
    // logs:
    // a[0] = 2 [2, 5, , 9]
    // a[1] = 5 [2, 5, , 9]
    // a[3] = 9 [2, 5, , 9]

* forEach 遍历的范围在第一次调用 callback 前就会确定。调用 forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()),之后的元素将被跳过.

    var words = ["one","two","three","four"];
    words.forEach((i)=>{
      console.log(i);
      if(i === "two"){
        words.shift();
      }
    })

下面的例子会输出"one", "two", "four"。当到达包含值"two"的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four"现在在数组更前的位置,"three"会被跳过。 forEach()不会在迭代之前创建数组的副本。

for in 和for of 也有此类特性

  let iterable = [10, 20, 30];
    for (const value in iterable) {
      if(value == 1){
        iterable.shift();
      }
      console.log(value);
    }

// 0
// 1


//
  let iterable = [10, 20, 30];
    for (const value of iterable) {
      if(value == 10){
        iterable.shift();
      }
      console.log(value);
    }
// 10
// 30

forEach(数组):

缺点:不能同时遍历多个集合,在遍历的时候无法修改和删除集合数据,方法不能使用break,continue语句跳出循环,或者使用return从函数体返回,对于空数组不会执行回调函数

优点:便利的时候更加简洁,效率和for循环相同,不用关心集合下标的问题,减少了出错的效率

for in (适用于对象,数组不建议):

 缺点:某些情况下,会出现随机顺序的遍历,因为里面的值是string类型,增加了转换过程,因此开销较大

 优点:可以遍历数组的键名,遍历对象简洁方便,可以使用break,continue和return

for of  (数组,一般对象不可用,可借助Object.keys()/Object.value())

优点:避免了for in的所有缺点,可以使用break,continue和return,不仅支持数组的遍历,还可以遍历类似数组的对象,支持字符串的遍历最简洁,最直接的遍历数组的语法支持map和Set对象遍历

 缺点:不适用于处理原有的原生对象(原生对象是一个子集,包含一些在运动过程中动态创建的对象)

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值