Chance.js 随机 MAC 地址生成详解:chance.mac() 用法、delimiter 配置与底层实现
Chance.js 随机 MAC 地址生成详解:chance.mac() 用法、delimiter 配置与底层实现
本篇技术指南以 Chance.js 官方文档中的 mac 说明 为骨架,完整讲解 chance.mac() 的调用方式、delimiter 分隔符配置,并结合仓库源码与测试用例,深入剖析 EUI-48 MAC 地址的生成算法与可复现机制。读完本文,你将掌握用 Chance 生成标准格式 MAC 地址的实战能力,并理解其在测试数据、网络设备模拟等场景下的正确用法。
一、功能概述:生成随机 EUI-48 MAC 地址
chance.mac() 是 Chance.js 提供的 Web 域随机生成器之一,用于返回一个随机生成的 MAC 地址(EUI-48)。EUI-48 即传统网卡地址标准,长度为 48 位(6 个八位组 / octet),在人类可读形式上表现为 6 组两位十六进制数,组间用分隔符连接,例如 08:c2:88:ad:49:23。
在 chance.js 中,mac() 被实现为 Chance 原型方法,与其同族的 ip()、ipv6()、domain() 等方法并列,供开发者在构造随机网络相关数据时直接调用。
二、基本用法
不带任何参数调用 chance.mac(),即可获得一个使用默认冒号(:)分隔的随机 MAC 地址:
// usage
chance.mac()
=> '08:c2:88:ad:49:23'
调用形式非常简单,无需初始化额外状态。在实际项目中,这常用于:
- 生成网络设备(交换机、路由器、物联网终端)的模拟 MAC 地址;
- 填充测试数据库中的设备标识字段;
- 构造抓包、流量分析等工具所需的仿真数据源。
三、delimiter 参数:自定义八位组分隔符
mac() 支持传入一个可选的配置对象,其中唯一对外暴露的配置项是 delimiter,用于指定八位组之间的分隔字符。默认值为冒号 :。
chance.mac({delimiter: '-'})
=> '00-0d-b9-52-f2-f0'
注:原始文档中该示例写作
chance.domain({delimiter: '-'}),经核对源码与上下文,此处实际调用的是chance.mac({delimiter: '-'}),本文已按正确用法呈现。
delimiter 并不仅限于 : 或 -,传入任意字符串都会被原样用作分隔符。这在需要贴合不同系统输出格式(如 Windows 的 -、Cisco 的 .)时非常实用。测试用例 test/test.web.js 即验证了 :、-、. 三种分隔符下输出格式均正确:
test('mac() uses delimiter option for MAC address', t => {
_.times(1000, () => {
const delimiter = ([':','-','.'])[Math.floor(Math.random() * 3)]
let mac = chance.mac({ delimiter })
t.true(_.isString(mac))
t.is(mac.split(delimiter).length, 6)
t.true((
new RegExp(`^${Array(6).fill('[0-9a-f]{2}').join(`\\${delimiter}`)}$`)
).test(mac))
})
})
该用例对每种分隔符各执行 1000 次,断言:结果为字符串、按分隔符拆分后恰好 6 段、且每段均匹配 [0-9a-f]{2}(即两位小写十六进制数)。
四、底层实现原理:八位组如何生成
要深入理解 mac() 的行为,需要读源码。其完整实现如下(chance.js):
Chance.prototype.mac = function (options) {
// Todo: This could also be extended to EUI-64 based MACs
// (https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml#ethernet-numbers-4)
// Todo: This can return some reserved MACs (similar to IP function)
// this should probably be updated to account for that rare as it may be
options = initOptions(options, { delimiter: ':' });
return this.pad(this.natural({max: 255}).toString(16),2) + options.delimiter +
this.pad(this.natural({max: 255}).toString(16),2) + options.delimiter +
this.pad(this.natural({max: 255}).toString(16),2) + options.delimiter +
this.pad(this.natural({max: 255}).toString(16),2) + options.delimiter +
this.pad(this.natural({max: 255}).toString(16),2) + options.delimiter +
this.pad(this.natural({max: 255}).toString(16),2);
};
整个生成流程由三个基础构件组成,逐一拆解如下:
1. 默认值合并(initOptions)
initOptions 是 Chance.js 内部的通用参数合并函数(chance.js):当传入的 options 中未定义某键时,才用默认值填充。因此 mac() 未传参时 delimiter 被设为 :;传了 delimiter 则使用用户值,其余逻辑不变。
2. 随机八位组(natural + toString(16))
每个八位组通过 this.natural({max: 255}) 生成。natural 的默认 min 为 0(见 chance.js),所以这里实际产生 0~255(含端点)的均匀随机整数,即一个八位组的完整取值范围。随后 .toString(16) 将其转为十六进制字符串(0~ff)。
3. 补零对齐(pad)
单个八位组转成十六进制后可能是 0、a、ff 等 1~2 位字符串,this.pad(number, 2)(chance.js)会将其左补 0 至至少两位,保证输出恒为 00~ff 的两位小写形式。这正是测试正则 [0-9a-f]{2} 所要求的格式。
将上述三个步骤在 6 个八位组上重复并拼接,即得到形如 08:c2:88:ad:49:23 的完整 EUI-48 地址。
从源码结构可以推断的边界行为
- 输出恒为小写十六进制:
toString(16)与pad均不产生大写字符,测试断言也印证了这一点。 - 可能生成保留/特殊地址:源码注释明确指出,当前实现与
ip()类似,没有过滤保留 MAC 地址(如全00、组播、本地管理等特殊范围),虽然出现概率极低,但在对地址合法性有严格要求的场景(如真实的网络协议仿真)需自行校验。 - 暂不支持 EUI-64:源码注释中的 TODO 表明该函数未来可能扩展为基于 EUI-64 的 MAC 生成,当前版本只产出 6 字节地址。
五、测试验证:格式与分隔符的双重保障
除上文的 delimiter 测试外,仓库还针对默认格式单独编写了测试(test/test.web.js):
test('mac() returns what looks like an MAC address (EUI-48)', t => {
_.times(1000, () => {
let mac = chance.mac()
t.true(_.isString(mac))
t.is(mac.split(':').length, 6)
t.true(/^[0-9a-f]{2}\:[0-9a-f]{2}\:[0-9a-f]{2}\:[0-9a-f]{2}\:[0-9a-f]{2}\:[0-9a-f]{2}$/.test(mac))
})
})
该用例循环 1000 次断言三点:返回值为字符串;按 : 拆分后恰为 6 段;整体匹配六组两位小写十六进制数的正则。两者合计 2000 次随机抽样验证,为 mac() 的格式稳定性提供了有力证据。如需自行复跑,可在仓库根目录执行 yarn 安装依赖后运行 yarn test(脚本定义见 package.json)。
六、可复现性:结合 seed 生成固定序列
Chance.js 基于 Mersenne Twister 伪随机数生成器构建(见 README.md 与 seed 文档),因此 mac() 的输出同样可复现:只要用相同的 seed 实例化 Chance,多次调用 chance.mac() 就会产生完全相同的地址序列。
var chance1 = new Chance(12345);
var chance2 = new Chance(12345);
// 两次调用得到相同的随机数序列,进而得到相同的 MAC 地址序列
console.log(chance1.mac());
console.log(chance2.mac());
这对需要确定性回归测试的场景(如快照比对、Golden File 测试)非常关键:既保留了 MAC 地址的“随机观感”,又保证了同一 seed 下结果可重复。
七、完整实战示例(Node.js)
在 Node.js 环境中按 安装文档 安装并实例化后即可使用:
npm install chance
# 或 yarn add chance
const Chance = require('chance');
const chance = new Chance();
// 默认冒号分隔
console.log(chance.mac()); // => '08:c2:88:ad:49:23'
// 自定义分隔符
console.log(chance.mac({delimiter: '-'})); // => '00-0d-b9-52-f2-f0'
console.log(chance.mac({delimiter: '.'})); // => '00.0d.b9.52.f2.f0'
// 批量生成并去重,适合填充设备表
const macs = new Set();
while (macs.size < 10) {
macs.add(chance.mac({delimiter: ':'}));
}
console.log([...macs]);
八、相关内部实现:mac_address
在 chance.js 中还存在一个内部的 mac_address 函数,它支持 separator(默认 :)与 networkVersion(为 true 时使用点分、每 4 位一组共 3 组的 Cisco 风格输出)选项。从代码结构看,它属于另一套独立实现,本文所述的标准入口仍以 chance.mac() 为准;若你遇到需要点分格式输出或探索更早期实现的需求,可查阅该函数源码了解差异。
九、小结
| 要点 | 说明 |
|---|---|
| 默认输出 | chance.mac() 返回小写六组两位十六进制、冒号分隔的 EUI-48 地址 |
| 核心参数 | delimiter:自定义八位组分隔符,默认 : |
| 生成原理 | natural({max:255}) 生成 0~255 随机数 → toString(16) → pad(...,2) 补零拼接 |
| 格式保障 | test/test.web.js 以 2000 次抽样断言格式与分隔符行为 |
| 可复现性 | 基于 Mersenne Twister,同一 seed 下输出序列可完全重复 |
| 注意事项 | 不过滤保留地址;当前仅支持 EUI-48,暂未扩展 EUI-64 |
总而言之,chance.mac() 是一个接口极简、行为可预期、且有完整测试兜底的随机 MAC 地址生成器。理解其 delimiter 配置与底层拼接逻辑后,你便能在测试数据生成、设备模拟、网络工具开发等场景中放心使用,并结合 seed 机制实现可复现的确定性输出。