首頁  >  文章  >  web前端  >  es6中for和foreach的差別是什麼

es6中for和foreach的差別是什麼

青灯夜游
青灯夜游原創
2022-10-21 17:32:541523瀏覽

區別:1、forEach是一個迭代器,是負責遍歷(Array Set Map)可迭代物件的;而for是一種循環機制,只是能透過它遍歷出數組。 2.for循環中會用到一些中斷行為,對於優化數組遍歷查找是很好的,但由於forEach屬於迭代器,只能按序依次遍歷完成,所以不支援中斷行為。 3.forEach的循環起點只能為0,且不能進行人為幹預;而for循環不同,可以人為控制循環起點。

es6中for和foreach的差別是什麼

本教學操作環境:windows7系統、ECMAScript 6版、Dell G3電腦。

for迴圈和forEach本質差異

#for迴圈是js提出時就有的迴圈方法。

forEach是ES5提出的,掛載在可迭代物件原型上的方法,例如Array Set Map

forEach是一個迭代器,負責遍歷可迭代物件。

那麼遍歷迭代可迭代物件分別是什麼呢。

  • 遍歷:指的對資料結構的每一個成員進行有規律的且為一次存取的行為。

  • 迭代:迭代是遞歸的特殊形式,是迭代器提供的一種方法,預設是依照一定順序逐一存取資料結構成員。迭代也是一種遍歷行為。

  • 可迭代物件:ES6中引入了iterable 類型,Array Set Map String arguments NodeList 都屬於iterable,他們特質就是都擁有[Symbol.iterator] 方法,包含他的物件被認為是可迭代的iterable

forEach 其實是一個迭代器,他與for 迴圈本質上的差別是forEach 是負責遍歷(Array Set Map)可迭代物件的,而for 迴圈是一種循環機制,只是能透過它遍歷出數組。

什麼是迭代器,當它被呼叫時就會產生一個迭代器物件(Iterator Object),它有一個.next()方法,每次呼叫傳回一個物件{value:value,done:Boolean}value傳回的是yield 後的回傳值,當yield 結束,done 變成true,透過不斷呼叫並依序的迭代存取內部的值。

迭代器是一種特殊物件。 ES6規範中它的標誌是傳回物件的 next() 方法,迭代行為判斷在 done 之中。在不暴露內部表示的情況下,迭代器實現了遍歷。看程式碼

let arr = [1, 2, 3, 4]  // 可迭代对象
let iterator = arr[Symbol.iterator]()  // 调用 Symbol.iterator 后生成了迭代器对象
console.log(iterator.next()); // {value: 1, done: false}  访问迭代器对象的next方法
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

我們看到了。只要是可迭代對象,呼叫內部的Symbol.iterator 都會提供一個迭代器,並根據迭代器返回的next 方法來存取內部,這也是for.. .of 的實作原理。

let arr = [1, 2, 3, 4]
for (const item of arr) {
    console.log(item); // 1 2 3 4 
}

把呼叫next 方法傳回物件的value 值並儲存在item 中,直到valueundefined 跳出循環,所有可迭代物件可供for...of消費。再來看看其他可迭代物件:

function num(params) {
    console.log(arguments); // Arguments(6) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    let iterator = arguments[Symbol.iterator]()
    console.log(iterator.next()); // {value: 1, done: false}
    console.log(iterator.next()); // {value: 2, done: false}
    console.log(iterator.next()); // {value: 3, done: false}
    console.log(iterator.next()); // {value: 4, done: false}
    console.log(iterator.next()); // {value: undefined, done: true}
}
num(1, 2, 3, 4)

let set = new Set('1234')
set.forEach(item => {
    console.log(item); // 1 2 3 4
})
let iterator = set[Symbol.iterator]()
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

所以可迭代物件中的Symbol.iterator 屬性被呼叫時都能產生迭代器,而forEach 也是生成一個迭代器,在內部的回呼函數中傳遞出每個元素的值

<span style="font-size: 20px;">#for</span>##循環和<span style="font-size: 20px;"></span>#forEach的語法差異

了解了本質差異,在應用過程中,他們到底有什麼語法差異呢?
  • forEach

    的參數。
  • forEach

    的中斷。
  • forEach

    刪除自身元素,index無法重設。
  • for

    迴圈可以控制迴圈起點。

forEach 的參數

我們真正了解 forEach

的完整傳參內容嗎?它大概是這樣:
    arr.forEach((self,index,arr) =>{},this)
  • self:

    陣列目前遍歷的元素,預設從左往右依序取得陣列元素。
  • index:

    陣列目前元素的索引,第一個元素索引為0,依序類別推。
  • arr:

    目前遍歷的陣列。
  • this:

    回呼函數中this指向。
  • let arr = [1, 2, 3, 4];
    arr.forEach(function (self, index, arr) {
        console.log(`当前元素为${self}索引为${index},属于数组${arr}`);
    }, person)
    我們可以利用 arr 實作陣列去重:###
    let arr1 = [1, 2, 1, 3, 1];
    let arr2 = [];
    arr1.forEach(function (self, index, arr) {
        arr.indexOf(self) === index ? arr2.push(self) : null;
    });
    console.log(arr2);   // [1,2,3]

    forEach 的中断

    在js中有break return continue 对函数进行中断或跳出循环的操作,我们在 for循环中会用到一些中断行为,对于优化数组遍历查找是很好的,但由于forEach属于迭代器,只能按序依次遍历完成,所以不支持上述的中断行为。

    let arr = [1, 2, 3, 4],
        i = 0,
        length = arr.length;
    for (; i < length; i++) {
        console.log(arr[i]); //1,2
        if (arr[i] === 2) {
            break;
        };
    };
    
    arr.forEach((self,index) => {
        console.log(self);
        if (self === 2) {
            break; //报错
        };
    });
    
    arr.forEach((self,index) => {
        console.log(self);
        if (self === 2) {
            continue; //报错
        };
    });

    如果我一定要在 forEach 中跳出循环呢?其实是有办法的,借助try/catch

    try {
        var arr = [1, 2, 3, 4];
        arr.forEach(function (item, index) {
            //跳出条件
            if (item === 3) {
                throw new Error("LoopTerminates");
            }
            //do something
            console.log(item);
        });
    } catch (e) {
        if (e.message !== "LoopTerminates") throw e;
    };

    若遇到 return 并不会报错,但是不会生效

    let arr = [1, 2, 3, 4];
    
    function find(array, num) {
        array.forEach((self, index) => {
            if (self === num) {
                return index;
            };
        });
    };
    let index = find(arr, 2);// undefined

    forEach 删除自身元素,index不可被重置

    forEach 中我们无法控制 index 的值,它只会无脑的自增直至大于数组的 length 跳出循环。所以也无法删除自身进行index重置,先看一个简单例子:

    let arr = [1,2,3,4]
    arr.forEach((item, index) => {
        console.log(item); // 1 2 3 4
        index++;
    });

    index不会随着函数体内部对它的增减而发生变化。在实际开发中,遍历数组同时删除某项的操作十分常见,在使用forEach删除时要注意。

    for 循环可以控制循环起点

    如上文提到的 forEach 的循环起点只能为0不能进行人为干预,而for循环不同:

    let arr = [1, 2, 3, 4],
        i = 1,
        length = arr.length;
    
    for (; i < length; i++) {
        console.log(arr[i]) // 2 3 4
    };

    那之前的数组遍历并删除滋生的操作就可以写成

    let arr = [1, 2, 1],
        i = 0,
        length = arr.length;
    
    for (; i < length; i++) {
        // 删除数组中所有的1
        if (arr[i] === 1) {
            arr.splice(i, 1);
            //重置i,否则i会跳一位
            i--;
        };
    };
    console.log(arr); // [2]
    //等价于
    var arr1 = arr.filter(index => index !== 1);
    console.log(arr1) // [2]

    <span style="font-size: 18px;">for</span>循环和<span style="font-size: 18px;">forEach</span>的性能区别

    在性能对比方面我们加入一个 map 迭代器,它与 filter 一样都是生成新数组。

    对比 for forEach map 的性能在浏览器环境中都是什么样的:

    性能比较:for > forEach > map 在chrome 62 和 Node.js v9.1.0环境下:for 循环比 forEach 快1倍,forEachmap 快20%左右。

    原因分析for:for循环没有额外的函数调用栈和上下文,所以它的实现最为简单。

    forEach:对于forEach来说,它的函数签名中包含了参数和上下文,所以性能会低于 for 循环。

    mapmap 最慢的原因是因为 map 会返回一个新的数组,数组的创建和赋值会导致分配内存空间,因此会带来较大的性能开销。

    如果将map嵌套在一个循环中,便会带来更多不必要的内存消耗。

    当大家使用迭代器遍历一个数组时,如果不需要返回一个新数组却使用 map 是违背设计初衷的。

    【相关推荐:javascript视频教程编程视频

    以上是es6中for和foreach的差別是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn