JavaScript 数据类型
一、基本数据类型
- Undefined
- Null => Object 空对象 表示空指针
- Boolean
- Number
- String
- Object 细分
- Function
- Array
- RegExp
- Object
二、typeof 操作符
typeof 只是检测最基础的类型,对于Object类型的细分无能为力,比如检测不到数组。然而function有些例外,从技术角度讲,函数在 ECMAScript中是对象,不是一种数据类型。然而,函数也 确实有一些特殊的属性,因此通过 typeof 操作符来区分函数和其他对象是有必要的。那怎么判断数组的类型呢 1. Object.prototype.toString.call() , 2.Array.isArray()
- “undefined”——如果这个值未定义
var a
alert(a) //undefined
typeof undefined // 'undefined'
alert(b) //Reference Error
typeof b //'undefined'
因此typeof可以检测两种状态的:
- 未声明
- 未赋值
- “boolean”——如果这个值是布尔值
- “string”——如果这个值是字符串
- “number”——如果这个值是数值
- “object”——如果这个值是对象(包括Object的派生类)或者null
typeof 只是检测基础类型其实可以说只是检测
三、细说类型
(1) null 空对象 和 undefined 未定义
undefined 是 派生于 null 的,它们有许多的共同点,他们最有意思的地方就是被Object排除在外
-
undefined 和 null 的特性
- 两种种类型各只有一个值:undefined 和 null,undefined 和 null 就既是类型也是值
- 都没有包装对象(或者说不可生成,应为null本身就是空对象,更别提包装成对象, undefined派生于null自然和其一个尿性),其实没有Object派生的对应的undefined和null的构造函数,也是证实他们不能转换为对象
- null == undefined => true;null === undefined => false,相等操作符验证值相等,全等操作符验证类型是否相等
-
其实真正开发的时候大部分人还是分不清楚null和undefined的,或者说该怎么使用
-
如果可以,请尽量不要使用undefined类型,即使要判断一个变量可能是undefined也不要尝试和 null相等比较,最简单直接的就是 typeof
-
事实上没有任何必要给一个变量设置未undefined,如果你觉得声明变量可能有不安全值可以不显示赋值undefined,否则推荐给定初值,这种当typeof一个变量为’undefined’的时候就知道是为声明而不是赋值为undefined
var number = 0//希望一个数值则给定一个数值初始化 var string = '' var boolean = false
-
即使真的需要undefined也可能只是在调用函数传递参数是,为了数据配对而被迫的使用undefined实参
myFun('a', undefined, 'b')
-
null 是当你需要使用一个引用变量的时候,作为初值使用;或者当你不需要使用该应用变量是赋值为null,方便清除对象内存
-
(2) Number类型
- 浮点数值
- 数值范围 zhengzhi、Infinity(无穷,分正负)
- NaN 表示“非数值”的一种特殊的数值,注意NaN仍然是一种数值
- 数值转换主要根据Number方法
-
NaN 表示一个本应该返回数值的操作却未返回数值
- 在进行计算的时候(+、-、*、/、%等)无论和什么值计算都是NaN
- 在比较的时候是false,总某种意义上说是根据
自然的进行比较(>、<、<=、>=等)都是falseNaN == NaN //为false
- isNaN函数,表示判断参数是否为能转换为数值且不为NaN
-
Infinity 表示无穷,超出范围
- 数值计算超出预定返回,返回Infinity,Number.MIN_VALUE、Number.MAX_VALUE:最小最带值
- Infinity分正负,一个负值Infinity为-Infinity,同理正值Infinity为+Infinity
Infinity == Infinity//true -Infinity == +Infinity//false Infinity == -Infinity//false Infinity == +Infinity//false
(3) String 类型
- ''单引号、""双引号、``模板字符串
- 转义字符
- 字符串的值是不可变的,"变"只不过是生成新的值,销毁原来的值
(4) Object类型
{
name: 'WcZix'
} instanceof Object
Object的prototype上有
- constructor 构造函数(当该对象作为原型使用时需要)
- hasOwnProperty(propertyName) 参数是否是该对象自身的属性而不是原型的(和 in 的本质区别在后面一段)
- isPrototypeOf
- propertyIsEnumerable(propertyName) 属性是否可以枚举,即被for in 遍历获取到
- toString 、 valueOf、[[ToPrimitive]] 是类型转换的时候默认调用的
1. 对象无法直接和原始类型比较
- 对象真实存在堆内存中,代码中可操作的也只是其地址,因此对象和原始类型从本质上就不能进行比较
- 但是为了能够比较,JS对对象绑定了一些方法,是得形式上对象和原始类型能够比较
- 这就是为什么需要 toString 、 valueOf、[[ToPrimitive]] 等方法
2. toString、valueOf意味着对象希望转换成字符串和数值
[[ToPrimitive]] 是 ES6 新增的,但是本质和valueOf、toString没有太大区别
-
注意,这里不包括转换Boolean的方法,说明对象默认不应该直接和Boolean比较,即使是和Boolean比较,最后也要转换为字符串和数值
-
toString、valueOf 表示将该对象转换为字符串、数值使用
-
但是在真正使用的时候调用方式有很大的区别:
- 当对象由于外部因素需要转换为数值时:该对象先调用valueOf判断是否返回原始类型,若不能则调用toString判断能否返回原始类型,若能将结果再次转换为数值
- 同理,当对象由于外部因素需要转换为字符串时,先toString,再valueOf
//在第四点代码基础上 1 / obj // 0.5 1 + obj // '12'
-
toString、valueOf和[[ToPrimitive]]都是可以被重写的
const obj = {} obj.valueOf = function() { return '2' //这里是故意这么写的 } obj.toString = function() { return 2 //这里是故意这么写的 }
你也能看得出来,他们并不安全,并且返回的值可以是任意的
-
对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:
- 如果已经该函数结果是原始类型了,那就不需要转换了
- 调用 x.valueOf,如果转换为基础类型,就返回转换的值
- 调用 x.toString,如果转换为基础类型,就返回转换的值
- 如果都没有返回原始类型
PS:更改 [[ToPrimitive]] 决定对象在转换原始类型时,是否调用valueOf和toString,即更改对象转换原始类型的方案
四、类型转换
(1) null 和 undefined
它们在转换为数值和字符串表现不一致
Number(undefined) // NaN
Number(null)// 0
String(undefined) // 'undefined'
String(null) // 'null'
但是在转换为Boolean一致,都为false
(2) 数值和字符串
其实上面都说完了其它类型的转换,Object的转换其实最终也体现在数值和字符串的转换,因此几乎可以说数值和字符串的转换在程序中是大量存在的,无论是显式或者隐式,
这里就不介绍了,看看书就行,按照标准来,没有太多技巧
五、运算符的偏爱类型
前面说过对象在转换的时候由外部因素决定,其它类型也是如此。但是这个外部因素究竟是谁?运算符的偏爱类型
(1) 计算型运算符
- +、-、*、/、%
- –、++
这些运算符中除了+ ,其它都偏爱数值类型,即Number
+ 比较特殊,它偏爱字符串,如果一个操作数是字符串,那么另外一个也要被转换为字符串,如果操作数是对象的话字符串优先
const obj = {}
obj.valueOf = function() {
return '2' //这里是故意这么写的
}
obj.toString = function() {
return 2 //这里是故意这么写的
}
1 + obj // '12'
再次看最后一行代码,肯定能感觉到满满的 “不公平”
(2) 比较型运算符
- >、>=、<、<=、== 偏爱数值
就拿 == 举例
1. 讨论两个操作数类型相同
- undefined == undefined
- null == null
- NaN != NaN 其它都和自己相等(当然要除去不可完全转换的数值,浮点数不准确),比较大小时也是通过数值大小直接比较
- 字符串逐字比较字符
- 对象直接比较地址
- Boolean没什么好说的
2. 讨论两个操作数类型不相同
- 一个对象的时候,对象转换为原始类型
- 字符串和Boolean转换都会优先转换为数值
//字符串转数值,而不是数值转字符串 1 == '01' //true //Boolean不会直接转成字符串,而是转换成数值之后比较 true == '0' //false
五、结语
类型转换非常复杂,但是如果你清晰了解各种运算符对数据类型的偏好,那掌握起来就很自然和舒服
一些面试题:
- [] == ![] 输出结果是?
- {} == !{} 输出结果是?
- 1 + ‘1’ 输出结果是?
- true + true 输出结果是?
- 4 + [] 输出结果是?
- 4 + {} 输出结果是?
- 4 + [1] 输出结果是?
- 4 + [1, 2, 3, 4] 输出结果是?
- ‘a’ + + 'b’输出结果是?