每日五道前端面试题--day3

以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

第三天要刷的面试题如下:

  1. 叙述js中其他类型的值到字符串类型的转换规则

  2. 叙述js中其他类型的值到布尔值的转换规则

  3. 对比操作符 ||, && 和 ??

  4. 对比 ==, === 和 Object.is,并手写Object.is

  5. 什么是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: 内部机制

所谓内部机制,指的就是:

  1. 判断x是否有valueOf方法,如果有的话根据其返回值做出不同的反映:
    • 返回值为symbol类型,报错

    • 返回值为引用类型, 跳到2

    • 返回值y为其它非引用类型,最终结果为:String(y)

  2. 如果没有valueOf方法,或valueOf返回引用类型,则接着判断是否有toString方法,如果有的话根据其返回值做出不同的反映:
    • 返回值为symbol类型,报错

    • 返回值为引用类型,报错

    • 返回值y为其它非引用类型的y,最终结果为:String(y)

  3. 如果没有toString方法,则返回Object.prototype.toString,call(x)

2. 叙述js中其他类型的值到布尔值的转换规则

js中其他类型的值向布尔类型转换就比较简单了;事实上,js中常见的一共只有8假值,也就是不能通过if()判断的值:

    1. ""

    1. false

    1. +0

    1. -0

    1. NaN

    1. null

    1. undefined

    1. Boolean(false)

其余的值都能够通过if()的判断,也就是所谓的真值。下面是一些容易搞混的假值:

    1. ![]

    1. !({})

    1. !123n

    1. !Symbol(0)

    1. !Object(false)

3. 对比操作符 ||, && 和 ??

  • 共同点:
    • 这三个操作符都具有短路的特性

    • ??可以看成是||收紧版本||对js中的假值短路生效,而??只对undefinednull生效(也就是说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的判断机制是:
      1. 如果a和b的类型相同(这里指的是typeof作用之后的返回值相同),那么对非引用值直接进行比较原始值;而对引用值比较的是内存地址;

      1. 如果a和b的类型不同,则首先判断是否其中有一个是引用类型的,如果两个都不是引用类型,则直接返回false;如果有一个是,则根据js设计规格将两者强制转成同一个非引用类型进行比较。

  • a === b的判断机制是:
      1. 如果a和b的类型不相同,则直接返回false

      1. 如果a和b都是引用类型的,则比较内存地址

      1. 如果a和b都是非引用类型的,则比较原始值

  • Object.is(a,b)的判断机制是:
      1. 检查是否符合两种特例,如果不符合,则直接返回a===b的结果

      1. 特例一:a和b都是NaN,此时应该返回true

      1. 特例二: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"。

这只是一种一般情况下的优先级顺序。实际行为可能会受到对象的具体实现和使用环境的影响。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值