var arr = [ 'a', 'b', 'c' ];
var obj = { 'key1': 'a', 'key2': 'v2', 'key3': 'v3'};
1 for循环
提前储存length提升遍历的性能:
//其实我在实际使用中,常常因为提前储存了len而导致出错。视情况而定
for ( var i = 0, len = arr.length; i < len; i++){
console.log(arr[i]);
}
还可以这么使用
for ( var i = 0, len = arr.length; i < len; i++);
比如只是纯粹为了计算 i 的值,可以省略掉 { } 用 ; 代替
2 for in
用来循环对象中非Symbol、可enumerable的属性。通过Array、Object创建的对象,所继承的non–enumerable的属性,不可遍历。比如,String的 indexOf() 方法 or Object toString() 方法.
这是一个无序遍历。
不应该去循环Array,因为Array的index顺序是必须的。但是for in不能保证这个index的顺序。最好使用forEach和for of来遍历数组
总的来说,最好不要在遍历期间,添加删除或修改属性。操作后不能确保这个属性还可以访问。
会遍历出原型链上的属性
Object.prototype.isTrue = 1;
for(var o in obj){
console.log(o)
}
//会遍历出isTrue属性
可以使用hasOwnProperty来规避
for(var o in obj){
if(obj.hasOwnProperty(o)){
console.log(o)
}
}
//如此不会打印出isTrue了
//或者使用getOwnPropertyNames()
Object.getOwnPropertyNames(obj)
//[ 'key1','key2','key3']
forEach
arr.forEach(function callback(currentValue[, index[, array]]) {
//your iterator
}[, thisArg]);
thisArg决定了callback function的context,不传默认为undefined
callback处理的item必须在调用forEach之前确定,否则不会访问到。即使item的值在这段时间发生改变,callback也不会获知。如果是被删除,之后的循环不会遍历到。
除非抛异常,否则无法中断forEach函数。(break return 失效) 。如果你想break,可以使用以下function
- A simple loop
- A for…of loop
- Array.prototype.every()
- Array.prototype.some()
- Array.prototype.find()
- Array.prototype.findIndex()
arr.forEach((item, index, array) => {
console.log(index, item);
});
使用this
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3
obj.sum;
// 16
copy函数的实现
function copy(obj) {
const copy = Object.create(Object.getPrototypeOf(obj));//获取obj的prototype
//getOwnPropertyNames获取可枚举和不可枚举的属性
const propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
//getOwnPropertyDescriptors 得到属性描述,不仅仅是包含value,还包含enumerable...
{ foo: { value: 123,
writable: true,
enumerable: true,
configurable: true
}
//重新给copy定义属性和描述
Object.defineProperty(copy, name, desc);
});
return copy;
}
const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // obj2 looks like obj1 now
for…of
用来遍历 iterable 对象:String Array 类Array对象(arguments和NodeList, TypedArray, Map, Set,自定义的可遍历的对象)
let iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
TypedArray
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]
// ['b', 2]
// ['c', 3]
for (let [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
Map和Object的区别:
- 一个对象的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
- Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
- 你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
- Map 是可迭代的,而 Object 的迭代需要先获取它的键数组然后再进行迭代。
- Object 都有自己的原型,所以原型链上的键名有可能和对象上的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
- Map 在涉及频繁增删键值对的场景下会有些性能优势。
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
DOM
// Note: This will only work in platforms that have
// implemented NodeList.prototype[Symbol.iterator]
let articleParagraphs = document.querySelectorAll('article > p');
for (let paragraph of articleParagraphs) {
paragraph.classList.add('read');
}
generators
function* fibonacci() { // a generator function
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
console.log(n);
// truncate the sequence at 1000
if (n >= 1000) {
break;
}
}
iterable objects
var iterable = {
[Symbol.iterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return { value: this.i++, done: false };
}
return { value: undefined, done: true };
}
};
}
};
for (var value of iterable) {
console.log(value);
}
// 0
// 1
// 2
跳出循环条件:break, continue, throw or return.
与for…in的区别
- for…in 无序遍历可enumerable的对象的key(包含原型链和属性值)
- for…of 遍历该对象的value
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
for (let i in iterable) {
//对象上定义的属性和原型链都被遍历出来了
//value不属于enumerable
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}
for (let i of iterable) {//只是对象内部的value
console.log(i); // logs 3, 5, 7
}
map方法
返回一个新数组,不会修改原来的对象
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array }[,
thisArg])
与forEach相同,只是有return值
const map1 = array1.map(x => x * 2);
arr.map(function(value,index){
return value++;
});
//求数组中每个元素的平方根
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]
//使用 map 重新格式化数组中的对象
var kvArray = [{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}];
var reformattedArray = kvArray.map(function(obj) {
var rObj = {};
rObj[obj.key] = obj.value;
return rObj;
});
// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}],
// kvArray 数组未被修改:
// [{key: 1, value: 10},
// {key: 2, value: 20},
// {key: 3, value: 30}]
//获取字符串中每个字符所对应的 ASCII 码
var map = Array.prototype.map
var a = map.call("Hello World", function(x) {
return x.charCodeAt(0);
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
//querySelectorAll 应用
var elems = document.querySelectorAll('select option:checked');
var values = Array.prototype.map.call(elems, function(obj) {
return obj.value;
});
总结:
- for…in可以遍历对象及其原型链上,enumerable属性,可以遍历数组但是你别这么使用
- for…of 遍历 iterable objects(String Array 类Array对象(arguments和NodeList, TypedArray, Map, Set,自定义的可遍历的对象))的value,可以break return。
- forEach 遍历数组
- map方法:遍历数组并返回一个新数组