javaScript的七种数据类型大全

        众所周知javaScript是一门“极像java的语言”,既然与java那么像,甚至可以说就是套用了java的语法,我在上篇文章《javaScript的基本语法大全》中也列出了很多例子,许多语法和java的一模一样。然而,比较java主要作用于后台开发,而javaScript主要作用于前端开发,其应用场景不一致,所面临的问题也不一样,这也就导致了javaScript的发展历史中又衍生出了许多属于自己的个性。

        目前JavaScript 的数据类型,共有七种。分别是:

  • 数值(number):整数和小数。
  • 字符串(string):文本(比如Hello World”)。
  • 布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)。
  • null:表示空值。
  • undefined:表示“未定义”或不存在,常用于区分“未申明”的变量;
  • 对象(object):各种值组成的集合,包括了数组等复合型数据集合。
  • Symbol :该数据类型是ES6 新增的。

判断数据类型

       虽然javaScript定义了这7种数据类型,但是申明时都是统一用var表示的,所以无法区分变量具体是什么类型的。而实际开发中有时候却需要判断数据类型,所以javaScript也提供了判断数据类型的方法。以下是几种判断方法

  • typeof 运算符 ,返回一个值的数据类型。
  • instanceof 运算符,返回一个值的数据类型,并且可以区分数组和普通对象。
  • Object.prototype.toString方法。

a.typeof 运算符

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
typeof undefined // "undefined"


function f() {}
typeof f // "function"

上面可以看出typeof运算符不仅可以判定基本数据类型,还可以判定函数。利用这写特性,可用于判定一个值是否是有效值,从而避免报错。

// 错误的写法
if (v) {
  // ...
}
// 报错:ReferenceError: v is not defined

// 正确的写法
if (typeof v === "undefined") {
  // ...
}

b.instanceof 运算符

      instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例。

var v = new Vehicle();
v instanceof Vehicle // true

instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上。因此,下面两种写法是等价的。

v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)

由于instanceof检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回true

var d = new Date();
d instanceof Date // true
d instanceof Object // true

上面代码中,d同时是DateObject的实例,因此对这两个构造函数都返回true。你可以这么理解Date也是一个对象,也属性Object类型。

instanceof的原理是检查右边构造函数的prototype属性,是否在左边对象的原型链上。有一种特殊情况,就是左边对象的原型链上,只有null对象。这时,instanceof判断会失真。

var obj = Object.create(null);
typeof obj // "object"
Object.create(null) instanceof Object // false

上面代码中,Object.create(null)返回一个新对象obj,它的原型是nullObject.create的详细介绍见本平台后续文章)。右边的构造函数Objectprototype属性,不在左边的原型链上,因此instanceof就认为obj不是Object的实例。但是,只要一个对象的原型不是nullinstanceof运算符的判断就不会失真。

因为instanceof 是通过构造函数来比较的,因此它可以区分数组和对象。

var o = {};
var a = [];

o instanceof Array // false
a instanceof Array // true

注意,instanceof运算符只能用于对象,不适用原始类型的值。

var s = 'hello';
s instanceof String // false

c.Object.prototype.toString方法。

       Object.prototype.toString能够打印出变量或者数据类型,并且能返回这个类型。

var obj = {};

obj.toString() //"[object Object]"
Object.prototype.toString(obj)  //"[object Object]"


var test = Object.prototype.toString(obj) 
test // "[object Object]"

 

下面我为大家逐一介绍以上的七种数据类型:

1.数值

        JavaScript 内部,所有数字都是以64位浮点数形式储存,所以JavaScript 语言的底层是没有整数的。需要注意的是某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,先转成32位整数,然后再参与运算。

由于浮点数存在不精确的问题,所以在javaScript小数的比较和运算时会存在误差,这点需要特别小心留意。

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

1.1数值精度问题:

       根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-9007199254740992到9007199254740992,都可以精确表示。超过这个范围就无法精确表示了。

Math.pow(2, 53)     // 9007199254740992
 
Math.pow(2, 53) + 1 // 9007199254740992

Math.pow(2, 53) + 2 // 9007199254740994

Math.pow(2, 53) + 3 // 9007199254740996

Math.pow(2, 53) + 4 // 9007199254740996

上面代码中,大于2的53次方以后,整数运算的结果开始出现错误。所以,大于2的53次方的数值,都无法保持精度。由于2的53次方是一个16位的十进制数值,所以简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。

Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将无法保存
9007199254740992111
// 9007199254740992000

上面示例表明,大于2的53次方以后,多出来的有效数字(最后三位的111)都会无法保存,变成0。

1.2数值范围 

        根据国际标准所定制的二进制规则, JavaScript 能够表示的数值范围为正负2的1024次方(开区间),超出这个范围的数无法表示。如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity

Math.pow(2, 1024) // Infinity

如果一个数小于等于2的-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0。

Math.pow(2, -1075) // 0

JavaScript 提供Number对象的MAX_VALUEMIN_VALUE属性,返回可以表示的具体的最大值和最小值。

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324(小数点后的0>5个, 就自动转为科学计数法)

1.3数据的全局方法:

  • parseInt():将字符串转为整数,如果参数不是字符串会先转成字符串再转为整数,parseInt方法还可以接受第二个参数(2到 36之间),表示被解析的值的进制,返回该值对应的十进制数。parseInt的第二个参数默认为10,即默认是十进制转十进制。
  • parseFloat():将一个字符串转为浮点数。
  • isNaN():判断一个值是否为NaN
  • isFinite():返回一个布尔值,表示某个值是否为正常的数值。

 

2.字符串

       字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始),也可以使用length属性。

var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"

// 直接对字符串使用方括号运算符
'hello'[1] // "e"

s.length //5

s.length = 3;
s.length //5

从上面可以看出,字符串取坐标跟数组一样,也能使用length得出长度,但是不可以改变它的长度值。通过这个特征便可以使用for循环遍历字符串了。

 

3.null 空值

         null表示不存在,与java中的null如出一辙,本身就是引用了java中null的规则。

var a = null;
a //null

根据C语言传传统规则,null在参与数字运算时可以转为0。

Number(null) // 0
5 + null // 5

 我们知道在java中null可能是情况是调用方法传参数的时候传的值是空值,也可能是申明了值,却并未赋值,因此null可以表示为一个空对象。但还有一种情况,就是并非申明的值,在java中对未申明的值编译的时候就会报错,但如果是创建了一个空对象就不会报错。如何去区分这两种不同的情况呢,况且null还可以转成0,可以回干扰误导计算。这时候就需要用到“undefined”了。

 

4.undefined 未定义

   undefined和null一样都可以表示“没有”,含义非常相似。将一个变量赋值为undefinednull,老实说,语法效果几乎没区别,几乎相等。

var a = undefined;
// 或者
var a = null;

null == undefined //true
null === undefined //false

从上面可以看出null与undefined相等,但用===比较又不相等,说明他们值相等,但类型不一致。

        既然含义与用法都差不多,为什么要同时设置两个这样的数据类型,这不是无端增加复杂度,令初学者困扰吗?说到这里这就与历史原因有关了。

      1995年 JavaScript 诞生时,最初像 Java 一样,只设置了null表示"无"。但是,JavaScript 的设计者 Brendan Eich,觉得这样做还不够。其次,那时的 JavaScript 不包括错误处理机制,Brendan Eich 觉得,如果null自动转为0,很不容易发现错误。

因此,他又设计了一个undefined。区别是这样的:null是一个表示“空”的对象,转为数值时为0undefined是一个表示"此处无定义"的原始值,转为数值时为NaN

Number(undefined) // NaN
5 + undefined // NaN

用法和含义

对于nullundefined,大致可以像下面这样理解。

null表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入null,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误。

undefined表示“未定义”,下面是返回undefined的典型场景。

// 变量声明了,但没有赋值
var i;
i // undefined

// 调用函数时,应该提供的参数没有提供,该参数等于 undefined
function f(x) {
  return x;
}
f() // undefined

// 对象没有赋值的属性
var  o = new Object();
o.p // undefined

// 函数没有返回值时,默认返回 undefined
function f() {}
f() // undefined

 

5.布尔值 Boolean

         布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

下列运算符会返回布尔值:

  • 前置逻辑运算符: ! (Not)
  • 相等运算符:===!====!=
  • 比较运算符:>>=<<=
  • 并且,或运算:&&,|| (这两个运算符一般搭配前面的运算符一起参与运算)

如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字符串)

在实际开发中可利用值转布尔值的规则,结合三元运算符一起使用。例如:当值为空串时显示空,不为空串时再判断类型,取对象中的属性。

var x = {};

function add(x){
    return !x:''? x.p!='' : x.p ? x ;
}

上面的代码中,函数add接收一个参数x,如果x是'',那么!x就会转成 true,就会显示'',如果不是空串,就会继续运行后面的三元运算符。

注意,空数组([])和空对象({})对应的布尔值,都是true

if ([]) {
  console.log('true');
}
// true

if ({}) {
  console.log('true');
}
// true

       

6.对象Object

      对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。对象是一种无序的复合数据集合,通过“键值对”(key-value)的来表达具体的数据类型。

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

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

       注意:对象的键名只能是字符串,默认就是字符串,所以申明的时候可以省略引号(如果键名保护不符合规范的字符还是需要加引号的),而值可以是任意的数据类型,甚至是函数都可以。

      对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。

var obj = {
  p: function (x) {
    return 2 * x;
  }
};

obj.p(1) // 2

6.1属性的操作

6.1.1.读取:

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

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

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

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

 6.1.2.属性的赋值:

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

var obj = {};

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

上面代码中,分别使用点运算符和方括号运算符,对属性赋值。

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

6.1.3.属性的查询与遍历

查看一个对象本身的所有属性,可以使用Object.keys方法,或者for in循环

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

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

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

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

for (var i in obj) {
  console.log('键名:', i);
  console.log('键值:', obj[i]);
}
// 键名: a
// 键值: 1
// 键名: b
// 键值: 2
// 键名: c
// 键值: 3

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

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
  • 它不仅遍历对象自身的属性,还遍历继承的属性,但如果改属性设置为了不可遍历,就不会被遍历到。

举例来说,对象都继承了toString属性,但是for...in循环不会遍历到这个属性。

var obj = {};

// toString 属性是存在的
obj.toString // toString() { [native code] }

for (var p in obj) {
  console.log(p);
} // 没有任何输出

如果继承的属性是可遍历的,那么就会被for...in循环遍历到。但是,一般情况下,都是只想遍历对象自身的属性,所以使用for...in的时候,应该结合使用hasOwnProperty方法,在循环内部判断一下,某个属性是否为对象自身的属性。

var person = { name: '老张' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name

6.1.4.属性的删除:delete 命令

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

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

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

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

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

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

上面代码中,对象obj并没有p属性,但是delete命令照样返回true。因此,不能根据delete命令的结果,认定某个属性是存在的。

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

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

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

上面代码之中,对象objp属性是不能删除的,所以delete命令返回false

另外,需要注意的是,delete命令只能删除对象本身的属性,无法删除继承的属性。

var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }

上面代码中,toString是对象obj继承的属性,虽然delete命令返回true,但该属性并没有被删除,依然存在。这个例子还说明,即使delete返回true,该属性依然可能读取到值。

6.1.5.属性是否存在:in 运算符

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

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

in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。就像上面代码中,对象obj本身并没有toString属性,但是in运算符会返回true,因为这个属性是继承的。

这时,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

6.2数组

       数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。数组可以和普通对象一样除了在定义时赋值,也可以和对象一样先定义后赋值。同样的任何类型的数据,都可以放入数组。普通对象所具有的特性,数组都具备。

数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。

var arr = ['a', 'b', 'c'];

上面代码中的abc就构成一个数组,两端的方括号是数组的标志。a是0号位置,b是1号位置,c是2号位置。

var arr = [
  {a: 1},
  [1, 2, 3],
  function() {return true;}
];

arr[0] // Object {a: 1}
arr[1] // [1, 2, 3]
arr[2] // function (){return true;}

上面数组arr的3个成员依次是对象、数组、函数。

如果数组的元素还是数组,就形成了多维数组。

var a = [[1, 2], [3, 4]];
a[0][1] // 2
a[1][1] // 4

数组同样也有length属性,也可以使用in判断里面是否存在值,几乎普通对象有的方法都可以作用于数组,这里就不一一举例了。

 

7.Symbol

7.1.定义:ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。

7.2.Symbol值通过Symbol函数生成。对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

7.3.注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

7.4.Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。Symbol值作为对象属性名时,不能用点运算符。在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。

7.5、Symbol实例:消除魔术字符串

     魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,该由含义清晰的变量代替。

 

function getArea(shape, options) {
  var area = 0;

  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

 

上面代码中,字符串“Triangle”就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

常用的消除魔术字符串的方法,就是把它写成一个变量。

 

var shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  var area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

 

上面代码中,我们把“Triangle”写成shapeType对象的triangle属性,这样就消除了强耦合。

如果仔细分析,可以发现shapeType.triangle等于哪个值并不重要,只要确保不会跟其他shapeType属性的值冲突即可。因此,这里就很适合改用Symbol值。

const shapeType = {
  triangle: Symbol()
};

上面代码中,除了将shapeType.triangle的值设为一个Symbol,其他地方都不用修改。

摘自http://es6.ruanyifeng.com/#docs/symbol

7.6、有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。

   Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

另一个新的API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

7.7、有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。

Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值