刚看到有人写这个:
let id = 1;
...
let employee = null;
for (const e of employees) {
if (e.id === id) {
employee = e;
break;
}
}
似乎是一种过于复杂的写法:
let id = 1;
...
let employee = employees.find(e => e.id === id);
使用带有
break
的循环与 find()
相比有什么好处吗?
find()
幕后的实现是什么?
尝试过这个:
var startTime, endTime;
function start() {
startTime = new Date();
};
function end() {
endTime = new Date();
var timeDiff = endTime - startTime; //in ms
console.log(timeDiff + " milliseconds");
}
let employees = [];
for (var i = 10000; i > 0; i--){
let thisEmployee = {
id: i,
name: "Person" + i
}
employees.push(thisEmployee);
}
let id = 1;
let employee1 = null;
start();
for (const e of employees) {
if (e.id === id) {
employee1 = e;
break;
}
}
end();
console.log("Method1: ", JSON.stringify(employee1));
start();
let employee2 = employees.find(e => e.id === id);
end();
console.log("Method2: ", JSON.stringify(employee2));
第一种方法要慢得多:
"12 milliseconds"
"Method1: "
"{\"id\":1,\"name\":\"Person1\"}"
"0 milliseconds"
"Method2: "
"{\"id\":1,\"name\":\"Person1\"}"
我将这两种方法实现为具有相同签名的两种方法(
forBreakMethod(x)
和findMethod (x)
),并通过简单的性能测试规范传递它们。
(() => {
const test = (DATA_LENGTH = 1000, INDEX = 9, TESTS_COUNT = 10000) => {
// data initialization
const employees = [];
for (let i = 1; i <= DATA_LENGTH; i++){
employees.push({ id: i });
}
// methods initialization
const forBreakMethod = (x) => {
const length = employees.length;
for (let i = 0; i < length; i++) {
if (x === employees.id) {
return employees[i];
}
}
}
const findMethod = (x) => {
return employees.find(item => x === item.id);
}
// for-break test
const time1 = performance.now();
for (let i = 0; i < TESTS_COUNT; i++) {
forBreakMethod(INDEX);
}
const time2 = performance.now();
console.log(`[for-break] find ${INDEX} from ${DATA_LENGTH}: ${time2 - time1}`);
// find test
const time3 = performance.now();
for (let i = 0; i < TESTS_COUNT; i++) {
findMethod(INDEX);
}
const time4 = performance.now();
console.log(`[Array.find] find ${INDEX} from ${DATA_LENGTH}: ${time4 - time3}`);
console.log('---------------');
};
test(10, 1, 1000000);
test(10, 5, 1000000);
test(10, 9, 1000000);
console.log('\n');
test(100, 10, 100000);
test(100, 50, 100000);
test(100, 99, 100000);
console.log('\n');
test(1000, 10, 10000);
test(1000, 500, 10000);
test(1000, 999, 10000);
console.log('\n');
test(10000, 10, 10000);
test(10000, 5000, 10000);
test(10000, 9999, 10000);
})();
我看到的一个结论是,如果我们要查找的项目位于数据数组的左侧部分,则 Array.find 方法具有优势,但当结果索引移至右侧时,其性能会降低。 for-break 方法似乎更稳定,因为它的性能不依赖于我们想要找到的索引,但它的成本是巨大的。
非常粗略,如果我们要遍历数据数组的前半部分,我会说 Array.find 方法可以被认为更具性能,否则我会使用 for-break 方法。
PS Chrome、Safari、Firefox,2018。
很明显,原生
find()
函数比循环算法更快。但OP问“使用循环有什么好处......?”
有人可能想要循环的一个理论上的原因是他们是否需要一路处理不匹配的元素。
所以我刚刚尝试了这个:
const array = [0, 1, 2, 3, 4, 5,];
for (const i of array) {
console.log(i);
if (i === 3)
break;
}
array.find(i => {
console.log(i);
return i === 3;
});
两者都输出
0
1
2
3
因此,正如我所期望的那样,他们在找到的第一个答案上都短路了,但至于具体性能,我不能肯定地说一个是否比另一个更好。我想即使性能不相同,也会具有可比性。
对我来说最大的区别是
find
返回值,但是 for 循环必须处理循环中的值,否则将其分配给变量以供稍后使用。这是一个小细节,但它可能会让其他人在查看您的代码时更具可读性。
如果您在一定程度上确定您要查找的元素更接近数组的末尾(右侧),则使用 for() 循环而不是 find 可能会有所帮助。您可以构建 for() 循环以相反的方式开始,因此它只会行进从末尾到元素的长度。例如,在 [1..100] 中查找 99。 for() 只会比较 2 个元素,而 find 会比较 99 个元素。
对性能的阐述。 我发现很奇怪的是,到目前为止我读到的所有内容都表明 find() 比 for() [在正向搜索中]更快,因为当我运行几次时,我观察到相反的情况,即 find() 大约为 30%-50 % 慢点。像这样:
所以我运行了 100 万次,然后对性能时间进行平均。澄清前提:
我创建了一个包含 100,000 个元素的数组。每个元素是一个有2个元素的对象,key = 0到100,000之间的数字,数组按key排序。从这个数组中,我尝试找到 key=97900 的对象,该对象即将结束。
let arr = [{key: 1, value: 1},...]
结果(平均时间以毫秒为单位) a) 查找:0.078452628 毫秒 b) 对于:0.217990078 毫秒 c) 对于[反向]:0.000223515 ms
从图中我们可以看到 for() 有更多不可预测的异常值。我们还可以观察到,一开始 find() 的性能较慢。
如果您想自己运行测试,这里有一些起始代码:
let arr = [];
for (i = 0; i < 100000; i++) {
arr.push({
key: i,
value: i ^ 2
});
}
console.log("find() vs for() vs reverse for() 97,900 in 100,000, array of objects")
console.time("find")
arr.find(i => {
return i.key === 97900
})
let find = console.timeEnd("find")
console.time("for")
for (i = 0; i < arr.length; i++) {
if (arr[i].key === 97900) {
break
}
}
console.timeEnd("for")
console.time("for-rev")
for (i = arr.lenght - 1; i > 0; i--) {
if (arr[i].key === 97900) {
break
}
}
console.timeEnd("for-rev")