数组
for循环
最传统的方法,按下标存取,也是最高效的方法
let arr = [1,2,3,4]
for(let i = 0; i < arr.length; i++){
console.log(arr[i])
}
//1,2,3,4
//不用每次计算len,更加高效
for(let i = 0, len = arr.length; i < len; i++){
console.log(arr[i])
}
//1,2,3,4
for...of循环
for...of语句创建一个迭代器(ES6引入,迭代器只会便利可枚举属性)。每一次循环都会调用迭代器的next对象。并返回当前该迭代器的值。
let arr = [1,2,3,4]
for(let val of arr){
console.log(val)
}
//1,2,3,4
另外只要实现了迭代器的数据才能使用for...of循环。具体有:Array,Maps,Set,String,Arguments Object参数对象,Generators(生成器)
for...in循环
for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。
let arr = [1,2,3,4]
for(let i in arr){
console.log(i)
}
//0,1,2,3
注意:
for ...in循环遍历得到的结果为数据的键(数组即为下标)。- 数组最好不要用
for...in,因为for...in循环是为遍历对象而且设计的。 - 该方法也可用于
String遍历下标。 for...in会顺着原型链向上遍历,原型链上所有的可遍历对象都会被遍历。比如(定义Array.prototype.max = () => {...}来获取数组中最大值,如果这么定义,那么这个属性也会被for...in纳入遍历)
Array.prototype.entries()
该方法一个新的 Array 迭代器对象。Array Iterator是对象,它的原型(proto:Array Iterator)上有一个next方法,可用用于遍历迭代器取得原数组的[key,value]。
var arr = ["a", "b", "c"];
var iterator = arr.entries();
console.log(iterator);
/*Array Iterator {}
__proto__:Array Iterator
next:ƒ next()
Symbol(Symbol.toStringTag):"Array Iterator"
__proto__:Object
*/
用法一(直接使用Iterator的next对象):
const array1 = ['a', 'b', 'c'];
const iterator1 = array1.entries();
console.log(iterator1.next().value);
// expected output: Array [0, "a"]
console.log(iterator1.next().value);
// expected output: Array [1, "b"]
用法二(二维数组按行排序):
function sortArr(arr) {
var goNext = true;
var entries = arr.entries();
while (goNext) {
var result = entries.next();
if (result.done !== true) {
result.value[1].sort((a, b) => a - b);
goNext = true;
} else {
goNext = false;
}
}
return arr;
}
var arr = [[1,34],[456,2,3,44,234],[4567,1,4,5,6],[34,78,23,1]];
sortArr(arr);
/*(4) [Array(2), Array(5), Array(5), Array(4)]
0:(2) [1, 34]
1:(5) [2, 3, 44, 234, 456]
2:(5) [1, 4, 5, 6, 4567]
3:(4) [1, 23, 34, 78]
length:4
__proto__:Array(0)
*/
法三(使用for…of 循环)最典型:
var arr = ["a", "b", "c"];
var iterator = arr.entries();
for (let v of iterator) {
console.log(v);
}
// [0, "a"]
// [1, "b"]
// [2, "c"]
for (let [index, value] of iterator) {
console.log(index+'---'+value);
}
// 0-"a"
// 1-"b"
// 2-"c"
Array.prototype.keys()
keys() 方法返回一个包含数组中每个索引键的Array Iterator对象。
与上面的Array.prototype.entries()相同,只是遍历的是索引键。用法与上面完全一致,本质也是迭代器。
典型用法(结合for...of):
let arr = [1,2,3,4]
for(let i of arr.keys()){
console.log(i)
}
//0,1,2,3
Array.prototype.values()
values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值
与上面的Array.prototype.entries()相同,只是遍历的是索引键。用法与上面完全一致,本质也是迭代器。
典型用法(结合for...of):
const arr = ['a', 'b', 'c'];
for (const value of arr.values()) {
console.log(value);
}
//a,b,c
Array.prototype.every()
**every()**方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
其接收一个回调函数callback,callback 在被调用时可传入三个参数:元素值,元素的索引,原数组。
every 不会改变原数组。
every 和数学中的"所有"类似,**当所有的元素都符合条件才会返回true。**正因如此,若传入一个空数组,无论如何都会返回 true。(这种情况属于无条件正确:正因为一个空集合没有元素,所以它其中的所有元素都符合给定的条件。)
注意:若收到一个空数组,此方法在一切情况下都会返回 true。
典型用法:
let arr = [5,6,4,7,8]
arr.every((value, index, arr) => {
return value > 3
})
//true(因为所有的value都大于3)
arr.every((value, index, arr) => {
return value > 5
})
//false(其中4,5不满足条件)
every方法不会改变数组。
Pollyfill(最基础,下同)
if (!Array.prototype.every) {
Object.defineProperty(Array.prototype, every, {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.every ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = true;
for (let [i, v] of this.entries()) {
if (!callback(v)) {
res = false;
break;
}
}
return res;
},
});
}
Array.prototype.some()
这个方法与上面的方法使用方法完全相同,但是所用相反,**some()**方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
其接收一个回调函数callback,callback 在被调用时可传入三个参数:元素值,元素的索引,原数组。
**注意:**如果用一个空数组进行测试,在任何情况下它返回的都是false。
典型用法:
let arr = [6,9,3,4]
arr.some((value, index, arr) => {
return value > 8
})
//true(因为存在一个9大于8)
arr.some((value, index, arr) => {
return value > 12
})
//false(因为所有的value都小于12)
some方法不会改变数组。
Pollyfill
if(!Array.prototype.some){
Object.defineProperty(Array.prototype, 'some', {
enumerable: false,
value: function (callbakc) {
if (this === null) {
throw new TypeError(
'Array.prototype.some ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = false;
for (let [i, v] of this.entries()) {
if (callbakc(v, i, this)) {
res = true;
break;
}
}
return res;
},
});
}
Array.prototype.filter()
filter() 方法创建一个新数组, 其包含通过所提供函数实现的过滤的所有元素的结果。
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
返回:一个数组,包含所有的元素的检测结果。
let arr = [1, 2, 3, 4]
arr.filter((value, index, arr) => {
return value > 2
})
//[3, 4]
该方法可用于剔除不合法数据,比如
let arr = [1,2,3, undefined, null, 4, 5]
arr.filter((value, index, arr) => {
return value != undefined && value != null
})
//[1, 2, 3, 4, 5]
flter方法不会改变数组。
Pollyfill
if (!Array.prototype.filter) {
Object.defineProperty(Array.prototype, 'filter', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.filter ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i])) {
res.push(this[i]);
}
}
return res;
},
});
}
Array.prototype.find()
find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
返回:数组中满足提供的测试函数的第一个元素的值。
let arr = [1,2,3,4]
arr.find((value, index, arr) => {
return value > 2
})
//2(由于3,4均大于2,但是3是第一个,所以返回3的下标2)
find方法不会改变数组。
Pollyfill
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.find ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = undefined;
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
res = this[i];
break;
}
}
return res;
},
});
}
Array.prototype.findIndex()
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
返回:数组中满足提供的测试函数的最后一个元素的值。
let arr = [1,2,3,4]
arr.find((value, index, arr) => {
return value > 2
})
//3(由于3,4均大于2,但是4是最后一个,所以返回4的下标3)
Pollyfill
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.findIndex ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = -1;
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
res = i;
break;
}
}
return res;
},
});
}
Array.prototype.forEach()
**forEach()**方法对数组的每个元素执行一次给定的函数。
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
返回:undefined。
forEach() 为每个数组元素执行一次 callback 函数;与 map() 或者 reduce() 不同的是,它总是返回 undefined 值,并且不可链式调用。其典型用例是在一个调用链的最后执行副作用(side effects,函数式编程上,指函数进行 返回结果值 以外的操作)。
forEach 不会直接改变调用它的对象,但是那个对象可能会被 callback 函数改变。
示例:
let arr = [1, 2, 3, 4]
let res = []
arr.forEach((value, index, arr) => {
res.push(value*2)
})
console.log(res) //[2,4,6,8]
Pollyfill
if (!Array.prototype.forEach) {
Object.defineProperty(Array.prototype, 'forEach', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.forEach ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
},
});
}
Array.prototype.map()
**map()**方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。(forEach类似,区别是forEach不用返回,一起操作都在回调函数中进行)
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
返回:一个由原数组每个元素执行回调函数的结果组成的新数组。
上面的示例重写:
let arr = [1, 2, 3, 4]
let res = arr.map((value, index, arr) => {
return value * 2
})
console.log(res) //[2, 4, 6, 8]
callback函数只会在有值的索引上被调用;那些从来没被赋过值或者使用delete删除的索引则不会被调用。- 因为
map生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。 map不修改调用它的原数组本身(当然可以在callback执行时改变原数组)- 根据规范中定义的算法,如果被map调用的数组是离散的(如:arr = [emptyx2, 3]),新数组将也是离散的保持相同的索引为空。
Pollyfill
if (!Array.prototype.map) {
Object.defineProperty(Array.prototype, 'map', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.map ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let res = [];
for (let i = 0; i < this.length; i++) {
res.push(callback(this[i], i, this));
}
return res;
},
});
}
Array.prototype.reduce()
reduce()方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
接受:函数累计处理的结果reducer 函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
返回值:函数累计处理的结果
示例:
- 数组里所有值的和
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// 和为 6
- 累加对象数组里的值
要累加对象数组中包含的值,必须提供初始值,以便各个item正确通过你的函数。
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
return accumulator + currentValue.x;
},initialValue)
console.log(sum) // logs 6
你也可以写成箭头函数的形式:
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
(accumulator, currentValue) => accumulator + currentValue.x
,initialValue
);
console.log(sum) // logs 6
- 计算数组中每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
Polyfill
reduce 被添加到 ECMA-262 标准第 5 版,因此它在某些实现环境中可能不被支持。把下面的代码添加到脚本开头可以解决此问题,从而允许在那些没有原生支持 reduceRight 的实现环境中使用它。
// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback /*, initialValue*/) {
if (this === null) {
throw new TypeError( 'Array.prototype.reduce ' +
'called on null or undefined' );
}
if (typeof callback !== 'function') {
throw new TypeError( callback +
' is not a function');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// Steps 3, 4, 5, 6, 7
var k = 0;
var value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}
// 3. If len is 0 and initialValue is not present,
// throw a TypeError exception.
if (k >= len) {
throw new TypeError( 'Reduce of empty array ' +
'with no initial value' );
}
value = o[k++];
}
// 8. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
// i. Let kValue be ? Get(O, Pk).
// ii. Let accumulator be ? Call(
// callbackfn, undefined,
// « accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}
// d. Increase k by 1.
k++;
}
// 9. Return accumulator.
return value;
}
});
}
//concise method
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
enumerable: false,
value: function (callback) {
if (this === null) {
throw new TypeError(
'Array.prototype.map ' + 'called on null or undefined'
);
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
let count = 0;
for (let i = 0; i < this.length; i++) {
count += callback(count, this[i], i, this);
}
},
});
}
Array.prototype.reduceRight()
这个方法与Array.prototype.reduce()只是执行顺序上相反(从右到左)。
**reduceRight()**方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
接收:一个回调函数,函数包含3个参数。value(元素的值),index(元素的索引),arr(被遍历的数组本身)
求一个数组中所有值的和
var sum = [0, 1, 2, 3].reduceRight(function(a, b) {
return a + b;
});
// sum is 6 (虽然结果相同,但是是从右到左加)
展示 reduce 与 reduceRight 之间的区别
var a = ['1', '2', '3', '4', '5'];
var left = a.reduce(function(prev, cur) { return prev + cur; });
var right = a.reduceRight(function(prev, cur) { return prev + cur; });
console.log(left); // "12345"
console.log(right); // "54321"
Polyfill
reduceRight 被添加到 ECMA-262 标准第 5 版,因此它在某些实现环境中可能不被支持。把下面的代码添加到脚本开头可以解决此问题,从而允许在那些没有原生支持 reduceRight 的实现环境中使用它。
// Production steps of ECMA-262, Edition 5, 15.4.4.22
// Reference: http://es5.github.io/#x15.4.4.22
if ('function' !== typeof Array.prototype.reduceRight) {
Array.prototype.reduceRight = function(callback /*, initialValue*/) {
'use strict';
if (null === this || 'undefined' === typeof this) {
throw new TypeError('Array.prototype.reduceRight called on null or undefined');
}
if ('function' !== typeof callback) {
throw new TypeError(callback + ' is not a function');
}
var t = Object(this), len = t.length >>> 0, k = len - 1, value;
if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k >= 0 && !(k in t)) {
k--;
}
if (k < 0) {
throw new TypeError('reduceRight of empty array with no initial value');
}
value = t[k--];
}
for (; k >= 0; k--) {
if (k in t) {
value = callback(value, t[k], k, t);
}
}
return value;
};
}
下面是几种遍历方法的时间对比,可以看出传统for...耗时最少,;for...in最差。
对象
for...in方法
上面已经提到了for...in方法用于遍历数据的索引键。所以这个方法也可以用于遍历对象,具体方法如下:
let obj = {a : 1, b : 2, c : 3, d : 4}
for(let i in obj){
console.log(`${i}---${obj[i]}`)
}
/*
*a---1
*b---2
*c---3
*d---4
*/
Object.entries()
**Object.entries()**方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
参数
-
obj可以返回其可枚举属性的键值对的对象。
返回值
Object.entries()返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
示例
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// array like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
// getFoo is property which isn't enumerable
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
// non-object argument will be coerced to an object
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
// iterate through key-value gracefully
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}
// Or, using array extras
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
});
Object.keys()
**Object.keys()**方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
参数
-
obj
要返回其枚举自身属性的对象。
返回值
一个表示给定对象的所有可枚举属性(索引键)的字符串数组。
描述
Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性(索引键)。这些属性的顺序与手动遍历该对象属性时的一致。
示例
// 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']
// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
// 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']
Object.values()
**Object.values()**方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
参数
-
obj被返回可枚举属性值的对象。
返回值
一个包含对象自身的所有可枚举属性值的数组。
描述
Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
示例
var obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
// array like object with random key ordering
// when we use numeric keys, the value returned in a numerical order according to the keys
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
// getFoo is property which isn't enumerable
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = 'bar';
console.log(Object.values(my_obj)); // ['bar']
// non-object argument will be coerced to an object
console.log(Object.values('foo')); // ['f', 'o', 'o']
本文大部分参考了MDN