类型概述
大约 15 分钟
类型概述
原始值(primitive value)
原始值(primitive value)是最简单的数据,也称为原始类型。
Undefined
:一个声明未定义的变量的初始值,或没有实际参数的形式参数。Null
:表示一个空对象指针。Boolean
:布尔值,取值仅能为true
(真)和false
(假)的数据类型。Number
:64 位双精度浮点型的数字数据类型。Bigint
:表示大于2^53 - 1
的整数,BigInt
可以表示任意大的整数。String
:字符串,用于表示文本的字符序列。Symbol
:Symbol()
函数会返回symbol
类型的值,symbol
值都是唯一的。一个symbol
值能作为对象属性的标识符,这是该数据类型仅有的目的。
原始值特点:
- 保存原始值的变量 :按值(by value)访问的。
- 原始值的初始化 :可以只使用原始字面量形式。如果使用的是
new
关键字,则 JavaScript 会创建一个Object
类型的实例,但其行为类似原始值。 - 函数传递参数 :按值传递。在传递原始值参数时,值会被复制到一个局部变量(即一个命名参数)。
- 原始值的比较 :值的比较。
- 原始值的保存 :存储在栈内存中。占用空间小、大小固定,通过值来访问。
引用值(reference value)
引用值(reference value)(或者对象)是某个特定引用类型的实例,也称为引用类型,是把数据和功能组织在一起的结构。
Object
:用于存储各种键值集合和更复杂的实体,所有引用类型都从它继承了基本的行为。Array
:表示一组有序的值,并提供了操作和转换值的能力。Function
:每个 JavaScript 函数实际上都是一个Function
对象。Map
:键/值对的集合,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值,一个键只能出现一次。Set
:值的集合。存储任何类型的唯一值,无论是原始值或者是对象引用。可以按照插入的顺序迭代它的元素,Set
中的元素是唯一的。WeakMap
:键/值对的集合,其中的键是弱引用的。键必须是对象,而值可以是任意的。WeakSet
:对象值的集合。只能是对象的集合,在WeakSet
的集合中,对象的引用为弱引用,所有对象都是唯一的。原始值包装类型
: 三种原始值包装类型Boolean
、Number
、String
可以被当做对象使用。具备以下特点:- 每种包装类型都映射到同名的原始类型。
- 以读模式访问原型值时,后台会实例化一个原始值包装类型的对象,调用实例上特定的方法操作相应的数据。
- 涉及原始值的语句执行完毕之后,包装对象会被销毁。
类型化数组
:类似数组的对象,并提供了一种用于在内存缓冲区中访问原始二进制数据的机制。ArrayBuffer
:用来表示一个通用的、固定长度的二进制数据缓冲区。不能直接操作ArrayBuffer
中的内容,需要使用DataView
DataView
:提供有可以操作缓冲区中任意数据的访问器(getter/setter)API。
Math
:包含辅助完成计算的属性和方法。Date
:提供关于日期和时间的信息,包括当前日期、时间及相关计算。RegExp
:提供正则表达式功能。- ......
引用值特点:
- 保存引用值的变量 :按引用(by reference)访问的。
- 函数传递参数:按值传递。在传递引用值参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部。
- 引用值的比较 :引用的比较。
- 引用值的保存 :存储在堆内存中。引用数据类型占据空间大、大小不固定,如果存储在栈中,将影响程序的运行性能。引用数据类型会在栈中存储一个指针,这个指针指向堆内存空间中该实体的起始地址。
类型判断
typeof
typeof operand
返回一个字符串,标识操作数的类型。适合用来判断一个变量是否为原始类型。
operand
:表示要返回类型的对象或基本类型的表达式。
// 判断原始值(原始类型) - Number
typeof 3.14 // 'number'
typeof Infinity // 'number'
typeof NaN // 'number'
typeof Number(1) // 'number'
typeof Number('shoe') // 'number'
// 判断原始值(原始类型) - BigInt
typeof 42n // 'bigint'
typeof Object(1n) // 'object' :使用 Object 包装后, BigInt 被认为是一个普通 'object'
// 判断原始值(原始类型) - String
typeof 'string' // 'string'
typeof typeof 1 // 'string'
typeof String(1) // 'string'
// 判断原始值(原始类型) - Boolean
typeof true // 'boolean'
typeof Boolean(1) // 'boolean'
typeof !!1 // 'boolean'
// 判断原始值(原始类型) - Symbols
typeof Symbol() // 'symbol'
typeof Symbol('foo') // 'symbol'
// 判断原始值(原始类型) - Undefined
typeof undefined // 'undefined'
typeof declaredButUndefinedVariable // 'undefined' - 已声明但未定义变量
typeof undeclaredVariable // 'undefined' - 未声明变量
// 判断函数
typeof function () {} // 'function'
typeof class C {} // 'function'
typeof Math.sin // 'function'
typeof console.log // 'function'
// 判断引用类型
typeof null // 'object'
typeof { a: 100 } // 'object'
typeof ['a', 'b'] // 'object'
typeof new Date() // 'object'
typeof /regex/ // 'object'
// 避免使用原始值包装类型,可使用字面量方式
typeof new Boolean(true) // 'object'
typeof new Number(1) // 'object'
typeof new String('abc') // 'object'
instanceof
object instanceof constructor
:用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
object
:某个实例对象constructor
:某个构造函数
'str' instanceof String // true
new String('newStr') instanceof String // true
new String('newStr') instanceof Object // true
{} instanceof Object // true
Object.create(null) instanceof Object // false,一种创建非 Object 实例的对象的方法
new Date() instanceof Date // true
new Date() instanceof Object // true
// ========================================
// ========================================
function Foo() {}
function Bar() {}
var foo = new Foo()
foo instanceof Foo // true
foo instanceof Object // true,因为 Object.prototype.isPrototypeOf(foo) 返回 true
Foo.prototype instanceof Object // true
Bar.prototype = new Foo() // 继承
var bar = new Bar()
bar instanceof Bar // true
bar instanceof Foo // true,因为 Foo.prototype 在 bar 的原型链上
Object.prototype.toString.call
toString
返回一个表示该对象的字符串,默认情况下返回类型字符串。该方法旨在重写(自定义)派生类对象的类型转换的逻辑。
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(3.14) // '[object Number]'
Object.prototype.toString.call('str') // '[object String]'
Object.prototype.toString.call({ a: 100 }) // '[object Object]'
Object.prototype.toString.call([1, 2, 3]) // '[object Array]'
Object.prototype.toString.call(function () {}) // '[object Function]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(function* () {}) // '[object GeneratorFunction]'
Object.prototype.toString.call(Promise.resolve()) // '[object Promise]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call(/regex/) // '[object RegExp]'
// ========================================
// ========================================
function typeOf(obj) {
const toString = Object.prototype.toString
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object',
'[object Map]': 'map',
'[object Set]': 'set',
'[object GeneratorFunction]': 'generatorFunction',
'[object Promise]': 'promise'
}
return map[toString.call(obj)]
}
// ========================================
// ========================================
function typeOf(o) {
var s = Object.prototype.toString.call(o)
return s.match(/\[object (.*?)\]/)[1].toLowerCase()
}
typeOf({}) // "object"
typeOf([]) // "array"
typeOf(5) // "number"
typeOf(null) // "null"
typeOf() // "undefined"
typeOf(/abcd/) // "regex"
typeOf(new Date()) // "date"
// 在 typeOf 函数的基础上,添加专门判断某种类型数据的方法
[
'Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp',
].forEach(function (t) {
typeOf['is' + t] = function (o) {
return typeOf(o) === t.toLowerCase()
}
})
typeOf.isObject({}) // true
typeOf.isNumber(NaN) // true
typeOf.isRegExp(/abc/) // true
Array.isArray
Array.isArray(value)
用于确定传递的值是否是一个 Array
。如果值是 Array
,则为 true
;否则为 false
。
// 下面的函数调用都返回 true
Array.isArray([])
Array.isArray([1])
Array.isArray(new Array())
Array.isArray(new Array('a', 'b', 'c', 'd'))
Array.isArray(new Array(3))
// Array.prototype 其实也是一个数组。
Array.isArray(Array.prototype)
// 下面的函数调用都返回 false
Array.isArray()
Array.isArray({})
Array.isArray(null)
Array.isArray(undefined)
Array.isArray(17)
Array.isArray('Array')
Array.isArray(true)
Array.isArray(false)
Array.isArray(new Uint8Array(32))
Array.isArray({ __proto__: Array.prototype })
当检测 Array
实例时,Array.isArray
优于 instanceof
,因为 Array.isArray
能检测 iframes
。
const iframe = document.createElement('iframe')
document.body.appendChild(iframe)
xArray = window.frames[window.frames.length - 1].Array
const arr = new xArray(1, 2, 3) // [1,2,3]
// 正确检查 Array
Array.isArray(arr) // true
arr instanceof Array // false
类型强制转换
转换为 Boolean
- 转换为 Boolean 值为
false
:undefined
、null
、0
、-0
、false
、NaN
、''
(空字符串) - 转换为 Boolean 值为
true
:所有其他值,包括任何对象,空数组([]
)或字符串"false"
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(-0) // false
Boolean(false) // false
Boolean(NaN) // false
Boolean('') // false
Boolean(0n) // false , 将 BigInt 转为 Boolean
Boolean(1n) // true , 将 BigInt 转为 Boolean
Boolean('false') // true
Boolean(3.14) // true
Boolean(true) // true
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
Boolean(Symbol('foo')) // true
转换为 String
- 原始值类型
Number
类型 :转换为相应的字符串String
类型 :转换后还是原来的值Boolean
类型 :true
转换成字符串'true'
,false
转换成字符串'false'
Undefined
类型 : 转换为字符串'undefined'
Null
类型 :转换为字符串'null'
Symbol
类型 :转换为相应的字符串
Object
类型- 调用对象自身的
toString
方法。如果返回原始类型的值,则对该值使用String
函数,不再进行以下步骤。 - 如果
toString
方法返回的是对象,再调用原对象的valueOf
方法。如果valueOf
方法返回原始类型的值,则对该值使用String
函数,不再进行以下步骤。 - 如果
valueOf
方法返回的是对象,就报错。
- 调用对象自身的
Array
类型 :返回该数组的字符串形式
String(3.14) // "3.14"
String(1n) // "1" , 将 BigInt 转为 String
String('foo') // "foo"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"
String(Symbol('foo')) // "Symbol(foo)"
String({ a: 1 }) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
String({
valueOf: function () {
return {}
},
toString: function () {
return {}
},
})
// toString 和 valueOf 方法,返回的都是对象,则会报错
// TypeError: Cannot convert object to primitive value
String({
toString: function () {
return 3
},
})
// 返回 toString 的值
// "3"
String({
valueOf: function () {
return 2
},
})
// 返回 valueOf 的值
// "[object Object]"
String({
valueOf: function () {
return 2
},
toString: function () {
return 3
},
})
// 调用 toString --> valueOf,返回的值
// "3"
转换为 Number
- 原始值类型
Number
类型 :转换后还是原来的值String
类型 :转换为原来的值- 如果可以被解析为数值,则转换为相应的数值
- 如果不可以被解析为数值,返回
NaN
''
(空字符串)转换为0
Boolean
类型 :true
转换成数值1
,false
转换成数值0
Undefined
类型 : 转换为NaN
Null
类型 :转换为NaN
Symbol
类型 :抛出TypeError
Object
类型- 调用对象自身的
valueOf
方法。如果返回原始类型的值,则直接对该值使用Number
函数,不再进行后续步骤。 - 如果
valueOf
方法返回的还是对象,则改为调用对象自身的toString
方法。如果toString
方法返回原始类型的值,则对该值使用Number
函数,不再进行后续步骤。 - 如果
toString
方法返回的是对象,就报错。
- 调用对象自身的
Array
类型 :转换为NaN
Number(3.14) // 3.14
Number(1n) // 1 , 将 BigInt 转为 Number
Number('3.14') // 3.14
Number('\t\v\r3.14\n') // 3.14,会自动过滤一个字符串前导和后缀的空格
Number('3.14abc') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0
Number(Symbol('foo')) // Uncaught TypeError: Cannot convert a Symbol value to a number
Number({ a: 1 }) // NaN
Number({}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
Number({
valueOf: function () {
return {}
},
toString: function () {
return {}
},
})
// toString 和 valueOf 方法,返回的都是对象,则会报错
// TypeError: Cannot convert object to primitive value
Number({
valueOf: function () {
return 2
},
})
// 返回 valueOf 的值
// 2
Number({
toString: function () {
return 3
},
})
// 返回 toString 的值
// 3
Number({
valueOf: function () {
return 2
},
toString: function () {
return 3
},
})
// 调用 valueOf --> toString,返回的值
// 2
类型自动转换
相等操作符
- 等于操作符用两个等于号(
==
)表示,如果操作数相等,则会返回true
。 - 不等于操作符用叹号和等于号(
!=
)表示,如果两个操作数不相等,则会返回true
。
这两个操作符都会先进行类型转换(通常称为强制类型转换),再确定操作数是否相等。
在转换操作数的类型时,相等和不相等操作符遵循如下规则:
- 如果任一操作数是
Boolean
,则将其转换为Number
再比较是否相等。false
转换为0
,true
转换为1
。 - 如果一个操作数是
String
,另一个操作数是Number
,则尝试将字符串转换为数值,再比较是否相等。 - 如果一个操作数是
Object
,另一个操作数不是,则调用对象的valueOf()
方法取得其原始值,再根据前面的规则进行比较。
在进行比较时,这两个操作符会遵循如下规则:
NaN
不等于NaN
。null
和undefined
相等。null
和undefined
不能转换为其他类型的值再进行比较。- 如果有任一操作数是
NaN
,则相等操作符返回false
,不相等操作符返回true
。 - 如果两个操作数都是
Object
,则比较它们是不是同一个Object
。如果两个操作数都指向同一个Object
,则相等操作符返回true
。否则,两者不相等。 - 如果有任一操作数是
Symbol
,则相等操作符返回false
,不相等操作符返回true
。
null == undefined // true
null == 0 // false
undefined == 0 // false
0 == false // true
1 == true // true
2 == true // false
0 == !!null // true
0 == !!undefined // true
NaN == 'NaN' // false
NaN == 3.14 // false
NaN == NaN // false
NaN != NaN // true
'str' == String('str') // true
'str' == new String('str') // true
String('str') == new String('str') // true
new String('str') == new String('str') // false
'3.14' == 3.14 // true
new Number(3.14) == 3.14 // true
new Number(3.14) == new Number(3.14) // false
[] == ![] // true
// 根据运算符优先级, ! 的优先级是大于 == ,所以先会执行 ![]
// [] == ![] --> [] == false --> [] == 0 --> '' == 0 -> 0 == 0 --> true
{} == !{} // false
// {} == !{} --> {} == false --> {} == 0 --> NAN == 0 --> false
if 语句
- truely 变量:
!!a === true
的变量 - falsely 变量:
!!a === false
的变量
// 以下是 falsely 变量。除此之外都是 truely 变量
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined == false
!!false === false
关系操作符
关系操作符执行比较两个值的操作,包括小于(<
)、大 于(>
)、小 于 等 于( <=
)和大于等于(>=
)
- 如果操作数都是
Number
,则执行数值比较。 - 如果操作数都是
String
,则逐个比较字符串中对应字符的编码(Unicode
)。 - 如果有任一操作数是
Number
,则将另一个操作数转换为数值,执行数值比较。 - 如果有任一操作数是
Object
,则调用其valueOf()
方法,取得结果后再根据前面的规则执行比较。如果没有valueOf()
操作符,则调用toString()
方法,取得结果后再根据前面的规则执行比较。 - 如果有任一操作数是
Boolean
,则将其转换为数值再执行比较。 - 任何值(包括
NaN
本身)与NaN
使用非相等运算符进行比较,返回的都是false
。
2 > 1 // true
'cat' > 'dog' // false
'cat' > 'Cat' // true,“c”的 Unicode 码点是 99,“C”是 67
'大' > '小' // false,“大”的 Unicode 码点是 22823,“小”是 23567
5 > '4' // true,等同于 5 > Number('4'),即 5 > 4
true > false // true,等同于 Number(true) > Number(false),即 1 > 0
2 > true // true,等同于 2 > Number(true),即 2 > 1
1 > NaN // false
1 <= NaN // false
'1' > NaN // false
'1' <= NaN // false
NaN > NaN // false
NaN <= NaN // false
var x = [2]
x > '11' // true
// 等同于 [2].valueOf().toString() > '11',
// 即 '2' > '11'
x.valueOf = function () {
return '1'
}
x > '11' // false
// 等同于 (function () { return '1' })() > '11'
// 即 '1' > '11'
// [2] > [1] // true
// 等同于 [2].valueOf().toString() > [1].valueOf().toString()
// 即 '2' > '1'
// [2] > [11] // true
// 等同于 [2].valueOf().toString() > [11].valueOf().toString()
// 即 '2' > '11'
// ({ x: 2 }) >= ({ x: 1 }) // true
// 等同于 ({ x: 2 }).valueOf().toString() >= ({ x: 1 }).valueOf().toString()
// 即 '[object Object]' >= '[object Object]'
四则运算符
加法运算符(+)
如果两个操作数都是数值,加法操作符执行加法运算并根据如下规则返回结果:
- 任一操作数是
NaN
,则返回NaN
Infinity + Infinity
,则返回Infinity
(-Infinity) + (-Infinity)
,则返回-Infinity
Infinity + (-Infinity)
,则返回NaN
(+0) + (+0)
,则返回+0
(-0) + (+0)
,则返回+0
(-0) + (-0)
,则返回-0
如果有一个操作数是字符串,则要应用如下规则:
- 如果两个操作数都是
String
,则将第二个字符串拼接到第一个字符串后面; - 如果只有一个操作数是
String
,则将另一个操作数转换为String
,再将两个字符串拼接在一起。 - 如果有任一操作数是
Object
、Number
或Boolean
,则调用toString()
方法以获取字符串,然后再应用关于字符串的规则。对于undefined
和null
,则调用String()
函数,分别获取"undefined"
和"null"
。
'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function () {} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
其他运算符
其他运算符都会把运算值自动转成数值。
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
一元运算符也会把运算值转成数值。
// +'abc' // NaN
// -'abc' // NaN
// +true // 1
// -false // 0
Date()
如果 Date()
构造函数被调用时,有一个参数不是 Date
实例,它将被强制转换为原始值,然后检查它是否是一个字符串。
new Date() // Fri Apr 07 2023 01:01:23 GMT+0800 (中国标准时间),当前时间
new Date(undefined) // Invalid Date
// undefined 为原始类型值,但不是一个字符串
// 将被强制转换为一个数值,也就是 NaN,因此不是一个有效的时间戳
new Date(null) // Thu Jan 01 1970 08:00:00 GMT+0800 (中国标准时间)
// null 为原始类型值,但不是一个字符串,将被强制转换为 0,