JS数据类型

229 阅读9分钟

概括:

JavaScript的数据类型共有6种,ES6又增加第7中(symbol)。

  • 数值(Number):整数和小数。
  • 字符串(String):文本。
  • 布尔值(boolean):只有两个值。true(真)、false(假)。
  • Undefined:未定义或不存在。
  • Null:表示空值
  • 对象(Object):各种值组成的集合。

对象是最复杂的数据类型,又可细分为三种:

  • 狭义的对象(object)
  • 数组(array)
  • 函数(function)

number,string,boolean这三个为原始类型。
object为合成类型
原始类型:最基本的数据类型,不能再细分。
合成类型:多个原始类型的值的合成。


1.number

1. 概括:JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数。JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数)。容易造成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,然后再进行运算

1===1.0        //true
0.1+0.2=0.3    //false
0.3/0.1        //2.9999999999999996
(0.3-0.2)===(0.2-0.1)   //false

2. 数值的表示法

JavaScript 的数值有多种表示方法,可以用字面形式直接表示,比如35(十进制)和0xFF(十六进制)。数值也可以采用科学计数法表示。

123e3       //    123000
123e-3      //    0.123

科学计数法允许字母e或E的后面,跟着一个整数,表示这个数值的指数部分。


以下两种情况,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。

小数点前的数字多于21位。

1234567890123456789012    //  1.2345678901234568e+21
123456789012345678901     //   123456789012345680000

小数点后的零多于5个。

0.0000003       //   3e-7
0.000003        //   0.000003

3.数值的进制

JavaScript 对整数提供四种进制的表示方法:十进制、十六进制、八进制、二进制。

  • 十进制:没有前导0的数值。
  • 二进制:有前缀0b或0B的数值。
  • 八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
  • 十六进制:有前缀0x或0X的数值。

默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制。下面是一些例子。

0xff        // 255
0o377       // 255
0b11        // 3

如果八进制、十六进制、二进制的数值里面,出现不属于该进制的数字,就会报错。

0xzz        // 报错
0o88        // 报错
0b22        // 报错

4.特殊数字 NaN

NaN是 JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。

5 - 'x'      // NaN

上面代码运行时,会自动将字符串x转为数值,但是由于x不是数值,所以最后得到结果为NaN,表示它是“非数字”(NaN)。


一些数学函数的运算结果会出现NaN。
Math.acos(2)        // NaN
Math.log(-1)       // NaN
Math.sqrt(-1)      // NaN

0除以0也会得到NaN。

0 / 0     // NaN

需要注意的是,NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number,使用typeof运算符可以看得很清楚。

typeof NaN          // 'number'


NaN不等于任何值,包括它本身。

NaN === NaN // false

数组的indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立。

[NaN].indexOf(NaN)     // -1

NaN在布尔运算时被当作false。

Boolean(NaN) // false

NaN与任何数(包括它自己)的运算,得到的都是NaN。

NaN + 32       // NaN
NaN - 32       // NaN
NaN * 32       // NaN
NaN / 32       // NaN


2. String

概述:字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。

'abc'
"abc"

单引号字符串的内部,可以使用双引号。双引号字符串的内部,可以使用单引号。

'key = "value"'
"It's a long journey"

如果要在单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义。双引号字符串内部使用双引号,也是如此。

'Did she say \'Hello\'?'
"Did she say \"Hello\"?"

字符串默认只能写在一行内,分成多行将会报错。
'a
b
c'
// SyntaxError: Unexpected token ILLEGAL

如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。

var longString = 'Long \
long \
long \
string';

longString
// "Long long long string"

上面代码表示,加了反斜杠以后,原来写在一行的字符串,可以分成多行书写。但是,输出的时候还是单行,效果与写在同一行完全一样。注意,反斜杠的后面必须是换行符,而不能有其他字符(比如空格),否则会报错


连接运算符(+)可以连接多个单行字符串,将长字符串拆成多行书写,输出的时候也是单行。
var longString = 'Long '
  + 'long '
  + 'long '
  + 'string';


2. 转义

反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。 需要用反斜杠转义的特殊字符,主要有下面这些。


    \0 :null(\u0000)
    \b :后退键(\u0008)
    \f :换页符(\u000C)
    \n :换行符(\u000A)
    \r :回车键(\u000D)
    \t :制表符(\u0009)
    \v :垂直制表符(\u000B)
    \' :单引号(\u0027)
    \" :双引号(\u0022)
    \\ :反斜杠(\u005C)


3. boolean

概述:布尔值代表的就两个值,true、false。

  • 两元逻辑运算符:&&(and) ||(or)
  • 前置逻辑运算符: !(not)
  • 相等运算符:===,!==,==,!=
  • 比较运算符:>,>=,<,<=

4. undefined和null

概述:undefined和null都可以表示‘没有’,含义非常相似。语法效果几乎没区别。

  • 变量没赋值(undefined)
  • 有一个对象,现在不能赋值 (null)空对象
  • 有一个非对象,不想赋值(undefined) 空非对象

5. Object

  1. 对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。

什么是对象?简单说,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。

var obj = {
  foo: 'Hello',
  bar: 'World'
};

上面代码中,大括号就定义了一个对象,它被赋值给变量obj,所以变量obj就指向一个对象。该对象内部包含两个键值对(又称为两个“成员”),第一个键值对是foo: 'Hello',其中foo是“键名”(成员的名称),字符串Hello是“键值”(成员的值)。键名与键值之间用冒号分隔。第二个键值对是bar: 'World',bar是键名,World是键值。两个键值对之间用逗号分隔。


2. 键名

概述:对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。上面的代码也可以写成下面这样。

var obj = {
  'foo': 'Hello',
  'bar': 'World'
};


var obj = {
  foo: 'Hello',
  bar: 'World'
};


如果键名是数值,会被自动转为字符串。

var obj = {
  1: 'a',
  3.2: 'b',
  1e2: true,
  1e-2: true,
  .234: true,
  0xFF: true
};

obj
// Object {
//   1: "a",
//   3.2: "b",
//   100: true,
//   0.01: true,
//   0.234: true,
//   255: true
// }

obj['100'] // true

上面代码中,对象obj的所有键名虽然看上去像数值,实际上都被自动转成了字符串。


如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错。

// 报错
var obj = {
  1p: 'Hello World'
};

// 不报错
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};

3.对象的引用

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

  1. 读取属性

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"


请注意,如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理。
var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2

上面代码中,引用对象obj的foo属性时,如果使用点运算符,foo就是字符串;如果使用方括号运算符,但是不使用引号,那么foo就是一个变量,指向字符串bar。


数字键可以不加引号,因为会自动转成字符串。

var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"

注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。

var obj = {
  123: 'hello world'
};

obj.123 // 报错
obj[123] // "hello world"

5.属性的赋值

点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。

var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';

JavaScript 允许属性的“后绑定”,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性。

var obj = { p: 1 };

// 等价于

var obj = {};
obj.p = 1;


  1. 查看所有的属性

查看一个对象本身的所有属性,可以使用 Object.keys 方法。

var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']


  1. delete 命令

delete命令用于删除对象的属性,删除成功后返回true。

var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

上面代码中,delete命令删除对象obj的p属性。删除后,再读取p属性就会返回undefined,而且Object.keys方法的返回值也不再包括该属性。


注意,删除一个不存在的属性,delete不报错,而且返回true。

var obj = {};
delete obj.p // true


只有一种情况,delete命令会返回false,那就是该属性存在,且不得删除。

var obj = Object.defineProperty({}, 'p', {
  value: 123,
  configurable: false
});

obj.p // 123
delete obj.p // false


  1. in 运算符

in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。

var obj = { p: 1 };
'p' in obj // true

in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。

var obj = {};
'toString' in obj // true


  1. for…in 循环

for...in循环用来遍历一个对象的全部属性。

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log(obj[i]);
}
// 1
// 2
// 3

使用for...in循环,提取对象属性名的例子。

var obj = {
  x: 1,
  y: 2
};
var props = [];
var i = 0;

for (var p in obj) {
  props[i++] = p
}

props // ['x', 'y']

for...in循环有两个使用注意点。

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。

  • 它不仅遍历对象自身的属性,还遍历继承的属性。