以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?
第三天要刷的面试题如下:
-
叙述js中其他类型的值到字符串类型的转换规则
-
叙述js中其他类型的值到布尔值的转换规则
-
对比操作符 ||, && 和 ??
-
对比 ==, === 和 Object.is,并手写Object.is
-
什么是js中的包装类型,又如何反包装?
下面是我的一些理解:
1. 叙述js中其他类型的值到字符串类型的转换规则
将x转成string类型,可分为隐式转换和强制转换,所谓隐式转换就是指""+x
的结果,所谓强制转换指的就是String(x)
的结果:
-
undefined: "undefined"
-
null: "null"
-
string: 返回原值,注意String("abc")不是包装类型,注意与new String("abc")区别开
-
number: "Infinity", "-Infinity", "NaN", 变成相同的字面量,但是数字太大会变成字符串类型的科学计数法的模样
-
boolean: "true", "false"
-
symbol: 隐式转换报错;强制转换如:
String(Symbol(9)) // "Symbol(9)"
-
bigint: 会变成将n去掉的相同的字面量,不会出现因为数字大变成字面量
-
object: 内部机制
所谓内部机制,指的就是:
- 判断x是否有valueOf方法,如果有的话根据其返回值做出不同的反映:
-
返回值为symbol类型,报错
-
返回值为引用类型, 跳到2
-
返回值y为其它非引用类型,最终结果为:
String(y)
-
- 如果没有valueOf方法,或valueOf返回引用类型,则接着判断是否有toString方法,如果有的话根据其返回值做出不同的反映:
-
返回值为symbol类型,报错
-
返回值为引用类型,
报错
-
返回值y为其它非引用类型的y,最终结果为:
String(y)
-
-
如果没有toString方法,则返回
Object.prototype.toString,call(x)
2. 叙述js中其他类型的值到布尔值的转换规则
js中其他类型的值向布尔类型转换就比较简单了;事实上,js中常见的一共只有8种假值,也就是不能通过if()
判断的值:
-
-
""
-
-
-
false
-
-
-
+0
-
-
-
-0
-
-
-
NaN
-
-
-
null
-
-
-
undefined
-
-
-
Boolean(false)
-
其余的值都能够通过if()
的判断,也就是所谓的真值。下面是一些容易搞混的假值:
-
-
![]
-
-
-
!({})
-
-
-
!123n
-
-
-
!Symbol(0)
-
-
-
!Object(false)
-
3. 对比操作符 ||, && 和 ??
- 共同点:
-
这三个操作符都具有短路的特性
-
??
可以看成是||
的收紧版本,||
对js中的假值短路生效,而??
只对undefined
和null
生效(也就是说console.log(0 ?? 20)
的执行结果为0而不是20)
-
- 不同点:
-
x||y
的特性是,x为假值(或者x表达式的返回值为假值)的时候,返回y的值(或者y表达式的返回值);但是x为真值(或者x表达式的返回值为真值)的时候,不会执行y或者y表达式,就好像||y
不存在一样,直接返回x(或者x表达式的返回值) -
x&&y
的特性是,x为真值(或者x表达式的返回值为真值)的时候,返回y的值(或者y表达式的返回值);但是x为假值(或者x表达式的返回值为真值)的时候,不会执行y或者y表达式,就好像||y
不存在一样,直接返回x(或者x表达式的返回值)
-
4. 对比 ==, === 和 Object.is,并手写Object.is
这个就不用找相同点和不同点了,这三个不能说一模一样吧,只能说是完全不同了,所以分开来单独说:
a == b
的判断机制是:-
-
如果a和b的类型相同(这里指的是typeof作用之后的返回值相同),那么对
非引用值
直接进行比较原始值;而对引用值
比较的是内存地址;
-
-
-
如果a和b的类型不同,则首先判断是否其中有一个是
引用类型的
,如果两个都不是引用类型
,则直接返回false;如果有一个是,则根据js设计规格将两者强制转成同一个非引用类型
进行比较。
-
-
a === b
的判断机制是:-
-
如果a和b的类型不相同,则直接返回false
-
-
-
如果a和b都是
引用
类型的,则比较内存地址
-
-
-
如果a和b都是
非引用
类型的,则比较原始值
-
-
Object.is(a,b)
的判断机制是:-
-
检查是否符合两种特例,如果不符合,则直接返回
a===b
的结果
-
-
-
特例一:a和b都是NaN,此时应该返回true
-
-
-
特例二:a和b都是0,只不过一个为正一个为负,此时需要返回false
-
-
手写Object.is
之前需要补充一个知识点:1 / Infinity === 0;
的结果是true
根据Object.is的判断机制,容易写出其实现:
function myObjectIs (a, b) {
if(Number.isNaN(a) && Number.isNaN(b)) return true;
if(a===0 && b===0 && 1/a!==1/b) return false;
return a===b;
}
有一个坑,那就是需要使用Number.isNaN
而不是isNaN
!
5. 什么是js中的包装类型,又如何反包装?
对于js中的一个变量x而言,如果typeof x
的返回值是C(= "number" | "string" | "boolean"),这意味着x是非引用类型。那么,按照道理来说,x是没有属性和方法的。但是作为使用者,仍然可以通过"abc".length
获得字符串的长度,或者使用123.toString(16)
将number转成字符串格式。
之所以能够这样,在于js中存在着的包装类型。包装类型实际上是一种机制,在使用者做上述操作的时候,js会自动将"number" | "string" | "boolean"
包装成对象,即所谓的包装对象。
这个过程可以表示为:let _x = new C(x);
其中,x是"number" | "string" | "boolean"
类型的,C为构造函数Number | String | Boolean
.
对于包装对象有如下特征:
-
x == _x // true 除过NaN
-
x === _x // false
-
_x.valueOf() === x // true 除过NaN
可以看到,使用包装对象的valueOf方法可以反包装,得到原始值(又称为是Primitive Value)
补充:一般认为js中有六种primitive value
-
undefined
-
null
-
string
-
number
-
boolean
-
symbol
-
至于bigint, 2020年之后才出现的,也算是
2023年10月11日补充:Symbol.toPrimitive
在 JavaScript 中,当一个对象需要转换为原始值时,会按照以下优先级顺序调用方法:
1. 如果对象具有 `Symbol.toPrimitive` 方法,那么将调用它并传递一个表示期望的转换类型的参数。可以是 `"number"`、`"string"` 或 `"default"`。这个方法具有最高的优先级,可以完全控制对象转换为原始值的行为。
2. 如果对象没有 `Symbol.toPrimitive` 方法,或者 `Symbol.toPrimitive` 方法返回一个不合法的值(非原始类型),则会调用 `valueOf` 方法。
3. 如果对象没有 `Symbol.toPrimitive` 方法,或者 `Symbol.toPrimitive` 方法返回一个不合法的值,以及 `valueOf` 方法不存在或者返回一个不合法的值,那么会调用 `toString` 方法。
4. 如果对象没有 `Symbol.toPrimitive` 方法,或者 `Symbol.toPrimitive` 方法返回一个不合法的值,以及 `valueOf` 和 `toString` 方法都不存在,那么会抛出错误。
这个优先级顺序确保了 JavaScript 可以根据对象上存在的方法来进行合适的转换。
下面是一个示例,展示了这些方法的优先级:
const obj = {
[Symbol.toPrimitive](hint) {
if (hint === "number") {
return 42;
}
if (hint === "string") {
return "Hello";
}
return "Default";
},
valueOf() {
return 10;
},
toString() {
return "World";
}
};
console.log(Number(obj)); // 输出: 42
console.log(String(obj)); // 输出: Hello
console.log(obj); // 输出: Default
在上述示例中,`obj` 对象定义了 `Symbol.toPrimitive`、`valueOf` 和 `toString` 方法。根据优先级顺序,首先调用 `Symbol.toPrimitive` 方法并返回期望的原始值,然后调用 `valueOf` 方法并返回值为 10 的原始值,最后调用 `toString` 方法并返回字符串 "World"。
这只是一种一般情况下的优先级顺序。实际行为可能会受到对象的具体实现和使用环境的影响。