目次
for...in はプロトタイプ オブジェクトのプロパティを走査します
序文
いくつかのトラバーサル メソッドの中で最も高速に実行されfor
、追加の関数呼び出しスタックやコンテキストがありません。しかし、実際の開発では、セマンティクス、可読性、プログラムのパフォーマンスを組み合わせて、使用するソリューションを選択する必要があります。for
、foreach
、map
、for...in
、 のfor...of
5 つのライブバトルの方法を見てみましょう。
自己紹介
ために
私はこの声明に目を通した最初の当事者であり、ここにいる誰もが私をおじいちゃんと呼ぶ必要があります。開発者のほとんどのニーズを満たすことができます。
// 遍历数组
let arr = [1,2,3];
for(let i = 0;i < arr.length;i++){
console.log(i) // 索引,数组下标
console.log(arr[i]) // 数组下标所对应的元素
}
// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i = 0, keys=Object.keys(profile); i < keys.length;i++){
console.log(keys[i]) // 对象的键值
console.log(profile[keys[i]]) // 对象的键对应的值
}
// 遍历字符串
let str = "abcdef";
for(let i = 0;i < str.length ;i++){
console.log(i) // 索引 字符串的下标
console.log(str[i]) // 字符串下标所对应的元素
}
// 遍历DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for(let i = 0;i<articleParagraphs.length;i++){
articleParagraphs[i].classList.add("paragraph");
// 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}
それぞれに
ES5バージョンをリリースします。有効な値を含む配列内の項目ごとにコールバック関数を昇順で 1 回実行します。削除または初期化されていない項目はスキップされます (疎配列など)。私は for ループの強化版です。
// 遍历数组
let arr = [1,2,3];
arr.forEach(i => console.log(i))
// logs 1
// logs 2
// logs 3
// 直接输出了数组的元素
//遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
let keys = Object.keys(profile);
keys.forEach(i => {
console.log(i) // 对象的键值
console.log(profile[i]) // 对象的键对应的值
})
地図
ES5 バージョンもリリースしました。新しい配列を作成でき、新しい配列の結果は、提供された関数を 1 回呼び出した後の元の配列の各要素の戻り値になります。
let arr = [1,2,3,4,5];
let res = arr.map(i => i * i);
console.log(res) // logs [1, 4, 9, 16, 25]
for...列挙中
ES5バージョンをリリースします。シンボル以外のオブジェクトの列挙可能なプロパティを任意の順序で繰り返します。
// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i in profile){
let item = profile[i];
console.log(item) // 对象的键值
console.log(i) // 对象的键对应的值
// 遍历数组
let arr = ['a','b','c'];
for(let i in arr){
let item = arr[i];
console.log(item) // 数组下标所对应的元素
console.log(i) // 索引,数组下标
// 遍历字符串
let str = "abcd"
for(let i in str){
let item = str[i];
console.log(item) // 字符串下标所对应的元素
console.log(i) // 索引 字符串的下标
}
...反復用
ES6版でリリースしました。反復可能なオブジェクト (Array、Map、Set、String、TypedArray、引数オブジェクトなど) に対して反復ループを作成し、カスタム反復フックを呼び出し、各個別のプロパティの値に対してステートメントを実行します。
/ 迭代数组数组
let arr = ['a','b','c'];
for(let item of arr){
console.log(item)
}
// logs 'a'
// logs 'b'
// logs 'c'
// 迭代字符串
let str = "abc";
for (let value of str) {
console.log(value);
}
// logs 'a'
// logs 'b'
// logs 'c'
// 迭代map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]
for (let entry of iterable) {
console.log(entry);
}
// logs ["a", 1]
// logs ["b", 2]
// logs ["c", 3]
// 迭代map获取键值
for (let [key, value] of iterable) {
console.log(key)
console.log(value);
}
// 迭代set
let iterable = new Set([1, 1, 2, 2, 3, 3,4]);
for (let value of iterable) {
console.log(value);
}
// logs 1
// logs 2
// logs 3
// logs 4
// 迭代 DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for (let paragraph of articleParagraphs) {
paragraph.classList.add("paragraph");
// 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}
// 迭代arguments类数组对象
(function() {
for (let argument of arguments) {
console.log(argument);
}
})(1, 2, 3);
// logs:
// 1
// 2
// 3
// 迭代类型数组
let typeArr = new Uint8Array([0x00, 0xff]);
for (let value of typeArr) {
console.log(value);
}
// logs:
// 0
// 255
最初の自己紹介とスキルのデモンストレーションの後、私たちは次のことを学びました。
-
for语句
は最も原始的なループ文です。変数(配列の添字を示す数値型)を定義し、i
その値を一定の条件に従ってi
循環累積します。通常、条件はループ オブジェクトの長さであり、その長さを超えるとループは停止します。物体では長さが判断できないのでObject.keys()
併用します。 -
forEach
ES5が提案されました。強化版であると主張していますfor语句
が、文章で書くよりもはるかに簡単であることがわかりますfor语句
。しかし、これは本質的には配列のループです。forEach
コールバック関数は配列要素ごとに 1 回実行されます。これは呼び出し元の配列であるため、元の配列は変更されません。戻り値は ですundefine
。 -
map
ES5が提案されました。コールバック関数は、元の配列の各要素に対して順番に 1 回呼び出されます。呼び出し元の元の配列自体を変更せずに、新しい配列を生成します。戻り値は新しい配列です。 -
for...in
ES5が提案されました。プロトタイプ オブジェクトのプロパティを含むオブジェクトの列挙可能なプロパティを任意の順序で走査します。つまり、順序は固定されていません。配列を走査する際には配列の添え字がキー値として使用され、このとき i は文字列型になります。これはオブジェクトのプロパティを反復処理するために構築されており、配列での使用はお勧めできません。 -
for...of
ES6 が提案されました。反復可能オブジェクトのデータのみを走査します。
能力審査
プログラマーとしては、これらを知っているだけでは十分ではなく、実際の開発においてそれぞれの長所と短所を特定する必要があります。地域の状況に応じてそれらを使用して、長所を最大化し、短所を回避します。それにより、プログラムの全体的なパフォーマンスを向上させることが能力の鍵となります。
ループ本体からの飛び出しについて
ループ内で特定の条件が満たされると、ループ本体から飛び出すか、修飾されていないデータをスキップして他のデータのループを継続します。は頻繁に発生する要件です。一般的に使用されるステートメントはbreak
と ですcontinue
。
復習として、この 2 つの違いについて簡単に説明します。
-
break
このステートメントは、現在のループから飛び出し、現在のループの後にステートメントを実行します。 -
continue
このステートメントは、現在のループを終了し、次のループの実行を継続することです。
注:forEach
と はmap
ループ本体からの飛び出しをサポートしていませんが、他の 3 つのメソッドはそれをサポートしています。
原則 : 実装原則を見ればforEach
問題が理解できます。
Array.prototype.forEach(callbackfn [,thisArg]{
}
ここで渡される関数はコールバック関数です。Break はループから抜け出すためにのみ使用でき、コールバック関数はループの本体ではないため、コールバック関数で Break を使用することは明らかに違法です。
コールバック関数内で return を使用しても、上位関数、つまりこの for ループ内に結果が返されるだけで、for ループが終了するわけではないため、return も無効になります。
map()
同じ方法。
map()
チェーンコール
map()
メソッドはチェーン可能です。つまり、他のメソッドと簡単に組み合わせることができます。例: reduce()
、 sort()
、 filter()
など。しかし、他の方法ではこれを行うことはできません。forEach()
の戻り値は であるundefined
ため、連鎖することはできません。
// 将元素乘以本身,再进行求和。
let arr = [1, 2, 3, 4, 5];
let res1 = arr.map(item => item * item).reduce((total, value) => total + value);
console.log(res1) // logs 55 undefined"
for...in
プロトタイプオブジェクトのプロパティが走査されます
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
console.log(i);
}
// logs
// 0
// 1
// 2
// foo
// arrCustom
// objCustom
ただし、実際の開発では、プロトタイプ オブジェクトのプロパティは必要ありません。この場合hasOwnProperty()
、オブジェクトが独自のプロパティに指定されたプロパティを持っているかどうか (つまり、指定されたキーが存在するかどうか) を示すブール値を返すメソッドを使用できます。次のように:
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
if (arr.hasOwnProperty(i)) {
console.log(i);
}
}
// logs
// 0
// 1
// 2
// foo
// 可见数组本身的属性还是无法摆脱。此时建议使用 forEach
純粋なオブジェクトの走査の場合は、for..in
列挙を選択する方が便利です。配列の走査の場合、for..of
インデックスの反復を知る必要がない場合は、中断することもできるため、列挙を選択するのがより適しています。インデックスを知る必要がある場合は、インデックスforEach()
他を知る必要があるの文字列、クラス配列、型配列の場合は、反復の方がfor..of
優勢であり、優れています。ただし、低バージョンのブラウザの互換性には注意してください。
パフォーマンス
興味のある読者は、自分でテストするデータのセットを見つけることができ、記事では結果が直接示され、対応する説明が提供されます。
-
for
ループには追加の関数呼び出しスタックやコンテキストがないため、もちろん最も簡単です。 -
for...of
データ構造に Iterator インターフェイスがある限り、そのメンバーを反復するために使用できます。キー値を直接読み取ります。 -
forEach
これは実際には想像よりも複雑で、実際にはarray.forEach(function(currentValue, index, arr), thisValue)
通常の for ループの糖衣構文ではなく、実行中に考慮する必要があるパラメーターとコンテキストが多数あるため、パフォーマンスが低下する可能性があります。 -
map()
最も遅いのは、その戻り値が同じ長さのまったく新しい配列であり、配列の作成と割り当てのパフォーマンスのオーバーヘッドが非常に高いためです。 -
for...in
カスタムで追加されたプロパティも含め、オブジェクトのすべてのプロパティを徹底的に列挙する必要があり、それらも横断することができます。また、for...in
キーはString
型であり、変換処理があり、オーバーヘッドが比較的大きいです。
要約する
実際の開発では、セマンティクス、可読性、プログラムのパフォーマンスを組み合わせて、使用するソリューションを選択する必要があります。
何らかの規則に従って配列を別の配列にマップする必要がある場合は、map を使用する必要があります。
単純なトラバースが必要な場合は、forEach または for of を使用します。
イテレータをトラバースする必要がある場合は、for of を使用します。
対象となるアイテムをフィルタリングする必要がある場合は、filterr を使用します。
最初にルールに従って新しい配列にマップし、次に条件に従ってフィルタリングする必要がある場合は、マップとフィルタを使用します。
つまり、対策を現地の状況に適応させ、時間の経過とともに変化させます。パフォーマンスを追求するあまり、セマンティクスや読みやすさを軽視しないでください。あなたのルールの下では、5 人はそれぞれの強みを発揮することしかできず、誰も支配しようとすることはできません。