【D3.js in Action 3 精译_012】1.2.5 与 D3 相关的 JavaScript 基础知识

当前内容所在位置

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
        • 1.2.1 HTML 与 DOM
        • 1.2.2 SVG - 可缩放矢量图形
        • 1.2.3 Canvas 与 WebGL
        • 1.2.4 CSS
        • 1.2.5 JavaScript ✔️
        • 1.2.6 Node 与 JavaScript 框架
        • 1.2.7 Observable 记事本

1.2.5 JavaScript

D3 是在 JavaScript 已有的核心功能上添加了众多新接口方法的一个 JavaScript 库,前期对 JavaScript 稍有了解都将对 D3 的使用有所帮助;此外,您也可以在构建 D3 项目时使用 JavaScript 已有的所有功能特性。

真正意义上的 JavaScript 入门知识就算单独写一本书来介绍都不为过;而本小节仅讨论在 D3 项目中用途最广泛的两个 JavaScript 话题:方法的链式调用(method chaining对象的操作(object manipulation

1 方法的链式调用

搜索网上的 D3 项目示例会发现,对于同一个选中的元素或区域(selection),其调用的方法在写法上往往是一个接一个的——该项技术我们称之为 方法的链式调用(method chaining,又称方法链)。它有利于保持代码的简洁性与可读性。

方法链可以用汽车装配生产线来进行类比。假设写好的脚本可供这样一条装配生产线运行:如下例所示,先声明一个变量 car 并赋值为一个新建的 Car() 对象;然后调用函数 putOnHood(),用于在汽车顶部装上引擎盖;接着再继续调用分别装配车轮、轮胎和车灯的函数。每次连续调用都会为 Car() 对象添加某个构件元素。待所有方法执行完毕后,汽车就有了引擎盖、车轮、轮胎和车灯。每个方法都会将更新后的汽车对象传递给下一个方法,从而形成“链式”(“chaining”)。请注意,每个调用之间都用一个句点(dot)隔开,而且方法的执行顺序就是它们被链接起来的顺序。在本例中,则需要先安装车轮,然后才能安装轮胎,示例如下:

let car = new Car().putOnHood().putOnWheels().putOnTires().putOnLights();

再来看看方法的链式调用在 D3 中的具体应用。假设要从 DOM 中抓取所有 <div> 元素并在其中分别添加一个段落元素。该段落元素的 class 属性假设为 my-class,并包含文本 Wow;之后在向各段落插入一个 <span> 元素,并加粗显示文本 Even More Wow。如果不用方法链,则需要将每个操作存到某个常量,然后在执行下一个操作时调用该常量,如下代码所示。光是看一眼就够累的了……

const mySelection = d3.selectAll("div");
const myParagraphs = mySelection.append("p");
const myParagraphsWithAClass = myParagraphs.attr("class", "my-class");
const myParagraphsWithText = myParagraphsWithAClass.text("Wow");
const mySpans = myParagraphsWithText.append("span");
const mySpansWithText = mySpans.text("Even More Wow")
const myBoldSpans = mySpansWithText.style("font-weight", "900");

而有了方法的链式调用,代码就简洁多了:

d3.selectAll("div").append("p").attr("class", "my-class").text("Wow")
    .append("span").text("Even More Wow").style("font-weight", "900");

在 D3 中,换行(这一点 JavaScript 往往会忽略)和缩进这些链式调用的方法将会非常常见,也使得代码更容易阅读,缩进也有助于查看正在处理的元素:

d3.selectAll("div")
  .append("p")
    .attr("class", "my-class")
    .text("Wow")
  .append("span")
    .text("Even More Wow")
    .style("font-weight", "900");

如果不能完全理解上述代码的含义,您也不用过于担心。这里只是为了让您熟悉 JavaScript 中的方法是怎样被链式调用的。后续第 2 章会介绍 D3 的专用术语。

2 数组与对象操作

D3 的核心在于数据,而数据通常是由 JavaScript 对象构造的。了解这些对象的构造、访问方法、以及操作其包含的数据,对于构建可视化效果大有帮助。

先来谈谈简单数组——它是一组元素的列表形式。在与数据相关的项目中,数组通常是由数字或字符串构成的有序列表:

const arrayOfNumbers = [17, 82, 9, 500, 40];
const arrayOfStrings = ["blue", "red", "yellow", "orange"];

数组中的每个元素都有一个数值位置,称为 索引,第一个元素的索引为 0(我们说 JavaScript 数组是从零开始索引的(zero-indexed))。

arrayOfNumbers[0]   // => 17
arrayOfStrings[2]   // => "yellow"

数组有一个 length 属性(property)。对于非稀疏数组(nonsparse arrays),该属性指定了数组包含的元素个数。由于数组的索引从零开始,因此数组中最后一个元素的索引值与数组长度减 1 相对应:

arrayOfNumbers.length;                      // => 5
arrayOfStrings[arrayOfStrings.length - 1]   // => "orange"

数组还可以使用 includes() 方法来确定该数组是否包含特定的值。如果数组中的一个元素与作为参数传递的值完全对应,则返回 true ;否则返回 false

arrayOfNumbers.includes(9)         // => true
arrayOfStrings.includes("pink")    // => false
arrayOfStrings.includes("ellow")   // => false

然而,大多数数据集都不是简单的数字或字符串列表,每个数据点通常都由多个属性(properties)组成。试想有一个如下表 1.1 所示的记录某虚拟机构雇员信息的数据库。该表包含四列:每名员工的 id、姓名(name)和职位(position),以及该员工是否使用 D3(works_with_d3)。

表 1.1 雇员及其职位的小型数据集

idnamepositionworks_with_d3
1ZoeData analyst(数据分析师)False
2JamesFrontend developer(前端开发)True
3AliceFullstack developer(全栈开发)True
4HubertDesigner(设计师)False

数据集中的每一行(或称数据点,即 data point)都可以用 JavaScript 对象来描述,如下面的 row1 所示:

const row1 = {
               id:"1",
               name:"Zoe",
               position:"Data analyst",
               works_with_d3:false
             };

也可以用句点符(dot notation)轻松访问对象中每个属性(property)的值:

row1.name            // => "Zoe"
row1.works_with_d3   // => false

此外,还可以使用中括号访问这些值。如果属性名包含空格等特殊字符,或者先前将属性名保存在常量或变量中,则使用中括号符访问属性值就非常方便:

row1["position"]                      // => "Data analyst"
 
const myProperty = "works_with_d3";
row1[myProperty]                      // => false

现实生活中,数据集通常格式化为对象数组。例如,用 D3 加载表 1.1 中的数据集(后续第 3 章会介绍)并保存到常量 data 中,将得到如下所示的对象数组:

const data = [
 {id:"1", name:"Zoe", position:"Data analyst", works_with_d3:false},
 {id:"2", name:"James", position:"Frontend developer", works_with_d3:true},
 {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true},
 {id:"4", name:"Hubert", position:"Designer", works_with_d3:false}
];

我们可以通过循环(loop)来遍历 data 数组中的每个元素(或数据)。更具体地说,就是使用 JavaScript 的 forEach 循环,代码写起来既方便,访问数据又轻松。遍历数据集的一个常见用例是数据整理(data wrangling)。需要加载外部 CSV 文件时,数字通常会被格式化为字符串形式。下面以 data 数组为例,尝试将 id 属性的值从字符串转换为数字格式。

如下代码所示,数组迭代器 d 可以遍历数组中的每条数据。使用句点符以及 + 运算符,可将各 id 转换为数字:

data.forEach(d => {
  d.id = +d.id;
});

JavaScript 提供了多种数组迭代器方法,以帮助开发者与数据交互,甚至在需要时重塑数据。比如,要将数据集中的每位员工定位到某个可视化项目中,假如创建一个只包含员工姓名的简单数组就行,则可以调用数组的 map() 方法:

data.map(d => d.name);   // => ["Zoe", "James", "Alice", "Hubert"]

同理,如果只想筛选出正在使用 D3 的员工,则可以改用 filter() 方法:

data.filter(d => d.works_with_d3);

/* => [
   {id:2, name:"James", position:"Frontend developer", works_with_d3:true},
   {id:4, name:"Hubert", position:"Designer", works_with_d3:true}
  ]; */

最后,还可以使用 find() 方法找出 id3 的员工。注意,find() 方法在找到要目标值后就会停止迭代,因此只能在搜索一个唯一的数据点时使用该方法。

data.find(d => d.id === 3);

// => {id:"3", name:"Alice", position:"Fullstack developer", works_with_d3:true}

本节讨论的数组方法虽然远远不能涵盖 JavaScript 提供的所有数组和对象操作相关的知识点,但它们也是您在处理数据时会经常用到的方法。当需要通过其他方式访问或操作数据时,求助 MDN 的官方在线文档 MDN Web Docs 是个不错的选择,里面有大量可靠的示例供您参考。

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安冬的码畜日常

您的鼓励是我持续优质内容的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值