# 原始数据类型

原始数据类型有六种:Undefined、Null、Boolean、Number、String 和 Symbol。

# Undefined

这个类型值有一个值 undefined 。

使用 var 或 let 声明了变量但没有初始化时,就相当于给变量赋予了undefined值。

let a
var b
console.log(a) // undefined
console.log(b) // undefined

注意:使用 const 声明变量如果没有赋初始值会报错。

const a // Uncaught SyntaxError: Missing initializer in const declaration

对未声明或未初始化的变量调用 typeof 的时候,返回都是 undefined。

var a
console.log(typeof a) // undefined
console.log(typeof b) // undefined

console.log(a) // undefined
console.log(b) // Uncaught ReferenceError: b is not defined

# Null

这个类型只有一个值 null 。

用等于操作符(==)比较null和undefined始终返回true。

null == undefined // true

用 === 操作符比较 null 和 undefined 返回false。

null === undefined // false

# Boolean

这个类型有两个值 true 和 false 。

调用 Boolean 函数可以将其他值转换为布尔类型。具体转换规则如下:

数据类型 转换为true的值 转换为false的值
Boolean true false
String 非空字符串 ""(空字符串)
Number 非零数值(包括无穷值) 0、NaN(参见后面的相关内容)
Object 任意对象 null
Undefined N/A(不存在) undefined

# Number

十进制这里我们不做特殊说明,这里我们简单列举下八进制和十六进制在 JS 中的表示方法。

let octalNum1 = 070;  // 八进制的56
let octalNum2 = 079;  // 无效的八进制值,当成79处理
let octalNum3 = 08;   // 无效的八进制值,当成8处理

TIP

ECMAScript 2015或ES6中的八进制值通过前缀0o来表示;严格模式下,前缀0会被视为语法错误,如果要表示八进制值,应该使用前缀0o。——译者注

let hexNum1 = 0xA;   // 十六进制10
let hexNum2 = 0x1f;  // 十六进制31

0.1加0.2得到的不是0.3,而是0.300 000 000 000 000 04。

TIP

之所以存在这种舍入错误,是因为使用了IEEE 754数值,这种错误并非ECMAScript所独有。其他使用相同格式的语言也有这个问题。

ECMAScript可以表示的最小数值保存在Number.MIN_VALUE中,这个值在多数浏览器中是5e-324;可以表示的最大数值保存在Number.MAX_VALUE中,这个值在多数浏览器中是1.797 693 134 862 315 7e+308。

任何无法表示的负数以-Infinity(负无穷大)表示,任何无法表示的正数以Infinity(正无穷大)表示。

isFinite()函数可以用来确定某个值是不是有限大。

console.log(0/0);    // NaN
console.log(-0/+0);  // NaN

// 分子是非0值,分母是有符号0或无符号0,则会返回Infinity或-Infinity
console.log(5/0);   // Infinity
console.log(5/-0);  // -Infinity

NaN不等于包括NaN在内的任何值。

console.log(NaN == NaN); // false

isNaN() 可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给isNaN()后,该函数会尝试把它转换为数值。

console.log(isNaN(NaN));     // true
console.log(isNaN(10));      // false,10是数值
console.log(isNaN("10"));    // false,可以转换为数值10
console.log(isNaN("blue"));  // true,不可以转换为数值
console.log(isNaN(true));    // false,可以转换为数值1

# String

ECMAScript中的字符串是不可变的,要修改的话,只能销毁重新创建。

我们可以使用 toString() 方法和 String() 构造函数,将其他类型转换为字符串类型。注意!null 和 undefined 没有 toString() 方法,不能调用。toString() 可以传入一个底数参数,来指定以什么为底数输出字符。

let num = 10;
console.log(num.toString());     // "10"
console.log(num.toString(2));    // "1010"
console.log(num.toString(8));    // "12"
console.log(num.toString(10));   // "10"
console.log(num.toString(16));   // "a"

String()函数遵循如下规则。

  • 如果值有toString()方法,则调用该方法(不传参数)并返回结果。
  • 如果值是null,返回"null"。
  • 如果值是undefined,返回"undefined"。

# Symbol

Symbol(符号)是ECMAScript 6新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。

let sym = Symbol();
console.log(typeof sym); // symbol

这块使用比较少,有需要的可以去 MDN 上查询。

# 对象类型

ECMAScript中的对象其实就是一组数据和功能的集合。

# 创建对象的方式

第一种,使用字面量的方式

var p = {name: 'f'}

第二种,使用构造函数

var p1 = new Object({name: 'f'})

function P() {
  this.name = 'f'
}
var p2 = new P()

第三种,使用Object.create,Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

var p1 = {name: 'f'}
var p2 = Object.create(p1)
console.log(p2.__proto__ === p1) // true

上面列举了三种创建对象的方法。这里重点介绍下通过 new 这种方式创建对象。

const num = new Number(1) // Number {1}
const str = new String('hello') // String {"hello"}
const bool = new Boolean(true) // Boolean {true}

// 构造函数 构造函数也是函数,一个函数通过 new 调用,就可以被称为构造函数。
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const person = new Person('f', 18) // Person {name: "f", age: 18}

# new关键字

上面展示了通过 new 方法创建的几个实例对象,你是不是会好奇 new 是干嘛的?它做了什么?下面我们来探究下,new 实际做了如下操作:

  • 一个新对象被创建,它继承自构造函数的原型对象。
  • 构造函数被执行,并传入相应的参数。同时this会被指向这个新对象。
  • 如果构造函数返回了一个对象,那么这个对象会取代new出来的结果。如果构造函数没有返回对象,那么new出来的结果为第一步创建的对象。

模拟new运算符

function new1(func) {
  // 创建一个对象,绑定原型
  const o = Object.create(func.prototype)
  // 绑定this
  const res = func.call(this)
  // 如果构造函数返回了一个对象,那么这个对象会取代new出来的结果。如果构造函数没有返回对象,那么new出来的结果为第一步创建的对象。
  return typeof res === 'object' ? res : o 
}

验证我们写的 new 方法

function new1(func) {
  const o = Object.create(func.prototype)
  const res = func.call(this)
  return typeof res === 'object' ? res : o 
}
function Fn() {}
const fn = new1(Fn)
console.log(fn instanceof Fn) // true
console.log(fn instanceof Object) // true
console.log(fn.__proto__.constructor === Fn) // true

这里我们列举几个使用对象时有可能会使用到的方法和属性:

  • constructor:用于创建当前对象的函数
  • hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性
  • toString():返回对象的字符串表示。
  • valueOf():返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同。

# typeof

我们可以通过 typeof 来检测数据类型。


 





 

typeof undefined // undefined
typeof null // object
typeof 1 // number
typeof true // boolean
typeof 'hello' // string
typeof Symbol() // symbol
typeof {} // object
typeof function(){} // function

typeof null返回的是"object"。这是因为特殊值null被认为是一个对空对象的引用。

函数在 ECMAScript 中被认为是对象,并不代表一种数据类型。可是,函数也有自己特殊的属性。为此,就有必要通过typeof操作符来区分函数和其他对象。