开始写作本文的时候,我已经顺利地度过了四个月的前端实习生活,其间经手的项目勉强称得上稳定和健壮。但是刚刚学习前端的那些日子,可没现在这么自信,回想起来仍然颇多感慨。

《JavaScript 典型应用与最佳实践》是我学习 JavaScript 的启蒙书籍,初版于 2008 年,面色古老而又心思缜密。其中的原始值和引用值一节,让我对 JavaScript 中数据类型的理解豁然开朗。如果从写作的角度看这本书,它的亮点在于以一种结构化的思维由浅入深地引导读者,主干和分支条缕清晰。有趣的是,在我用谷歌为结构化思维(Structed Thinking)查找一些理论依据的时候,链接到了一本更古老的书籍《金字塔原理:思考、写作和解决问题的逻辑》,书中介绍的正是作者自 1963 年以来反复实践的一套结构化思维,具体的细节等我读完后再聊吧。

最近项目中使用到的技术都比较激进,比如 ES6 / React / Flux 等等。在 ES6 的代码中你能看到诸多其他语言的影子,比如 Python,所以上一周我决定重新回顾一下 Python 中的要点,选用的参考资料是由 Wesley J. Chun 编写的《Python 核心编程(第二版)》。

当读到 4.8 节《标准类型的分类》时,我可以确定,这又是一次结构化思维在知识传播过程中华丽的表演。在高级程序设计语言中,数据类型是必不可少的一部分。理解一门语言的数据类型,也是最基础的一步。虽然下面的示例交错使用 Python 和 JavaScript,但其中所叙述的本质是具有广泛性的。

Python

Python 中每个对象都有三个特性:id, type 和 value。这些特性在对象初始化时创建。Python 内建了多种数据类型,基本类型包括:

  • integer, long integer, float, complex number
  • string
  • list
  • tuple
  • dictionary

对数据类型分类,可以让我们更清晰地理解类型之间的关系以及工作原理。首先,使用存储模型进行分类。存储类型的衡量标准就是看对象能保存多少个对象。对于只能存储单个基本类型的对象,我们称之为原子或标量存储;那些可以存储多个基本类型的对象,称之为容器存储。

分类 Python 类型
标量 / 原子存储 数值、字符串
容器类型 列表、元素、字典

然后,使用更新模型进行分类,更新模型的分类依据是看对象是否允许多次赋值。

分类 Python 类型
可变类型 列表、字典
不可变类型 数值、字符串、元组

最后,使用访问模型进行分类,访问模型的分类依据是看访问方式:直接存取、顺序存取以及映射存取。直接存储通过地址直接访问内存获取数据,数值属于这一类型;顺序存取通过对象的索引从 0 顺序访问内部元素,字符串、列表和元组都属于这一类型;映射存储与顺序存取类型,不同点在于它使用哈希键来访问数据,字典属于这一类型。

分类 Python 类型
直接访问 数值
顺序访问 字符串、列表、元组
映射访问 字典

文中使用这么多的模型对数据类型分类,将基本类型和扩展类型进行区别,描述了各种类型的具体特征。简而言之,文中调侃了一句话:“一个优秀的工匠应该知道自己的工具箱有什么宝贝”。

JavaScript

ECMAScript 规范中定义了变量的两种类型:原始值类型和引用值类型。用以区分的依据就是它们在内存中的存储类型:值存储和址存储。

为什么要分值和地址两种方式?一般而言,值存储的变量(原始值类型)都具有占据空间小、大小固定的特点。只有 string 是个特例,虽然它不具备大小固定的要求,但它是不可变的,是稳定而又会被频繁调用的。在其他语言中,string 大多是可变的。

址存储的类型,具有占据空间大、大小不固定的特点,因此如果使用值存储的方式放在内存中,将会影响程序运行的性能。