目 录
1、JavaScript中的数据类型
↬ JavaScript中的数据一共有八种:Number
、String
、Boolean
、Undefined
、Null
、Object
、Symobl
、BigInt
;
↬ 主要分为两种,分别是:
原始数据类型
、引用数据类型
;
1.1 原始数据类型
↬ 原始数据类型有:
Number
、String
、Boolean
、Undefined
、Null
、Symobl
、BigInt
;
其中 Symobl、BigInt是ES6新增的数据类型;
Symobl
:通过Symobl创建的数据类型是独一无二的并不可被改变,可以用于解决可能出现的全局变量冲突以及对象属性不唯一的问题;
BigInt
:表示一种数字类型的数据,可以表示任意精度格式的整数,可以安全的存储与操作即使超出了Number所能表示的范围的大整数;
1.2 引用数据类型
↬ 引用数据类型只有Object
;
Object类型是一个大类,
Array
、Function
、Date
等内置对象都是Object类型的子类;
1.3 原始数据类型与引用数据类型的区别
其主要区别在于他们的存储位置的不同:
引用数据类型
:数据存储在堆
内存中,指向数据的指针,存放在栈内存中,当访问引用数据的时候,先是在栈内存中检索指针,然后通过指针找到数据;
原始数据类型
:数据直接存储在栈
内存中;
堆内存与栈内存的区别
➢ 堆与栈的概念存在于操作系统内存与数据结构中;
数据结构中的堆与栈
:在数据结构中,栈中的数据存取方式是先进后出,而堆是一个优先队列,按照优先级来进行排序的,优先级可以按照大小来规定;
操作系统内存中的堆与栈
:在操作系统中,内存被分为堆区与栈区。
➲ 栈区内存由编译器自动分配与释放,用于存放函数的参数值,局部变量的值等,其操作方式类似于数据结构中的栈。
➲堆区内存一般由开发者分配与释放,若开发者不对堆区内存进行释放,那么程序结束时,可能会由垃圾回收机制进行回收。
2、检测数据类型的方法
2.1 typeof
在JavaScript中,typeof是一个运算符,通过该方法可以检测出数据的类型。
console.log(typeof 1) // number
console.log(typeof '1') // string
console.log(typeof true) // boolean
console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(typeof function(){}) // function
console.log(typeof {}) // object
console.log(typeof []) // object
由上方可以看出,通过typeof检测引用数据类型的时候,除Function外,检测出的类型都是Object类型,甚至连null检测出来的都是Object类型;
原因是因为,typeof 操作符是按照数据在计算机底层存储的二进制结果进行检测的。而null的二进制为:000000,所有的Object类型的数据在计算机底层存储的二进制都是000开头的,所以typeof无法很好的识别出Object类型与null数据类型;
2.2 instanceof
运行原理
:检测实例对象是否是该构造函数的实例对象,主要通过查询被检测对象的原型链上,能否找到与目标对象相同的原型;
console.log(1 instanceof Number) // false
console.log('1' instanceof String) // false
console.log(true instanceof Boolean) // false
console.log(function(){} instanceof Function) // true
console.log({} instanceof Object) // true
console.log([] instanceof Array) // true
通过上方可以看出,instanceof只能判断引用类型,不能判断基本数据类型,但是我们通过构造函数的方式去创建一个Number类型的值,就可以通过instanceof进行检测。
const a = new Number(1)
const b = Number(2)
const c = 3
console.log(a, a instanceof Number) // Number{1} true
console.log(b, b instanceof Number) // 2 false
console.log(c, c instanceof Number) // 3 false
手写 instanceof 方法
function myInstanceof(instance, type) {
let proto = Object.getPrototypeOf(instance) // 获取实例对象的原型
let prototype = type.prototype // 获取构造函数的prototype对象
while (true) {
// 实例对象原型为null时返回 false
if (!proto) return false;
// 当查找到的原型与目标对象的prototype对象全等时,返回true
if (proto === prototype) return true;
// 沿着原型链向上查找,直到查找至Null为止
proto = Object.getPrototypeOf(proto);
}
}
console.log(myInstanceof(1, Number)) // true
console.log(myInstanceof('1', String)) // true
console.log(myInstanceof(true, Boolean)) // true
console.log(myInstanceof({}, Object)) // true
console.log(myInstanceof([], Array)) // true
console.log(myInstanceof(function () {}, Function)) //true
2.3 constructor
constructor
属性,不仅可以让实例对象访问其构造函数,还可以做数据类型的判断;
console.log((1).constructor === Number); // true
console.log(('1').constructor === String); // true
console.log((true).constructor === Boolean); // true
console.log(({}).constructor === Object); // true
console.log((function () {}).constructor === Function); // true
console.log(([]).constructor === Array); // true
2.4 Object.prototype.toString.call()
toString是Object原型上的方法,Array、function等类型作为Object
的实例,都重写了toString方法。不同的对象类型调用toString方法时,调用的都是重写后的方法(function类型返回内容为函数体的字符串,Array类型返回内容为元素组成的字符串等),而不会去调用Object上原型toString方法(返回对象的具体数据类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,想要得到对象的具体类型,应该调用原型上的toString方法。
let obj = {
name:'link',
age:18
}
function fun() {
return 1
}
let arr = [1, 2, 3]
console.log(obj.toString())
console.log(arr.toString())
console.log(fun.toString())
let obj = {
name: 'link',
age: 18
}
function fun() {
return 1
}
let arr = [1, 2, 3]
console.log(Object.prototype.toString.call(1))
console.log(Object.prototype.toString.call('1'))
console.log(Object.prototype.toString.call(true))
console.log(Object.prototype.toString.call(undefined))
console.log(Object.prototype.toString.call(null))
console.log(Object.prototype.toString.call(obj))
console.log(Object.prototype.toString.call(fun))
console.log(Object.prototype.toString.call(arr))
3、类型转换
在JavaScript中数据类型转换分为两种,分别是显示类型转换、隐式类型转换;
显示类型转换
:强制类型转换,由开发人者通过内置方法转换数据类型,如:Number(‘123’);
隐式类型装换
:当对不同类型的值使用运算符的时候,值会在类型之间自动的切换;
3.1 ==
操作符号的类型转换规则
对于
==
操作符来说,如果对比的双方的类型不同,就会进行类型的转换,其判断流程如下:
- 首先会判断两者类型是否相同;
- 类型不同就进行转换;
- 判断是否在对比
null
和undefined
,如果是的话,就返回true
- 判断两者类型是否为
String
和Number
,是的话,会将String
类型转换为Number
类型 - 判断其中一方是否为
Boolean
类型,如果是就会把Boolean
转换为number
类型进行比较 - 判断其中一方是否为
Object
且另一方为String
、Number
或者Symobl
,是的话,就会把Object
转为原始类型进行判断。
流程图如下
// 操作符两边的值都尽量转换成Number
3 == true // false, 3 转为number为3,true转为number为1
'0' == false //true, '0'转为number为0,false转为number为0
'0' == 0 // '0'转为number为0
3.2 其他类型转String
的规则
Null
和Undefined
类型 ,null
转换为"null"
,undefined
转换为"undefined"
;Boolean
类型,true
转换为"true"
,false
转换为"false"
。Number
类型的值直接转换,不过那些极小和极大的数字会使用指数形式。Symbol
类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误。- 对普通对象来说,除非自行定义
toString()
方法,否则会调用toString()
(Object.prototype.toString())来返回内部属性[[Class]]
的值,如"[object Object]"
。如果对象有自己的toString()
方法,字符串化时就会调用该方法并使用其返回值。
3.3 其他类型转Number
的规则
Undefined
类型的值转换为NaN
。Null
类型的值转换为0
。Boolean
类型的值,true
转换为1
,false
转换为0
。String
类型的值转换如同使用Number()
函数进行转换,如果包含非数字值则转换为NaN
,空字符串为0
。Symbol
类型的值不能转换为数字,会报错。- 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive
会首先(通过内部操作 DefaultValue
)检查该值是否有valueOf()
方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString()
的返回值(如果存在)来进行强制类型转换。
【注意】如果
valueOf()
和toString()
均不返回基本类型值,会产生TypeError
错误。
3.4 其他类型转Boolean
的规则
以下这些是假值:
undefined
null
false
+0
、-0
和NaN
""
假值的布尔强制类型转换结果为 false
。从逻辑上说,假值列表以外的都应该是真值。
3.5 补充
首先要介绍
ToPrimitive
方法,这是 JavaScript 中每个值隐含的自带的方法,用来将值 (无论是基本类型值还是对象)转换为基本类型值。如果值为基本类型,则直接返回值本身;如果值为对象,其看起来大概是这样:
/**
* @obj 需要转换的对象
* @type 期望的结果类型
*/
ToPrimitive(obj,type)
type
的值为number
或者string
。
(1)当type为number时规则如下:
- 调用
obj
的valueOf
方法,如果为原始值,则返回,否则下一步; - 调用
obj
的toString
方法,后续同上; - 抛出
TypeError
异常。
(2)当type为string时规则如下:
- 调用
obj
的toString
方法,如果为原始值,则返回,否则下一步; - 调用
obj
的valueOf
方法,后续同上; - 抛出
TypeError
异常。
可以看出两者的主要区别在于调用
toString
和valueOf
的先后顺序。默认情况下:
- 如果对象为
Date
对象,则type
默认为string
;- 其他情况下,
type
默认为number
。
总结上面的规则,对于 Date 以外的对象,转换为基本类型的大概规则可以概括为一个函数:
var objToNumber = value => Number(value.valueOf().toString())
objToNumber([]) === 0
objToNumber({}) === NaN
而 JavaScript 中的隐式类型转换主要发生在+、-、*、/
以及==、>、<
这些运算符之间。而这些运算符只能操作基本类型值,所以在进行这些运算前的第一步就是将两边的值用ToPrimitive
转换成基本类型,再进行操作。
3.5.1 +
-
*
/
操作符
+
操作符的两边有至少一个
string
类型变量时,两边的变量都会被隐式转换为字符串;其他情况下两边的变量都会被转换为数字。
1 + '23' // '123'
1 + false // 1
1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number
'1' + false // '1false'
false + true // 1
-
、*
、 /
操作符转换出来的也是数字
1 * '23' // 23
1 * false // 0
4 / 2 // 2
'2' - 1 // 1 number
3.5.2 <
>
比较符
如果两边都是字符串,则比较字母表顺序:
'ca' < 'bd' // false
'a' < 'b' // true
其他情况下,转换为数字在进行比较:
'12' < 13 // true
false > -1 // true
以上说的是基本类型的隐式转换,而对象会被ToPrimitive
转换为基本类型再进行转换:
var a = {}
a > 2 // false
// 对比过程如下:
a.valueOf() // {}, 上面提到过,ToPrimitive默认type为Number类型,所以先valuOf,结果还是一个对象,进行下一步;
a.toString() // "[object object]", 现在是一个字符串了;
Number(a.toString()) // NaN,根据上面 ‘<’ 和 '>' 的规则,需要转换为Number
NaN > 2 // false, 得出结果
再举一个小栗子:
var a = {name:'Jack'}
var b = {age: 18}
a + b // "[object Object][object Object]"
// 运算过程如下:
a.valueOf() // {},上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
a.toString() // "[object Object]" b.valueOf() // 同理
b.toString() // "[object Object]"
a + b // "[object Object][object Object]"
相关文章
- ECMAScript - 初识 ECMAScript
- ECMAScript - 条件判断 & 循环
- ECMAScript - 数组 & Math() 对象
- ECMAScript - 函数
- 前端学习 - 初识HTML
- 前端自学 - CSS总结篇(一)
- 前端自学 - CSS总结篇(二)
- 前端自学 - CSS 定位 position
- 前端自学 - CSS 布局总结
- 前端自学 - CSS 过渡 & 动画
……