类型系统之JS数据类型

196 阅读6分钟

前言

在程序设计的类型系统中,数据类型(英语:Data type),又称数据型态、数据型别,是用来约束数据的解释。

数据类型描述了数值的表示法、解释和结构,并以算法操作,或是对象在存储器中的存储区,或者其它存储设备。

高级语言中设计数据类型可以具有如下优点。

  1. 方便管理计算机的内存空间,提高运行时效率;
  2. 帮助构建抽象数据类型和模块化;
  3. 构建语言的语义模型。

数据类型背后隐藏的是编译器或者解释器对数据处理方式的定义

当我们真正掌握数据类型后,我们清楚的知道某种数据类型可以进行哪些操作,这些操作的规则又是什么,从而提高我们的编码效率。

JS 里的数据类型

JavaScript 中的变量分为基本类型和引用类型。一共有八种,分别是:

  • Undefined
  • Null
  • Boolean
  • String
  • Number
  • Symbol
  • BigInt (新增)
  • Object (引用数据类型)

前七种是基本数据类型,最后一种是引用数据类型

undefined

JavaScript 中下述情况下结果为 undefined

  1. 声明一个变量未赋值;
  2. 访问某个对象不存在的属性;
  3. 没有返回值的函数或者 return; 的函数
// 1.
const num;
console.log(num); // undefined

// 2.
const book = {};

console.log(book.title); // undefined

// 3.
const foo = () => {};

console.log(foo()); // undefined

undefined 值没有任何属性、没有任何方法。访问 undefined 值的任何属性和方法,都会报错。

const val = undefined;

console.log(val.title); // Cannot read property 'title' of undefined
console.log(val.toString()); // Cannot read property 'toString' of undefined

undefined 一般认为是属于系统层面的、报错层面的信息缺失

null

null 表示的是一个明确的已经知道的值,是一个空对象。null 代表有存储信息的容器 (比如之前被赋过值的变量),但该容器中的内容为空。

const obj = null;

null 值没有任何属性、没有任何方法。访问 null 值的任何属性和方法,都会报错。

const val = null;

console.log(val.title); // TypeError: Cannot read property 'title' of null
console.log(val.toString()); // Cannot read property 'toString' of null

null 一般认为是属于编程层面的、逻辑操作层面的信息值为空

String、Number 和 Boolean

在 JS 中,String、Number 和 Boolean 分别是 string、number boolean 值对应的引用类型。

包装类型,是一个专门封装原始类型的值,并提供对原始类型的值执行操作的 API 对象。

普通的内置对象与基本包装类型的主要区别就是对象的生命期,使用 new 操作符创建的引用类型的实例, 在执行流离开当前作用域之前都一直保存在内存中,而自动创建的基本包装类型的对象,则只是存在于一 行代码的执行瞬间,然后立即被立即销毁。这意味着我们不能再运行时为基本包装类型值添加属性和方法。

基本包装对象类型在运行时不能添加属性和方法,以 String 包装对象举例子说明如下:

var str = "some text";
str.color = "red";
console.log(str.color); //undefined

普通对象类型在运行时可以添加属性和方法,如下:

const book = {};
book.title = "JavaScript 高级程序设计";
book.showTitle = function () {
  return this.title;
};
console.log(book.title); //'JavaScript 高级程序设计'
console.log(book.showTitle()); //'JavaScript 高级程序设计'

string 类型的值有 3 类,分别是单引号、双引号和模板字符串。

console.log("foo"); // 'foo';
console.log("foo"); // 'foo';

es6 新增了模板字符串,用反引号表示。模板字符串使用反引号 (``) 来代替普通字符串中的 用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符。

const name = "qinghuanI";

console.log(`Hi ${name}`); // 'Hi qinghuanI'

number 类型的值有 5 类, 分别是整型、浮点型、NaN、Infinity、 -Infinity。

console.log(2); // 2 整型
console.log(2.33); // 2.33 浮点型
console.log(Number("aaa")); // NaN 不是一个数
console.log(1 / 0); // Infinity
console.log(1 / -0); // -Infinity

boolean 类型的值有两个, 分别是 truefalse

const bool = true || false;

Symbol

Symbol 是一种基本数据类型。每个从 Symbol() 返回的 symbol 值都是唯一的。

const s1 = Symbol();
const s2 = Symbol();

console.log(s1 === s2); // false

一个 symbol 值能作为对象属性的标识符。来看下面的例子。

const name = Symbol();
const person = { [name]: "qinghuanI" };

console.log(person[name]); // 'qinghuanI'

BigInt

BigInt 是新增的基本数据类型。它提供了一种方法来表示大于 25³ - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。

console.log(typeof BigInt(1)); // 'bigint'

Object

JS 中最重要同时也是最复杂的类型是 Object 类型。

JavaScript 中的所有对象都来自 Object,所有对象从 Object.prototype 继承方法和属性。 我们常用的 Array、Set、Map、Date 等等内置对象均继承 Object 基对象。

console.log(Array instanceof Object); // true
console.log(Set instanceof Object); // true
console.log(Map instanceof Object); // true
console.log(Date instanceof Object); // true

但是,Array、Set、Map、Date 等等内置对象会重写 Object 的 constructor 属性和 toString() 方法。看看 Array 的例子。

console.log(Object.toString()); // function Object() { native code }
console.log(Array.toString()); // function Array() { native code }

console.log({}.toString()); // "[object Object]"
console.log([].toString()); // ""

如果想了解对象更底层的实现,请阅读《从 Chrome 源码看 JS Object 的实现》

Object 类型是一个包含键/值对的集合。ECMAScript 提供了两种创建创建对象的方式:构造函数创建和字面量创建。 构造函数创建是指使用 new Object()的方式创建,而字面量创建是指使用{}创建。

// 构造函数创建
const obj = new Object();

// 对象字面量 推荐使用该方式
const obj = {};

使用对象字面量创建对象比使用构造函数创建对象更简洁,所以推荐使用对象字面量方式创建对象。

ECMAScript 还为对象提供了更精细的控制。对象的属性分为数据属性访问器属性, 就是属性的属性,用来描述属性的行为特性。

数据属性

数据属性包含一个数据值的位置。在这个位置可以读取和写入值。

  1. [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性, 或者能否把属性修改为访问器属性。默认值为 true
  2. [[Enumerable]]:表示能否通过for-in 循环返回属性值。默认值为 true
  3. [[Writable]]:表示能否修改属性的值。默认为 true
  4. [[Value]]:包含这个属性的属性值。读取属性值的时候,从这个位置读;写入属性值的时候,把 新值保存在这个位置。默认值为 undefined

我们通过一个例子,将对象的某个属性设置为不可以修改。

const book = {};

Object.defineProperty(book, "title", {
  writable: false,
  value: "JavaScript 高级程序设计",
});

book.title = "JavaScript 权威指南";

console.log(book.title); // 'JavaScript 高级程序设计'

访问器属性

  1. [[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性, 或者能否把属性修改为访问器属性。默认值为 true
  2. [[Enumerable]]:表示能否通过for-in 循环返回属性值。默认值为 true
  3. [[Get]]:在读取属性时调用的函数。默认为 undefined
  4. [[Set]]:在写入属性时调用的函数。默认值为 undefined
var dog = {
  _age: 2,
  weight: 10,
};

Object.defineProperty(dog, "age", {
  get: function () {
    return this._age;
  },
  set: function (newVal) {
    this._age = newVal;
    this.weight += 2;
  },
});

dog.age = 3;

console.log(dog); // {_age: 3, weight: 12}

我们声明一个对象时,默认为访问器属性

参考链接