数据类型

  • 原始类型:NumberStringBooleanNullUndefinedSymbolBigInt
  • 引用类型:Object

引用数据类型可统称为一种,在JS中万物皆对象,除了基本数据类型其他都是对象。数组是对象、函数是对象、正则表达式也是对象。如Function、Array、Date等都是基于Object的。

基本数据类型

基本数据类型的主要特点是赋值方式是传值,并且值存在

由操作系统自动分配释放存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈;

数据类型 实例 说明
Number 12-117.233.1E-2 数值型数据
BigInt 12n-2n0n 表示任意精度格式的整数BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
String "message"“str”“name” 字符型数据,需加英文双引号单引号反引号
Boolean true, false 布尔型数据,不用加引号,表示逻辑真与假
Undefined undefined JavaScript中未定义的值
Null null 表示空值
Symbol let var_symbol = Symbol(); let var_symbol = Symbol.for('symbol'); Symbol 指的是独一无二的值。每个通过 Symbol() 生成的值都是唯一的。可以防止命名冲突

Number

Number型数据即为数值型数据用来表示整数和浮点数,分为正数、负数、小数等。

整数型数值可以使用十进制、八进制以及十六进制标识

  • 十进制:直接使用数字表示,例如:123456等。
  • 八进制:以0开头,例如:010077等。
  • 十六进制:以0x0X开头,例如:0xFF0x3F等。
1
2
3
4
5
let numn1=1;  //十进制,输出值为1
let numn12=-23; //十进制,输出值为-23
let numn2=0177;//八进制,输出值为127
let numn3=0xA3;//十六进制,输出值为163
console.log(numn1,numn2,numn3);//输出值为1,127,163

注意: 当你在JavaScript中使用八进制或十六进制数值时,它们会被自动转换为十进制表示。例如,010会被解释为十进制的8但是081会被解释成81(因为8进制里面没有80xFF会被解释为十进制的255


浮点数是指包含小数点的实数,且可用科学计数法来表示。科学计数法一般用于值极大或者极小的时候。

1
2
3
4
5
6
//Number--浮点数
let numfloat=1.23; //输出值为1.23
let numfloat1=-12.34; //输出值为-12.34
//科学计算法
let numfloat2=152e5; //值是152乘以10的5次方,即15200000
let numfloat3=152e-5; //值是152乘以10的-5次方,即0.00152

如果浮点数看起来是整数, JavaScript会 自动将浮点数转换为整数。原因是JavaScript总是希望使用更少的内存,因为浮点数使用的内存是整数值的两倍


范围: Number类型只能表示$-(2^{53}-1)到(2^{53}-1)$ 之间的值

  • Number.MIN_VALUE的值为 5e-324,这是 Number 类型能够表示的最接近于零的正数。这意味着任何小于 5e-324 的正数都会被 JavaScript 解释为 0
  • Number.MAX_VALUE 的值为 1.7976931348623157e+308,这是 Number 类型能够表示的最大值。这意味着任何大于 1.7976931348623157e+308 的数值都会被 JavaScript 解释为 Infinity
  • 当数值范围超过最大值或最小值时,可以使用Infinity-Infinity来表示无限数
1
2
3
//Number.MIN_VALUE和Number.MAX_VALUE
console.log(Number.MAX_VALUE); //输出值:1.7976931348623157e+308
console.log(Number.MIN_VALUE); //输出值:5e-324

特殊的Number类型:

  • Infinity

    • 当一个数太大,以至于无法用 Number.MAX_VALUE 表示时,JavaScript 会返回 Infinity
    • 当一个正数除以0时,结果为 Infinity
    • 使用 Number.POSITIVE_INFINITY可以访问这个值。例如:1 / 0返回 Infinity
  • -Infinity

    • 当一个数太小,以至于无法用 Number.MIN_VALUE 表示时,JavaScript 会返回 -Infinity
    • 当一个负数除以0或一个正数除以-0时,结果为 -Infinity
    • 使用 Number.NEGATIVE_INFINITY 可以访问这个值。例如:-1 / 0 返回 -Infinity
  • NaN(虽然 Not a Number,但是是Number类型)

    • 表示值不是数字(Not a Number) 。他是一个特殊的数值,表示无效数字。当一个操作或计算机结果不是一个有效的数字时,JavaScript会返回NaN
    • 任何与NaN的运算都会返回NaN
    • NaN不等于任何值,包括其本身

BigInt

BigInt 类型的数据必须添加后缀n。

BigInt数据类型提供了一种方法来表示大于$2^{53}-1$的整数。BigInt可以表示 任意大的整数(不能表示小数

Number类型只能安全的支持$-9007199254740991(-(2^{53}-1))$ 和 $9007199254740991(2^{53}-1)$之间的整数,任何超过这个范围的数值都会失去精度;而BigInt可以解决这个问题

  • 在整数的末尾追加nconsole.log(9007199254740999n); //9007199254740999
  • 调用BigInt() 构造函数
    1
    2
    let bigInt = BigInt("9007199254740999"); 
    //传递给BigInt()的参数将自动转换为BigInt 9007199254740999n

注意:

  • BigInt 除了不能使用一元加号运算符外,其他的运算符都可以使用
  • BigInt Number 之间不能进行混合操作

String

字符串是零个或多个字符的序列。JavaScript不区分单个字符和字符串,任何字符或字符串以英文单引号('')或双引号("")开头和结尾。

JavaScript字符串是不可变的,意味着一旦被创建就无法修改。

  • 以双引号开头的字符串必须是双引号结尾,以单引号开头的字符串也必须是单引号结尾,以什么引号开头就以什么引号结尾
  • 无论单引号或是双引号必须成对使用
  • 单引号/双引号可以互相嵌套,但是不以自已嵌套自已(单套双或双套单)
  • 必要时可以使用转义符 `` ,输出单引号或双引号
转义字符 描述
\b 退格符
\f 换页符
\n 换行符
\r 回车符
\t 制表符(Tab)
\v 垂直制表符
' 单引号
" 双引号
`` 反斜杠

Boolean

boolean型数据表示的是布尔型数据,它有两个固定的值 truefalse,表示肯定的数据用 true,表示否定的数据用 false,且任何时刻都只能使用两种状态中的一种,不能同时出现。

注意: Boolean型变量赋值时,不能在 true 或 false 外面加引号,否则就变成字符串(string)了

Undefined

Undefined型是未定义类型,未定义是比较特殊的类型,只有一个值 undefined。默认情况下,当声明变量时但未赋值的情况下,变量的默认值为 undefinedJavaScript 中变量的值决定了变量的数据类型。( Undefined是一种数据类型,只有一个值undefined

1
2
3
// 只声明了变量,并末赋值
let str
console.log(typeof str) // 结果为 undefined

Null

Null型数据表示空值,作用是表明数据空缺的值,只有一个值null。一般在设定已存在的变量(或对象的属性)为空时较为常见。(Null是一种数据类型,只有一个值null

注意: console.log(null == undefined) 结果是true

Symbol

Symbol 本质上是一种唯一标识符,可用作对象的唯一属性名,这样其他人就不会改写或覆盖你设置的属性值。在创建symbol类型数据 时的参数只是作为标识使用,所以 Symbol() 也是可以的。

  • 声明:let id = Symbol('id')
  • 即使是用同一个变量(标识)生成的值也不相等:
  • 隐藏性,for···in object.keys() 不能访问
  • 但是也有能够访问的方法:Object.getOwnPropertySymbols, 该方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
1
2
3
4
5
6
7
let id = Symbol("id");
let obj = {
[id]:'symbol'
};
let array = Object.getOwnPropertySymbols(obj);
console.log(array); //[Symbol(id)]
console.log(obj[array[0]]); //'symbol'
  • 全局注册并登记的方法: Symbol.for() :有时候希望能够多次使用同一个symbol值的情况。
1
2
3
let name1 = Symbol.for('name'); //检测到未创建后新建
let name2 = Symbol.for('name'); //检测到已创建后返回
console.log(name1 === name2); // true
  • 通过symbol对象获取到参数值:Symbol.keyFor()
1
2
3
4
let name1 = Symbol.for('name');
let name2 = Symbol.for('name');
console.log(Symbol.keyFor(name1)); // 'name'
console.log(Symbol.keyFor(name2)); // 'name'

引用数据类型

堆(heap):用来保存栈中简单数据字段对指针的引用

存储复杂类型,一般由程序员分配释放,若程序员不释放,由垃圾回收机制 回收;

复杂数据类型引用放在栈里面,实际数据存放到堆里面。

  • Object
  • Array
  • Function
  • Date
  • RegExp
  • Map
  • Set
  • WeakMap
  • WeakSet
  • Promise
  • Error
  • Blob
  • File
  • ArrayBuffer
  • . . .

包装类型

在JavaScript中,一些操作并不直接适用于原始类型的值,而是需要将它们转换为相应的对象。这就引入了”包装类”的概念。这些包装类是由JavaScript自动创建的临时对象,用于实现对原始值进行操作。

一般来说,有三种包装类:NumberStringBoolean当我们使用原始数据类型调用相关属性或方法时,JavaScript会 自动将 其转换为相应的包装对象,以便于对其进行操作。

所有的包装类都有 valueOf() toString() 方法,除了Math 和 Error 对象没有valueOf方法

包装类才有组包与拆包

组包与拆包

组包是由简单变复杂,拆包是由复杂变简单。

组包

基本数据类型 转成 引用数据 类型

  • 显式组包:new 包装类()
  • 隐式组包:
1
2
let test=1;
test.toString(); //'1' 这个过程就有隐式组包,因为只有对象才有方法

拆包

引用数据 类型 转成 基本数据 类型

  • 调用 包装类.valueOf()

  • 隐式组包 和 隐式拆包:

    • let test = 1; test.toString(); // '1' 组包 test = new Number(test);
    • typeof test;// number 隐式拆包了
  • 显式组包 和 显式拆包:

    • test = new Number(test);// 显式组包
    • test = test.valueof();//显式拆包

Boolean

  • toString() 方法返回指定的布尔对象的字符串形式。
1
2
3
4
let flag1 = new Boolean(true);
console.log(flag1.toString()); // "true"
let flag2 = new Boolean(1);
console.log(flag2.toString());// "true"
  • valueOf() 方法返回一个Boolean对象的原始值
1
2
3
4
let x = new Boolean();
console.log(x.valueOf());// false
let y = new Boolean("Mozilla");
console.log(y.valueOf());// true

Number

一些方法和Global上的方法功能一样,一般都是用Global上的方法即可。

1
2
3
4
//例如: Number.parseInt() 、Number.parseFloat()
let num = '23a3';
Number.parseInt(num) // 23
parseInt(num); // 23, Global上的parseInt()

String

String包装类 隐式 或 显式 组包以后可以使用一些String原型上的一些api:

  • charAt(index) 方法从一个字符串中返回指定的字符。

    • index默认为0
    • index大于字符串总长度,返回空字符串
  • indexOf(searchValue[, fromIndex]) 查找字符串中是否存在某个字符串,并返回所在索引 。

  • fromIndex 默认为0,从某个位置开始查找

    • 如果未找到该值,则返回-1
  • lastIndexOfindexOf类似,是查找最后一次出现的索引位置

  • slice(begin[, end]) 方法提取一个字符串的一部分,并返回一新的字符串。

    • begin 开始位置
    • end结束位置,默认值是从begin一直到字符串结束
  • concat将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。

  • toUpperCase() 将调用该方法的字符串值转换为大写形式,并返回。

  • toLowerCase() 会将调用该方法的字符串值转为小写形式,并返回。

  • trim() 方法会从一个字符串的两端删除空白字符

类型转换

JS中只存在 三种 类型转换

  • to string
  • to boolean
  • to number

然后具体到不同的数据类型向这三种类型转换,如原始数据类型(string, number, boolean, undefined, null) 和引用数据类型(object)

按转换类型划分

其他类型转成Number

数据类型 转为值
String 纯数字的字符串转为对应的数字,空字符串和空格字符串是0,其他字符变成NaN
Boolean true为1,false为0
Object 调用valueOf()的返回结果,没有则返回toString()结果,若对象没有valueOf()toString()则返回NaN
null 返回0
undefined 返回NaN

Number([])返回0Number([3])返回3Number([1, 2])返回NaN

显式转换

  • Number():可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回 NaN
  • parseInt()/parseFloat()parseFloat 会解析第一个 . 遇到第二个 . 或者非数字结束,如果解析的内容里只有整数,解析成整数
1
2
3
4
5
6
7
let a = '12.3px'
console.log(parseInt(a)); // 12
console.log(parseFloat(a)); // 12.3

let b = 'abc2.3'
console.log(parseInt(a)); // NaN
console.log(parseFloat(a)); // NaN

隐式转换

isNaN() 函数用于判断是否是一个非数字类型

  • -,*,/,%,**(除了+)都可以将其他类型在和数字进行运算时转成数字类型
1
2
3
4
5
console.log('2' - 1) // 1
console.log('2' * 1) // 2
console.log('2' / 1) // 2
console.log('2' % 1) // 0
console.log('2' ** 1) // 2
  • +和-(-转过来是负的) 都可以把其他类型(尤其是字符串)转成数字类型
1
2
3
console.log(+'21') // 21
console.log(+true) // 1
console.log(-true) // -1

其他类型转成String

数据类型 转为值
Number 转为对应数字的字符串形式,NaN“NaN”
Boolean true"true"false"false"
Object 返回toString()的返回值,默认是“[object Object]”
null 返回"null"
undefined 返回"undefined"

显式转换

  • toString(), 注意,不可以转 null underfined ,但NaN可以转NaN.toString()结果是'NaN'
  • String(),都可以转

隐式转换

  • 用“字符串(通常是空字符串) + ”拼接,如:12+''会转成字符串'12'

其他类型转成Boolean

数据类型 转为true的值 转为false的值
Number 任何非零数字(包括无穷大) 0-0NaN
String 任何非空字符 “”(空字符串)
Object 任何对象
null null
undefined undefined
  • 转为false的:

    • "" 空字符串
    • 0 || -0 || 0n各种0
    • null
    • undefined
    • NaN
  • 其余都是true,注意'0', '<一到多个空格>' 也是true哦,它们不是空字符串。

显式转换

  • Boolean() 都可以转,包括null undefined NaN (这三个都是false)
  • !! :经过两次取反可以将一个值转成布尔类型。如!!null的结果是false

隐式转换

ifwhile,逻辑运算符或逻辑判断时会自动转成Boolean。

显式转换

to string的原始类型转换

  • toString(), 注意,不可以转 null underfined ,但NaN可以转NaN.toString()结果是'NaN'
1
2
3
4
5
6
7
8
console.log(1..toString()) // '1'  
// 普通的number类型需要加两个点..来调用toString()方法,第一个点对被当成小数点
console.log(1n.toString()) // '1'
// BIgInt类型则不需要加两个点,因为它只能表示整数
console.log(true.toString()) // 'true'
console.log(NaN.toString()) // 'NaN'
console.log(null.toString()) // 报错
console.log(undefined.toString()) // 报错
  • String(),都可以转
1
2
3
4
5
6
console.log(String(1)) // '1'  
console.log(String(1n)) // '1'
console.log(String(true)) // 'true'
console.log(String(NaN)) // 'NaN'
console.log(String(null)) // 'null'
console.log(String(undefined)) // 'undefined'

to number的原始类型转换

原始类型 类型转换规则
string (""空字符串, "<空白符>", "<多个空白符>") -> 0 ; "number"纯数字字符串 -> number"<16进制数>" -> <16 进制数> -> 十进制数; 其他字符串 → NaN
number 无需转换(NaN->NaN)
boolean true -> 1, false -> 0
null 0
undefined NaN
  • Number():可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回 NaN
  • parseInt()/parseFloat()parseFloat 会解析第一个 . 遇到第二个 . 或者非数字结束,如果解析的内容里只有整数,解析成整数

to boolean的原始类型转换

  • 转换为false

    • ""
    • 0 || -0 || 0n
    • null
    • undefined
    • NaN
  • 转换为true

    • 非空字符串
    • 0 和非NaN 的数值

对象 to {string, number, boolean}的 To Primitive类型转换

ES5版本, 当我们显式或者隐式地将对象转换为原始值(primitive value)的时候, 通常是默认调用对象自身或者原型链上的toString() 或者valueOf() 方法, 将其转换原始值. 或者可以自定义覆盖对象的这两个方法来控制对象的to primitive行为, 不过不建议这样做.

ES6 为开发者提供了官方的[Symbol.toPrimitve] 接口来自定义对象的to primitive操作, 当对象发生显式或者隐式类型转换操作的时候, 会自动调用其预先定义的[Symbol.toPrimitive] 方法, 同时会忽略对象自身的toString()valueOf()方法.

toString()valueOf()方法, 这两个方法可以自定义,也可以是原型方法.。一些JavaScript的内置对象会有自己的内置toString()valueOf()方法, 位于其原型上, 汇总如下:

类型 toString valueOf
object [object <type>] 指向自身
function 函数的字符串形式 指向自身
array arr[0], arr[1], ...“ 或者 ""(数组为空) 指向自身
date 包含本地时间信息的字符串 从1970年 1 月 1 日开始至今的毫秒数
regexp 正则表达式的字符串表示形式 指向自身
error 错误名+错误信息: “<err>.name:<err>.message 指向自身

总结只有Date重写了自身的valueOf()方法,其余都是返回自身除了NumberBooleanStringArrayDateRegExpFunction这几种实例化对象之外,其他对象返回的都是该对象的类,都是继承的Object.prototype.toString方法。

1
2
3
4
let obj = new Object({});
obj.toString(); // "[object Object]"

Math.toString(); // "[object Math]"

对象 to boolean

一般情况下, 对象to boolean都是直接转换为 true , 而且不会调用对象的 [Symbol.toPrimitive], toString, valueOf 这三个方法。

注意: document.all对象在浏览器中转为布尔值的时候为false, 这是一种极特殊情况,在vscode中使用这个对象的时候,会提示已经被弃用了,了解即可。

对象to string | number

ES5时的转换方式

在ES5的时候, 对象的类型转换是通过内置或者自定义的toString, valueOf 方法进行to primitive类型转换的。

对象to string
  1. 调用对象的toString() 方法:首先,尝试调用对象的toString()方法。

  2. 检查toString() 的返回值

    • 如果toString()方法返回一个原始值(如字符串、数字、布尔值等),则将这个原始值用作对象的字符串表示形式。
    • 如果toString()方法返回一个对象,那么程序将进入下一步。
  3. 调用对象的valueOf() 方法:如果toString()方法返回一个对象,则尝试调用对象的valueOf()方法。

  4. 检查valueOf() 的返回值

    • 如果valueOf()方法返回一个原始值,那么这个原始值将被用作对象的字符串表示形式。
    • 如果valueOf()方法返回一个对象,那么JavaScript将会抛出一个错误,因为无法将对象转换为字符串。

总结来说,对象到字符串的转换遵循以下顺序:

  1. 调用对象的toString()方法。
  2. 如果toString()返回原始值,使用该值。
  3. 如果toString()返回对象,调用对象的valueOf()方法。
  4. 如果valueOf()返回原始值,使用该值。
  5. 如果valueOf()返回对象,抛出错误。
对象to number

顺序和对象to string相反。

  1. 调用对象的valueOf() 方法:首先,尝试调用对象的valueOf()方法。

  2. 检查valueOf() 的返回值

    • 如果valueOf()方法返回一个原始值(如字符串、数字、布尔值等),则将这个原始值用作对象的数字表示形式。
    • 如果valueOf()方法返回一个对象,那么程序将进入下一步。
  3. 调用对象的toString() 方法:如果valueOf()方法返回一个对象,则尝试调用对象的toString()方法。

  4. 检查toString() 的返回值

    • 如果toString()方法返回一个原始值,那么这个原始值将被用作对象的数字表示形式。
    • 如果toString()方法返回一个对象,那么JavaScript将会抛出一个错误,因为无法将对象转换为数字。

总结来说,对象到数字的转换遵循以下顺序:

  1. 调用对象的valueOf()方法。
  2. 如果valueOf()返回原始值,使用该值进行原始值到数字的转换。
  3. 如果valueOf()返回对象,调用对象的toString()方法。
  4. 如果toString()返回原始值,使用该值进行原始值到数字的转换。
  5. 如果toString()返回对象,抛出错误。
ES6的转换方式

在ES6, 开发者可以通过官方提供的 [Symbol.toPrimitive] 接口去定义对象 to primitive 的行为。 需要注意的是, 如果自定义了对象的 [Symbol.toPrimitive] 的方法, 那么, 当对象发生 to primitive类型转换的时候, 那么只会调用 [Symbol.toPrimitive] 方法, 而无视ES5中提供的toString() , valueOf() 方法

比如下面将对象显式转换为string的代码中, 只有 [Symbol.toPrimitive]() 会被调用:如果返回结果为对象, 则直接报错(不会去调用toString或者是valueOf方法); 如果返回结果为原始值, 则将将该原始值进行to string操作, 作为最终的对象 to primitive结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const obj = {
[Symbol.toPrimitive] () {
console.log("to primitive called") // to primitive called
// return {} error!!!
return 1
},

toString() {
console.log("to string called")
},

valueOf() {
console.log("value of called")
}

}

String(obj) //输出: to primitive called '1'

隐式转换

对于某些运算符, 当A B的时候, 如果A和B类型不一致, 那么将会触发隐式类型转换, 这些运算符汇总如下:

  • 宽松相等运算符==, !=
  • 关系运算符(>, <, <=, >=)
  • 逻辑运算符(&&, ||, !)
  • if, while, for, ? : + (condition)中的条件表达式(condition)
  • 加性运算符 +
  • 算数运算符(-, *, /, %)
  • 一元 +, - 操作

一元加号,减号

  • +即强制转换为Number类型。直接参考显示类型转换中的to number
  • -+类似,不过会将结果取负号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
+"1"  // 1
+true //1
+"" //0
+"1c" //NaN
+undefined //NaN
+null //0
+{a:1} //NaN

-"1" // -1
-"" // -0
+"1c" //NaN
-undefined //NaN
-null //-0
-{a:1} //NaN

加法

在求值时,它首先将两个操作数强制转换为基本类型。然后,检查两个操作数的类型:

  • 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。
  • 如果双方都是 BigInt,则执行 BigInt 加法。如果一方是 BigInt 而另一方不是,会抛出 TypeError
  • 否则,双方都会被转换为数字,执行数字加法。
1
2
3
4
5
6
1 + "1"  // "11"
1 + 1n // 报错
true + 1n // 报错
1+{a:1} // "1[object Object]"
//1.{a:1}转换为 "1[object Object]"
//1 + "[object Object]" ,出现字符串,进行字符串拼接

减法

减法不涉及字符串拼接,故需要都转换为Number类型。

  • 如果有一个操作数为stringbooleannullundefined中一种,则在后台调用Number()将其转化为数值,再进行减法运算。
  • 如果有一个操作数是对象 ,则调用对象的 valueOf() 方法取得表示该对象的数值。如果得到的值是NaN,减法结果是NaN如果对象没有valueOf()方法,调用它的toString()方法,并将得到的字符串转化为数值。 (参考上文一元加号的运算法则)
1
2
3
4
5
6
7
8
9
10
[]-1 	//-1  过程[]->''->0  即相当于 0-1
[0]-1 //-1
[0,1]-1 //NaN 过程[0,1]->"0,1"->NaN 即相当于 NaN-1

const obj = {
valueOf(){
return 0
{
}
console.log(obj - 1) // -1

相等判断

==相等

注意:特例NaN不等于自身

ECMAScript® 2023 Language Specification (ecma-international.org) 对于==的规定:

  1. Type(x)Type(y) 相同,则进行严格相等 (===) 的比较。

  2. xnullyundefined,返回 true

  3. xundefinedynull,返回 true

  4. 这里只需记住 document.allnullundefined,返回 true 即可。

  5. Type(x)NumberType(y)String,返回比较 x == ToNumber(y) 的结果。

  6. Type(x)StringType(y)Number,返回比较 ToNumber(x) == y 的结果。

  7. Type(x)StringType(y)BigInt

    • 使用与 BigInt() 构造函数相同的算法将字符串转换为 BigInt。如果转换失败,返回 false,然后再进行比较。
  8. 和第7步类似(顺序交换即可):

    • Type(x)BigIntType(y)String,尝试将字符串 y 转换为 BigInt,转换成功则比较两个 BigInt 值。
  9. Type(x)Boolean,返回比较 ToNumber(x) == y 的结果。

  10. Type(y)Boolean,返回比较 x == ToNumber(y) 的结果。

  11. Type(x)StringNumber,且 Type(y)Object,返回比较 x == ToPrimitive(y) 的结果。

  12. Type(x)ObjectType(y)StringNumber,返回比较 ToPrimitive(x) == y 的结果。

  13. BigIntNumber 类型比较:
    a. 如果 Number 类型的值为无穷值(Infinity-Infinity),直接返回 false
    b. 如果两个值转换为数学上的值相同,返回 true。(数学上 -0+0 均为 0

  14. 返回 false

===全等

类型必须相同。

NaN不等于自身。

1
2
3
4
5
null == undefined  //true
0 == false //true

null===undefined //false
0 === false //false

Object.is()

全等的缺陷:NaN === NaN //false-0 === +0 //true

1
2
Object.is(NaN,NaN) //true
Object.is(-0,+0) //false

Object.is() ===的区别:

  • 处理 0 的特殊情况:如果 ab 严格等于 0,函数会检查 1/a 是否等于 1/b。这是因为 1/0 的结果是Infinity,而 1/-0 的结果是-Infinity,这可以区分 +0-0
  • 处理 NaN 的特殊情况:如果 ab 都不等于它们自己(这是 NaN 的一个特性),则函数返回 true,表示两个值都是 NaN
  • 一般比较:如果上述两种特殊情况都不满足,函数使用 === 运算符进行比较,这是一种严格相等的比较。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//实现Object.is()
//可观察Object.is()与===的区别
function equal(a,b) {
// 出现0、+0、-0
if(a===0||b===0){
return 1/a === 1/b
}
// 均为NaN
if(a!==a&&b!==b){
return true
}

return a===b
}

面试题

题目一

console.log([] == !{})结果是?

  1. ! 运算符优先级高于==,故先进行!运算。
  2. !{}运算结果为false,结果变成 [] == false比较。
  3. 根据4.1中的规则(引用类型与基本类型比较),等式左边ToPrimitive([]) == ''。按照上面规则进行原始值转换,[]会先调用valueOf函数,返回this。不是原始值,继续调用toString方法,x = [].toString() = ''。 故结果为 '' == 0比较。
  4. 等式左边为string,右边为number,等式左边x = ToNumber('') = 0。所以结果变为: 0 == 0,打印true,比较结束。

题目二

let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;result的结果是?

1
2
3
4
5
6
7
8
9
10
11
//拆解
100 + true //100 + 1 = 101
101 + 21.2 // 122.2
122.2 + null // 122.2 + 0
122.2 + undefined //NaN
NaN + "Tencent" // "NaNTencent"
"NaNTencent" + [] // "NaNTencent" + ""
"NaNTencent" + null // "NaNTencent" + "null"
"NaNTencentnull" + 9 // "NaNTencentnull9"
"NaNTencentnull9" + false // "NaNTencentnull9false"
// result的结果是:"NaNTencentnull9false"

题目三

1
2
3
4
5
6
7
8
9
let arr= []
arr[0] = 1
arr["0"] = 2
arr["1"] = 3
arr[-1] = 4
arr[{}] = 5

console.log(arr);
// 结果是?
  • 知识点1:在js中,数组底层也是通过键值对存储的。当我们以索引形式为数组添加元素时,会发生以下过程。自动将传入的索引值转换为CanonicalNumericIndexString (规范数字索引字符串)

    1. 将索引转换为数值类型
    2. 再将索引转换为字符串类型
    3. 最终添加至数组
  • arr[0]arr["0"]事实上等价

  • 知识点2:数组也是一个对象,可以添加属性。数组的索引范围为 $[0,2^{32}-1)$ ,而向对象中添加属性时,若不在此范围,就相当于向其身上添加属性。


在JavaScript中,数组的索引通常是数字,并且它们被用来按顺序存储元素。然而,JavaScript对象(包括数组)的键(即索引)可以是字符串或任何值,只要它能够被转换为字符串(除了 undefined),这就意味着你可以使用非数字的键来设置数组的元素。

  • let arr = [];这行代码创建了一个空数组 arr
  • arr[0] = 1;这里将数字 1 赋值给数组的第一个元素,使用数字 0 作为索引。
  • arr["0"] = 2;这行代码将数字 2 赋值给数组的第二个元素,使用字符串 "0" 作为索引。在JavaScript中,字符串索引和数字索引是等价的,所以这实际上是创建了数组的第二个元素。
  • arr["1"] = 3;这里将数字 3 赋值给数组的第三个元素,使用字符串 "1" 作为索引。同样,字符串索引会被转换为数字索引。
  • arr[-1] = 4;这行代码将数字 4 赋值给数组的一个特殊索引 -1。在大多数情况下,数组的索引不能为负数,但在这里,由于使用了方括号语法,JavaScript 允许将任何值(包括负数)作为索引。这将创建一个索引为 -1 的属性,但它不是数组的常规索引,并且不会影响数组的 length 属性。
  • arr[{}] = 5;这行代码尝试将数字 5 赋值给一个对象作为索引。在JavaScript中,对象作为索引时会被转换为字符串 [object Object],因此这将创建一个非数字索引的属性,其值为 5
  • 最终,当你打印 arr 时:console.log(arr);

你将得到一个包含多个元素和属性的数组对象,其中一些是数组的索引,一些是对象的属性。数组的 length 属性将反映其索引数组的长度,即 [0, 3]。这是因为 arr["0"]arr[0] 引用相同的位置,而 arr["1"] 会创建一个新的索引为 1 的元素,使得数组长度变为 2arr[-1]arr[{}] 不会影响数组的 length,但它们会被添加为对象的属性。

请注意,数组的 length2,因为只有索引 01 被赋值,而 -1{} 创建的是非标准索引,它们不会出现在数组的索引序列中,但作为对象属性存在。

类型判断

typeof

typeof是一元运算符,可以用来返回一个变量或表达式的数据类型。它可以准确地判断除null之外的原始类型,并且可以判断function类型。

但是没法用来区分引用数据类型的,因为所有的引用数据类型都会返回”object”

注意: null NaN document.all 的判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
console.log(typeof "hello"); // 输出: "string"
console.log(typeof 123); // 输出: "number"
console.log(typeof true); // 输出: "boolean"
console.log(typeof undefined); // 输出: "undefined"
console.log(typeof null); // 输出: "object" (这是一个历史遗留问题)一个BUG
console.log(typeof NaN) // 输出: "number"
console.log(typeof document.all) // 输出: "undefined"
console.log(typeof Symbol(123))//输出: "symbol"
console.log(typeof 123n); // 输出: "bigint"

console.log(typeof function(){}); // 输出: "function"

console.log(typeof {}) // 输出: "object"
console.log(typeof []) // 输出: "object"
console.log(typeof new Set()) // 输出: "object"
console.log(typeof new Map()) // 输出: "object"

instanceOf

语法:obj instanceof Type

判断 obj 是不是 Type 类的实例 ,只可用来判断引用数据, 右操作数必须是函数或者 class

用于判断一个对象是否是某个构造函数(或其子类)的实例,只能判断引用类型。它通过原型链的查找来判断对象的类型

手写instanceOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function myInstanceof(Fn, obj) {
// 获取该函数显示原型
const prototype = Fn.prototype;
// 获取obj的隐式原型
let proto = obj.__proto__;
// 遍历原型链
while (proto) {
// 检测原型是否相等
if (proto === prototype) {
return true;
}
// 如果不等于则继续往深处查找
proto = proto.__proto__;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
let arr = [1, 2, 3];
console.log(arr instanceof Array); // 输出: true
console.log(arr instanceof Object); // 输出: true
console.log(arr instanceof RegExp); // 输出: false

function Person() {}
let p = new Person();
console.log(p instanceof Person); // 输出: true
console.log(p instanceof Object); // 输出: true
console.log(p instanceof Array); // 输出: false

Object.prototype.toString.call()

利用函数动态 this 的特性

Object.prototype.toString()方法可以返回一个对象的字符串表示形式。它会读取数据结构内部属性来读取数据的类型 class。通过调用call() 方法,可以改变toString() this的指向,使其返回 "[object 类型]" 的形式,其中 类型可以是任意数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Object.prototype.toString.call(123); // "[object Number]"
Object.prototype.toString.call("hello"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(function(){}); // "[object Function]"
// 注意的是,Object.prototype.toString.call 方法返回的字符串格式为 "[object 类型]"

// 封装
function myTypeOf(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}

// 测试
console.log(myTypeOf(1)); // Number
console.log(myTypeOf("1")); // String
console.log(myTypeOf(true)); // Boolean
console.log(myTypeOf(null)); // Null
console.log(myTypeOf(undefined)); // Undefined
console.log(myTypeOf(Symbol(1))); // Symbol
console.log(myTypeOf({})); // Object
console.log(myTypeOf([])); // Array
console.log(myTypeOf(function () {})); // Function
console.log(myTypeOf(new Date())); // Date
console.log(myTypeOf(new RegExp())); // RegExp

constructor

constructor 作用和 instanceof 非常相似。但 constructor 检测 Object 与 instanceof 不一样,还可以处理 基本数据类型 的检测。

constructor 指向创建 该实例对象的构造函数

注意: nullundefined 没有 constructor,以及 constructor 可以被改写,不太可靠

1
2
3
4
5
6
7
8
9
10
11
12
13
const arr = [1, 2, 3];
console.log(arr.constructor === Array) // true

const obj = {name: "云牧", age: 18};
console.log(obj.constructor === Object) // true


String.prototype.constructor = function fn() {
return {};
}

// constructor 可以被改写
console.log("云牧".constructor) // [Function: fn]

isxxx

isPrototypeof()

用于判断一个对象是否为另一个对象的原型。

prototypeObj.isPrototypeOf(object),如果 prototypeObjobject 的原型对象,isPrototypeOf 方法返回 true,否则返回 false

功能基本等同于 instanceof

注意:isPrototypeOf 方法只能用于判断对象类型,不能用于判断基本数据类型。如果 prototypeObj 不是一个对象,isPrototypeOf 方法会抛出 TypeError 异常。

1
2
3
4
5
6
7
8
9
10
11
12
const obj = { name: "云牧", age: 18 }
const arr = [1, 2, 3]

const proto1 = Object.getPrototypeOf(obj)
console.log(proto1.isPrototypeOf(obj)) // true

const proto2 = Object.getPrototypeOf(arr)
console.log(proto2.isPrototypeOf(arr)) // true

console.log(Object.isPrototypeOf({})) // false
console.log(Object.prototype.isPrototypeOf({}))
// true 期望左操作数是一个原型,{} 原型链能找到 Object.prototype

getPrototypeOf()

getPrototypeOf() 返回一个对象的原型,只能用于判断对象类型。

1
2
3
4
const obj = { name: "云牧", age: 18 }
const arr = [1, 2, 3]
console.log(Object.getPrototypeOf(obj) === Object.prototype) // true
console.log(Object.getPrototypeOf(arr) === Array.prototype) // true

Array.isArray()

Array.isArray()方法用于判断一个对象是否为数组,且只能判断数组类型

1
2
3
console.log(Array.isArray([1, 2, 3])); // 输出: true
console.log(Array.isArray({length: 3, 0: "a", 1: "b", 2: "c"})); // 输出: false 这是一个类数组对象
console.log(Array.isArray("hello")); // 输出: false

Number.isNaN()

Number.isNaN()方法用于判断是否为NaN

1
2
3
4
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN('NaN')) // false
console.log(Number.isNaN(null))// false
console.log(Number.isNaN(undefined))// false

加餐:ES6中NaN的加强

NaN 和 Number.NaN 特点
  • typeof 判断类型是数字number
  • 自己不等于自己
isNaN

如果非数字,隐式转换传入结果如果是 NaN,就返回 true,反之返回 false

1
2
console.log(isNaN(NaN)) // true
console.log(isNaN({})) // true
Number.isNaN

判断一个值是否是数字,并且值是否等于 NaN

1
2
3
4
5
6
7
8
9
console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN({})) // false

// 综合垫片(如果不支持 Number.isNaN 的话)
if (!("isNaN" in Number)) {
Number.isNaN = function (val) {
return typeof val === "number" && isNaN(val)
};
}
indexOf 和 includes

indexOf 不可查找 NaN, includes 则可以查找 NaN

1
2
3
4
const arr = [NaN]

console.log(arr.indexOf(NaN)) // -1
console.log(arr.includes(NaN)) // true

Number.isFinite()

Number.isFinite() 可以判断一个值是否为有限数

1
2
3
console.log(Number.isFinite(123)); // true
console.log(Number.isFinite("hello")); // false
console.log(Number.isFinite(Infinity)); // false

鸭子类型检测

检查自身属性的类型或者执行结果的类型

通常作为候选方案

1
2
3
4
5
6
7
8
9
const isObject = value =>
value !== null && (typeof value === "object" || typeof value === "function");

export default function isPromise(value) {
return (
value instanceof Promise ||
(isObject(value) && typeof value.then === "function" && typeof value.catch === "function")
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function kindof(obj) {
var type;
if (obj === undefined) return "undefined";
if (obj === null) return "null";

switch ((type = typeof obj)) {
case "object":
switch (Object.prototype.toString.call(obj)) {
case "[object RegExp]":
return "regexp";
case "[object Date]":
return "date";
case "[object Array]":
return "array";
}

default:
return type;
}
}

Symbol.toStringTag

  • 原理:Object.prototype.toString 会读取该值
  • 适用场景:需自定义类型
  • 注意事项:兼容性
1
2
3
4
5
6
7
8
class MyArray {
get [Symbol.toStringTag]() {
return "MyArray";
}
}

const arr = new MyArray();
console.log(Object.prototype.toString.call(arr)); // [object MyArray]

等比较

原理:与某个固定值进行比较

适用场景:undefinedwindowdocumentnull

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
_.isNull = function(obj) {
return obj === null;
};

_.isUndefined = function(obj) {
//void 0可以返回一个安全的undefined
return obj === void 0;
};

_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};

_.isString = function(obj) {
return toString.call(obj) === '[object String]';
};

_.isNumber = function(obj) {
return toString.call(obj) === '[object Number]';
};

_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};

_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};

_.isFunction = function(obj) {
return typeof obj === 'function' || false;
};

void 0 始终返回 undefinedvoid 后面接任意值都是返回 undefined, 这是为了兼容 IE,因为在 IE 中 undefined 值可以被改写