D3 v6正常运行的scaleBand在v7失效,咨询版本差异或操作问题
D3 v6与v7中scaleBand处理数组类型定义域键的行为差异
v6.7.0中的正常表现
在D3 v6.7.0中,以下代码的两个测试用例均能正常返回对应刻度值:
const range = [0, 828.156]; const domain = [[300,1],[300,2],[300,3],[400,1],[400,2],[400,3],[400,4]] const scale = d3.scaleBand() .domain(domain) .range(range); // test 1: 调用domain中的原数组实例,正常生效 const a = domain[3]; console.log('array a', a); console.log('scaled a:', scale(a)); // test 2: 声明内容相同的新数组,同样正常生效 const b = [400, 1]; console.log('array b', b); console.log('scaled b:', scale(b));
引入的D3脚本:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js" integrity="sha512-cd6CHE+XWDQ33ElJqsi0MdNte3S+bQY819f7p3NUHgwQQLXSKjE4cPZTeGNI+vaxZynk1wVU3hoHmow3m089wA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
v7.9.0中的异常表现
升级到D3 v7.9.0后,测试2无法返回预期刻度值(输出undefined),测试1仍正常:
const range = [0, 828.156]; const domain = [[300,1],[300,2],[300,3],[400,1],[400,2],[400,3],[400,4]] const scale = d3.scaleBand() .domain(domain) .range(range); // test 1: 调用domain中的原数组实例,正常生效 const a = domain[3]; console.log('array a', a); console.log('scaled a:', scale(a)); // test 2: 声明内容相同的新数组,无法匹配到刻度值 const b = [400, 1]; console.log('array b', b); console.log('scaled b:', scale(b));
引入的D3脚本:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js" integrity="sha512-vc58qvvBdrDR4etbxMdlTt4GBQk1qjvyORR2nrsPsFPyrs+/u5c3+1Ct6upOgdZoIl7eq6k3a1UPDSNAQi/32A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
原因说明
这是D3从v6到v7的有意版本变更,并非你的操作错误:
- 在v6中,scaleBand内部对数组类型的定义域键采用了值序列化比较(类似将数组转为字符串后匹配),因此内容相同的不同数组实例会被视为同一个键。
- 在v7中,scaleBand改用原生Map存储键值映射,Map的键比较遵循SameValueZero规则:对于引用类型(如数组),只有当两个引用指向同一个对象实例时才会被判定为相等。因此新声明的
[400,1]和domain中的原数组是不同实例,无法匹配。
解决方案
推荐以下两种简单可行的方案:
- 使用原始类型作为定义域键:将数组转换为字符串或其他原始值,比如把
[300,1]转为"300-1",确保内容相同的键会被视为同一值:const domain = ["300-1", "300-2", "300-3", "400-1", ...]; // 查询时同样使用对应字符串 const b = "400-1"; console.log('scaled b:', scale(b)); - 复用定义域中的原数组实例:如果必须使用数组作为键,确保查询时使用的是domain中的原数组引用,而非新创建的数组。
内容的提问来源于stack exchange,提问作者toms
相关产品推荐
相关产品推荐

