共计 7495 个字符,预计需要花费 19 分钟才能阅读完成。
我在 Github explore 上搜索时发现了一个密码生成器(omgopass)(https://github.com/omgovich/omgopass),据说它比其他替代品要快得多。比 600 倍快 password-generator(https://www.npmjs.com/package/password-generator)。
这是基准测试 omgopass 显示:
看到这个后,我记得几周前我做了一个密码生成器,并且没有执行任何基准测试,所以我决定用其他库测试我的方法。
令我惊讶的是,它表现得相当好,在相同的基准测试中获得第二名,如上所示。即使不尝试也很好。
使用我的 pass 生成器进行基准测试 (passGenny)(https://github.com/nombrekeff/pass-genny):
注意事项
该基准测试并不能反映库的质量或开发人员的技能,以真正确保应该进行更多的测试和基准测试。
此外,每个库的功能也有所不同,有些是可读的,有些则不可读。有些使用加密进行随机,有些则不使用。
话虽这么说,
让我们让 passGenny 更快
我决定尝试一下,并尝试优化它,让我们看看原始代码:
class PasswordGenerator {static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz'.split('');
static symbolsChars = '[]{}=?()&%$#@!¡?¿*_-.:;,'.split('');
static numbersString = '0123456789'.split('');
constructor(options = {}) {
this.options = {
uppercase: true,
lowercase: true,
symbols: false,
numbers: true,
readable: false,
length: 12,
...options,
};
}
updateOptions(newOptions = {}) {
this.options = {
...this.options,
...newOptions,
};
}
random(min = 0, max = 10) {
return Math.floor(Math.random() * (max - min) + min
);
}
_getCharactersForOptions() {const combinedCaracters = [];
if (this.options.lowercase)
combinedCaracters.push(...PasswordGenerator.lowerCaseChars);
if (this.options.uppercase)
combinedCaracters.push(...PasswordGenerator.upperCaseChars);
if (this.options.symbols)
combinedCaracters.push(...PasswordGenerator.symbolsChars);
if (this.options.numbers)
combinedCaracters.push(...PasswordGenerator.numbersString);
return combinedCaracters;
}
generate() {let combinedCaracters = this._getCharactersForOptions();
let password = '';
for (let c = 0; c 这个类的作用是,从一组选项中生成密码。它通过将(选项)允许的所有字符组合到一个数组中来实现此目的,然后迭代密码的长度(由选项定义),并从该数组中获取随机字符。
够简单吧?现在,我认为我们可以对此进行一些优化,好吗?
优化 1
好的,我注意到的第一件事是,在 中_getCharactersForOptions,我使用数组来保存有效字符。使用扩展运算符将它们附加到 combinedCaracters 数组中。
这有点多余,因为我们可以一直使用字符串。连接字符串比组合数组便宜得多。
让我们看看我们可以改变什么。
首先我们需要改变存储字符的方式,我们不需要分割它们:
class PasswordGenerator {
static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz';
static symbolsChars = '[]{}=?()&%$#@!¡?¿*_-.:;,';
static numbersString = '0123456789';
// ... more code
}
太好了,现在我们来修改_getCharactersForOptions 方法:
class PasswordGenerator {_getCharactersForOptions() {
let combinedCaracters = '';
if (this.options.lowercase)
combinedCaracters += PasswordGeneratorFast1.lowerCaseChars;
if (this.options.uppercase)
combinedCaracters += PasswordGeneratorFast1.upperCaseChars;
if (this.options.symbols)
combinedCaracters += PasswordGeneratorFast1.symbolsChars;
if (this.options.numbers)
combinedCaracters += PasswordGeneratorFast1.numbersString;
return combinedCaracters;
}
}
请注意我们现在如何返回一个字符串,而不是一个数组。
让我们看看它在基准测试中的表现如何
妈的,没想到变化这么大,几乎翻了一倍。
正如您所看到的,在这种特殊情况下,字符串的性能比数组好得多。
可是等等
我想我可以进一步优化它,你可能已经注意到,使用相同的选项,结果_getCharactersForOptions 总是相同的。这意味着我们不需要连接每个密码上的字符串,我们只需要在选项更改时生成它们。
我们可以通过多种方式来解决这个问题,使用记忆化(可能更好)、围绕对象创建代理或我接下来将向您展示的简单方法。
优化 2
我要做的是,将选项设为私有,并迫使人们使用 updateOptions 方法更改选项。这将允许我标记选项是否已更改。
让我们看一下完整的示例,然后我将对其进行分解:
class PasswordGeneratorFast2 {
static upperCaseChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
static lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz';
static symbolsChars = '[]{}=?()&%$#@!¡?¿*_-.:;,';
static numbersString = '0123456789';
constructor(options = {}, randomFn) {
this._options = {
uppercase: true,
lowercase: true,
symbols: false,
numbers: true,
readable: false,
length: 12,
...options,
};
this._random = randomFn || mathRandom;
this._combinedCharacters = '';
this._optionsChanged = true;
this._getCharactersForOptions();}
updateOptions(newOptions = {}) {
this._options = {
...this._options,
...newOptions,
};
this._optionsChanged = true;
}
generate() {const combinedCaracters = this._getCharactersForOptions();
const length = combinedCaracters.length;
let password = '';
for (let c = 0; c 我们添加, 指示自上次调用_optionsChanged 以来选项是否已更改。_getCharactersForOptions
我们将最后一个组合字符存储在_combinedCharacters
我们修改_getCharactersForOptions,这样如果选项没有改变,我们返回最后生成的_combinedCharacters
我们改变 password +=(password.concat()在我的测试中,它 concat 的表现比 += 更好)
就是这样,让我们看看它是如何做到的:
![passGenny 的基准测试具有两个优化级别 passGenny 的基准测试具有两个优化级别](https://yojack.cn/wp-content/themes/wordpress-theme-puock-master/assets/img/z/load.svg)
如果你问我的话,令人印象深刻的是,我们传球的速度比吉尼快了一倍,以相当大的优势取得了第一。如果我们像 omgovich 那样表述它,它比密码生成器 passGenny(https://www.npmjs.com/package/password-generator)快 2,444 倍
从中可以得到什么?
-
保持简单可以等同于高性能
-
如果不需要,不要使用数组
-
检查是否每次都需要执行操作
-
如果您需要性能,有时较小的事情会产生最大的差异
PD:我不是性能专家,所以我可能会错过一些重要的事情,如果我错过了某些内容或误解了结果,请告诉我。
网友反馈留言 1
您还可以进行一些改进(主要针对 DX,而不是针对性能):
-
使用 使私有成员真正成为私有成员 #。
-
使用 getter 和 setter。
-
将静力学移至常量。
-
的经典类过程 this.generate = this.generate.bind(this);,因此人们可以在地图等内容中使用该函数而不会出现问题。
应用这些建议,代码看起来像这样(我添加了 JSDocs 以获得更好的 DX):
/**
* @typedef PasswordGeneratorOptions
* @property {number} [length=12]
* @property {boolean} [lowercase=true]
* @property {boolean} [numbers=true]
* @property {typeof mathRandom} [randomFunction=mathRandom]
* @property {boolean} [symbols=false]
* @property {boolean} [uppercase=true]
*//**
* @param {number} min
* @param {number} max
*/const mathRandom = (min, max) => Math.floor(Math.random() * (max - min) + min);const uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";const lowercaseChars = "abcdefghijklmnopqrstuvwxyz";const symbolsChars = "[]{}=?()&%$#@!¡?¿*_-.:;,";const numbersString = "0123456789";export class PasswordGeneratorFast {/** @type {PasswordGeneratorOptions} */
#options = {
length: 12,
lowercase: true,
numbers: true,
randomFunction: mathRandom,
symbols: false,
uppercase: true
};
#characters = "";
#optionsChanged = true;
set options(options) {
this.#optionsChanged = true;
this.#options = {
...this.#options,
...options
};
}
get options() {return this.#options;}
get characters() {if (this.#optionsChanged) {
this.#characters =
(this.#options.lowercase ? lowercaseChars : "") +
(this.#options.uppercase ? uppercaseChars : "") +
(this.#options.symbols ? symbolsChars : "") +
(this.#options.numbers ? numbersString : "");
this.#optionsChanged = false;
}
return this.#characters;
}
/** @param {PasswordGeneratorOptions} options */
constructor(options = {}) {
this.options = options;
this.generate = this.generate.bind(this);
}
generate() {const { characters} = this;
const length = characters.length;
let password = "";
for (let char = 0; char 性能方面我的可能是最差的(没有测试它,但我认为 setter/getter 的性能不如仅使用方法,我可能是错的),但除了性能之外,我们总是必须考虑 DX,并且从我的从角度来看,在处理类时,getter 和 setter 提供了更好的 DX。我个人更喜欢只使用函数,甚至不去上课。
网友反馈留言 1
我的主要原因主要是用法:文章来源:https://www.toymoban.com/diary/js/377.html
import {Something} from"./Something";
console.log(Something.aValue);
// vs
import {aValue} from "./Something";
console.log(aValue);
我知道,超级利基,但是以前从类中有用的封装现在我从模块中获得:D文章来源地址 https://www.toymoban.com/diary/js/377.html
到此这篇关于优化 JavaScript - 密码生成器(速度提高 2.15 倍)的文章就介绍到这了, 更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持 TOY 模板网!
原文地址:https://www.toymoban.com/diary/js/377.html
如若转载,请注明出处:如若内容造成侵权 / 违法违规 / 事实不符,请联系站长进行投诉反馈,一经查实,立即删除!