简介

编程语言会有不同的数据类型,这是因为数据是对不同场景的实体的量化抽象,不同实体之间会有区别,另外,不同类型的数据的应用场景和操作也是不一样的。

例如提示的话术就应该用字符串类型,可以拼接你好,我是${name}。而例如游戏得分应该用数值类型,可以进行加减等运算。

数据类型

JavaScript类型介绍

数据类型分类

ECMSScript有5种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number、String。还有一种复杂的数据类型——Object。

—— 《JavaScript高级程序设计》

根据《JavaScript高级程序设计》中说明,JavaScript有6种数据类型,Undefined、Null、Boolean、Number、String、Object。但实际上typeof null的值是"object",另外typeof function() {}的值是"function"。因此我们认为null并不是一个独立的类型,null是object类型是一个值,而function也是一个独立的类型。

__js数据类型有6种:

  • number
  • string
  • boolean
  • object
  • function
  • undefined

undefined类型的值只有一个,就是undefined。

数值类型有两个特殊的值,NaN(not a number)和Infinity(无穷大)

object类型又可以分为

  • plain object // 普通对象
  • Date // 日期
  • Array // 数组
  • RegExp // 正则

其中number、string、boolean、undefined是值类型,function和object是引用类型。

值类型和引用类型

值类型和引用类型的区别是,值类型赋的变量直接存储数据,引用类型的变量存储数据的引用。

  1. let a = 1;
  2. let b = a;
  3. b = 2;
  4. console.log(a, b); // 1, 2
  5. let c = {attr: 'yes'};
  6. let d = c;
  7. d.attr = 'no';
  8. console.log(c.attr, d.attr); // no no
  1. function test(arg) {
  2. arg = 2;
  3. }
  4. let a = 1;
  5. // 相当于将a的值赋给test中的参数变量,参数改变并不会影响到a
  6. test(a);
  7. console.log(a); // 1
  8. function update(arg) {
  9. arg.attr = 2;
  10. }
  11. let b = {attr: 1};
  12. // 将b的引用赋给update的参数变量,参数变量改变引用指向的数据,也会影响到b
  13. update(b);
  14. console.log(b.attr); // 2

包装类型

基础类型的数据在使用时候,js引擎会先将之包装为对象,语句执行完对象被销毁。这个过程也被称为“装箱拆箱”。例如

const arr = '1,2,3'.split(',');

字符串先包装为String对象,然后对象执行相应方法,语句执行完后,包装对象就被销毁。

再如(1).toString()将返回数据类型的包装对象转换成的字符串。

注意:1.toString()会将”.”解析为小数点,因此会报语法错误

包装类型机制扩展了基本数据类型的能力,方便了日常开发。

因为基础类型也有包装类型转为对象,因此除了Symbol都有构造函数。

  1. "1".constructor // 返回函数 String() { [native code] }
  2. (1).constructor // 返回函数 Number() { [native code] }
  3. false.constructor // 返回函数 Boolean() { [native code] }
  4. [1,2,3].constructor // 返回函数 Array() { [native code] }
  5. {}.constructor // 返回函数 Object() { [native code] }
  6. new Date().constructor // 返回函数 Date() { [native code] }
  7. function () {}.constructor // 返回函数 Function(){ [native code] }

null和undefined的区别

本身都表示“没有”,但null表示引用类型的对象为空,undefined则表示变量未定义。

在相等判断时候,null和undefined是相等的。

但null和undefined在很多方面有区别。

含义不同

null表示对象空指针,undefined表示变量未定义。

类型不同

  1. typeof null // 'object'
  2. typeof undefined // 'undefined'
  3. Number(null) // 0
  4. Number(undefined) // NaN

应用场景不同

null

作为对象原型链的终点。

undefined

定义了变量,没有初始化,默认是undefined。

函数不return,或者return后面没有值,则函数默认返回undefined。

函数参数如果不传,默认是undefined。

类型判断

判断类型的方法

typeof

typeof用来查看字面量或者变量的数据类型

  1. typeof 1 // 'number'
  2. typeof '1' // 'string'
  3. typeof false // 'boolean'
  4. typeof {} // 'object'
  5. typeof [] // 'object'
  6. typeof new Date() // 'object'
  7. typeof (() => {}) // 'function'
  8. typeof undefined // 'undefined'
  9. typeof Symbol(1) // 'symbol'

由结果可知typeof可以测试出numberstringbooleanSymbolundefinedfunction,而对于null数组对象,typeof均检测出为object,不能进一步判断它们的类型。

instanceof

instanceof可以判断一个对象的构造函数是否等于给定的值

  1. ({}) instanceof Object // true
  2. [] instanceof Array // true
  3. new Date() instanceof Date // true
  4. /123/g instanceof RegExp // true

instanceof方法一般用于判断自定义构造函数实例。

  1. function Person() {}
  2. const p = new Person();
  3. p instanceof Person // true
  4. p instanceof Object // true
The instanceof** operator** tests to see if the prototype property of a constructor appears anywhere in the prototype chain of an object. The return value is a boolean value. —— MDN

instanceof原理是判断构造函数的原型prototype属性是否出现在对象的原型链上。

需要注意的是,这里提到instanceof是判断对象的构造函数,不适用与非对象类型的变量,看MDN的例子:

  1. let literalString = 'This is a literal string';
  2. let stringObject = new String('String created with constructor');
  3. literalString instanceof String; // false, string literal is not a String
  4. stringObject instanceof String; // true

constructor

  1. console.log(false.constructor === Boolean);// true
  2. console.log((1).constructor === Number);// true
  3. console.log(''.constructor === String);// true
  4. console.log([].constructor === Array);// true
  5. console.log(({}).constructor === Object);// true
  6. console.log((function test() {}).constructor === Function);// true
  7. console.log(Symbol('1').constructor === Symbol);// true

注意:undefined和null没有contructor属性

这里可以看到虽然数字1的构造函数是Number,但1是对象字面量,不是通过new创建的,因此使用instanceof判断为false。

Object.prototype.toString

Object是js中所有其他数据类型的父类。意思是所有的数据类型都继承了Object。但是无论是string还是array都是会重写这个tostring方法的。所以'1'.toString()Object.prototype.toString.call('1')的结果不同。

Object.prototype.toString.call可以用来区分数组、null等引用类型。

  1. function Test(){};
  2. const t = new Test();
  3. Object.prototype.toString.call(1); '[object Number]'
  4. Object.prototype.toString.call(NaN); '[object Number]'
  5. Object.prototype.toString.call('1'); '[object String]'
  6. Object.prototype.toString.call(true); '[object Boolean]'
  7. Object.prototype.toString.call(undefined); '[object Undefined]'
  8. Object.prototype.toString.call(null); '[object Null]'
  9. Object.prototype.toString.call(Symbol());'[object Symbol]'
  10. Object.prototype.toString.call(Test); '[object Function]'
  11. Object.prototype.toString.call([1,2,3]); '[object Array]'
  12. Object.prototype.toString.call({});'[object Object]'
  13. Object.prototype.toString.call(t);'[object Object]'

注意自定义对象的判断只能得到”[object Object]”的结果。

常见变量的类型判断

判断一个变量是否是对象

  1. Object.prototype.toString.call(obj) ==='[object Object]'

判断JavaScript对象是否为空对象

  1. // 方法1 注意该方法性能较差
  2. function isEmptyObject(obj) {
  3. return JSON.stringify(obj) === '{}';
  4. }
  5. // 方法2 因为for in只能枚举对象自身的属性,不能枚举原型属性,因此可以用来判断空对象
  6. function isEmptyObject(obj) {
  7. for (var key in obj) {
  8. return false;
  9. }
  10. return true;
  11. }
  12. // 方法3 Object.keys也是只能获取自身属性,不能获取原型属性
  13. function isEmptyObject(obj) {
  14. return Object.keys(obj).length === 0;
  15. }

如何判断一个对象是否数组

  1. // ES6中增加的数组方法
  2. Array.isArray()
  3. // 使用constructor判断
  4. function isArray(arr) {
  5. return arr.constructor.toString().indexOf("Array") > -1;
  6. }
  7. function isArray(arr) {
  8. return arr.constructor === Array;
  9. }
  10. // 用instanceof判断
  11. function isArray(arr) {
  12. return arr instanceof Array;
  13. }

判断NaN

isNaN()用来判断一个变量是否为NaN

  1. isNaN(NaN); // true

或者利用NaN和自己不相等的特性

  1. typeof num === 'number' && num !== num

类型转换

为什么要做类型转换

js是弱类型的语言,声明变量时候未指定变量类型,因此在很多场景下需要做类型转换。

js和其他端交互(如服务端、native、DOM(如input的value是字符串,需要转换为数字))时候,其他端对数据类型可能有要求

不同类型数据之间可能要进行运算

某些场景支持的数据类型固定(如if的condition需要是bool),这时候需要进行类型转换。

类型转换分为显式转换(包装类型函数、parseInt )和自动转换(隐式转换)。

转换为字符串类型

使用toString方法或者String()效果相同

  1. (1).toSting(); // '1'
  2. String(1); // '1'

null和undefined没有toString方法,其他的类型都有。null 和undefined转换字符串可以用String(null)或者’’ + null

转换为数值类型

parseInt和parseFloat对字符串解析会将字符串前面符合数字规则的部分解析成数字,如果开头就不是数字则返回NaN

  1. parseInt(123); // 123
  2. parseInt('123'); // 123
  3. parseInt('123a'); // 123
  4. parseInt('a123'); // NaN
  5. parseInt('123.123'); // 123
  6. parseFloat('123.123'); // 123.123

解析数组,解析第一个元素。其他情况都返回NaN。

  1. parseInt([]); // NaN
  2. parseInt([1]); // 1
  3. parseInt([1, 2]); // 1
  4. parseInt(['1']); // 1
  5. parseInt(['a']); // NaN

类型转换规则

常见变量转换表

原始值 转为数字 转为字符串 转为布尔
false 0 “false” false
true 1 “true” true
0 0 “0” false
1 1 “1” true
“0” 0 “0” true
“000” 0 “000” true
“1” 1 “1” true
NaN NaN “NaN” false
Infinity Infinity “Infinity” true
-Infinity -Infinity “-Infinity” true
“” 0 “” false
“20” 20 “20” true
“a” NaN “a” true
[] 0 “” true
[10,20] NaN “10,20” true
function(){} NaN “function(){}” true
{ } NaN “[object Object]” true
null 0 “null” false
undefined NaN “undefined” false

对象转原始类型

  • 如果有Symbol.toPrimitive()方法,优先调用再返回
  • 调用valueOf(),如果转换为原始类型,则返回
  • 调用toString(),如果转换为原始类型,则返回
  • 没有Symbol.toPrimitive/valueOf/toString的情况
    • 转布尔值
      • null转为false
      • 非null转为true
    • 转数字NaN
    • 转字符串’[object Object]’
  1. // Symbol.toPrimitive
  2. var obj = {
  3. [Symbol.toPrimitive] () {
  4. return 3;
  5. },
  6. valueOf () {
  7. return 2;
  8. },
  9. toString () {
  10. return 1;
  11. }
  12. }
  13. console.log(obj) // 3
  14. // valueOf
  15. var obj = {
  16. valueOf () {
  17. return 2;
  18. },
  19. toString () {
  20. return 1;
  21. }
  22. }
  23. console.log(obj) // 2
  24. // toString
  25. var obj = {
  26. toString () {
  27. return 1;
  28. }
  29. }
  30. console.log(obj) // 1
  31. // 默认
  32. var obj = {
  33. }
  34. console.log(!!{}); // true
  35. console.log(obj + '') // "[object Object]"
  36. console.log(+obj); // NaN

数组转为原始类型

  • 转成bool永远是true
  • 转成字符串,用逗号将各个元素连接起来
  • 转成数值,先转成字符串,再将字符串转成数值类型

另一个例子:

如何让if (a == 1 && a == 2)返回true

  1. var a = {
  2. value: 0,
  3. valueOf: function() {
  4. this.value++;
  5. return this.value;
  6. }
  7. };
  8. console.log(a == 1 && a == 2);//true

隐式转换

在一些场景中,不同类型的变量会放在一起处理,这时候js引擎会做隐式转换转,转换为相同的类型后再处理。还有些情况下对变量的类型有要求,而变量如果不符合要求就会进行隐式转换(如if语句要求是bool值,如果是非bool值,会先转换为bool再处理)。

隐式转换场景

  1. 算术运算
  2. 单目运算符+
  3. if条件表达式转换为布尔
  4. !运算符转为布尔
  5. ==比较
  6. 比较运算符>、<、≥、≤

转换规则

  • 双目+号
    • 如果两个操作数都是数值,执行常规的加法计算
    • 如果两个操作数是数值或者布尔,则都转为数值进行计算
    • 如果有至少一个操作数是字符串,则都转成两个字符串拼接
    • 如果有一个操作数是对象,则将这个对象调用toString()转为字符串进行计算
  • -*/号都转换成数字。注意NaN和任何变量运算结果为NaN
  • if转换为bool。
  • !转换为bool。
  • 单目+会转为数字,转换失败时候会转为NaN。
  • ===如果类型不同返回false,如果类型相同则比较值是否相同,注意引用类型对象只和自身相等。
  • == 转换规则
    • 如果是类型相同,直接进行===比较
    • 如果类型不同,要进行转换再比较
      • 如果有一个操作数是布尔,则在比较前先将其转为数值(true -> 1; false -> 0)
      • 数值和字符串比较,比较前先将字符串转为数字
      • 如果一个是对象,另一个不是,则先调用对象的valueOf方法,用得到的基本类型值按照前面的规则比较。
      • null和undefined相等
      • 比较之前,null和undefined不转换为其他任何值(所以null只与自己和undefined==,而且null只和自己===;undefined也是一样)
      • 如果有一个是NaN,则相等返回false,不等返回false
      • 如果两个都是对象,则如果它们是同一个对象返回true,否则返回false
  • 比较运算符> < >= <=转换规则
    • 如果两个操作数都是数值,直接比较
    • 如果只有一个是数值,将另一个操作数转为数值,然后比较
    • 如果两个操作数都是字符串,则按字典比较
    • 如果有一个是bool,则将这个转为数值再比较
    • 如果有一个操作数是对象,则调用它的valueOf()获取原始值,若没有valueOf()方法,则调用toString()方法得到字符串,然后按照之前的规则进行比较。

练习题

  1. 1 + '1' // '11'
  2. 1 + + '1' // 2
  3. 1 + 1 + '1' // '21'
  4. 1 + '1' + 1 // '111'
  5. 1 + 'a' // '1a'
  6. 1 + +'a' // NaN
  7. 1 - '1' // 0
  8. !![] // true
  9. !!'' // false
  10. !!{} // true
  11. if ([]) {console.log('bingo')} // 'bingo'
  12. if ('') {console.log('bingo')} //
  13. if ('0') {console.log('bingo')} // 'bingo'
  14. if ('{}') {console.log('bingo')} // 'bingo'
  15. NaN == '' // false
  16. NaN == 0 // false
  17. NaN == 'NaN' // false
  18. 1 == '1' // true
  19. 0 == '0' // true
  20. 0 == '' // true
  21. true == '1' // true
  22. true == 'true' // false
  23. true == '0' // false
  24. [] == true // false
  25. [] == ![] // true (首先这个表达式等同于[] == false,然后布尔转为数字:[] == 0,然后对象要转为字符串再比较,即:'' == 0,这样是一个字符串和一个数值比较,要先将字符串转为数字,即:0 == 0)
  26. [] == '0' // false
  27. [] == '' // true
  28. ({}) == '[object Object]' // true
  29. ({}) == 0 // false
  30. ({}) == NaN // false
  31. true > '0' // true
  32. '1.2.3' < '1.2.4' // true
  33. [2] > 1 // true
  34. [2, 1] > 1 // false

__

__