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

本文详细解析了JavaScript中的运算符行为,特别是+运算符在处理不同数据类型时的转换规则,涉及bigint、primitivevalue、object、Symbol等类型,并讨论了const和箭头函数的新手知识点。
摘要由CSDN通过智能技术生成

如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

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

  1. 叙述+操作符的计算规则

  2. bigint类型的作用

  3. 对比扩展运算符和Object.assign

  4. 说说const的原理及其可修改性

  5. 如果尝试new一个箭头函数会怎么样

下面是我的一些理解:

1.1 与数字做加法隐式转换规则

测试程序如下:

const a = [undefined, null, 0, '3', 123n, Symbol(0), true, {}];

function testPrimitiveValue(_x){
    _x.forEach(i=>{
        try {console.log(2+i)} catch(e) {console.log(e)}
    })
}

function testValueOf(_x){
    _x.forEach(i=>{
        try {console.log(2+{valueOf(){return i}})} catch(e) {console.log(e)}
    })
}

function testToString(_x){
    _x.forEach(i=>{
        try {console.log(2+{toString(){return i}})} catch(e) {console.log(e)}
    })
}

function testPropToString(_x){
    _x.forEach(i=>{
        try {console.log(2+{})} catch(e) {console.log(e)}
    })
}



    NaN
    2
    2
    23
    Cannot mix BigInt and other types, use explicit conversions
    Cannot convert a Symbol value to a number
    3
    2[object Object]
 */



    NaN
    2
    2
    23
    TypeError: Cannot mix BigInt and other types, use explicit conversions
    TypeError: Cannot convert a Symbol value to a number
    3
    2[object Object]
 */



    NaN
    2
    2
    23
    Cannot mix BigInt and other types, use explicit conversions
    Cannot convert a Symbol value to a number
    3
    Cannot convert object to primitive value
 */



    2[object Object]
    2[object Object]
    2[object Object]
    2[object Object]
    2[object Object]
    2[object Object]
    2[object Object]
    2[object Object]
 */



结论

与数字做加法运算,会尝试将另外一个操作数转成number类型:

  1. 如果另外一个操作数是primitive value则:

  • undefined -> NaN

  • null -> 0

  • number -> 不用转换

  • string -> 优先级高于number不会转换(反而是另一个number操作数会被转成string类型的)

  • bigint -> 仍为bigint(而不是number),证明: console.log(2n+{valueOf(){return 1n}}); // 3n

  • symbol -> 报错:Cannot convert a Symbol value to a number

  • boolean -> 0或者1

  • object -> ↓↓↓↓↓↓

  1. 如果另外一个操作数是object则检查valueOf方法的返回值:

  • undefined -> NaN

  • null -> 0

  • number -> 不用转换

  • string -> 优先级高于number不会转换(反而是另一个number操作数会被转成string类型的)

  • bigint -> 仍为bigint(而不是number),证明: console.log(2n+{valueOf(){return 1n}}); // 3n

  • symbol -> 报错:Cannot convert a Symbol value to a number

  • boolean -> 0或者1

  • object -> ↓↓↓↓↓↓

  1. 如果另外一个操作数是object并且valueOf方法不存在则检查toString方法的返回值:

  • undefined -> NaN

  • null -> 0

  • number -> 不用转换

  • string -> 优先级高于number不会转换(反而是另一个number操作数会被转成string类型的)

  • bigint -> 仍为bigint(而不是number),证明: console.log(2n+{valueOf(){return 1n}}); // 3n

  • symbol -> 报错:Cannot convert a Symbol value to a number

  • boolean -> 0或者1

  • object -> Cannot convert object to primitive value 可以看出来,toString方法的返回指检查和valueOf返回值的检查方法基本上一致,但是如果toString方法还是返回引用类型的话,那就是给机会不中用了!

  1. 如果另外一个操作数是object并且valueOf、toString方法都不存在 这种情况下返回值出奇的统一就是将2和Object.prototype.toString.call(x)的拼接值

总结一下: 如果另外一个操作数x是primitive value则x一定会向number类型尝试转换,记住每一种转换的结果即可; 如果x是引用类型的,则先查valueOf的存在性和其返回值,再查toString的存在性和返回值,最后由Object.prototype.toString.call(x)结束。

1.2 与字符串做加法隐式转换规则

测试程序如下:

const a = [undefined, null, 0, '3', 123n, Symbol(0), true, {}];

function testPrimitiveValue(_x){
    _x.forEach(i=>{
        try {console.log(""+{valueOf(){return i}})} catch(e) {console.log(e)}
    })
}

function testValueOf(_x){
    _x.forEach(i=>{
        try {console.log(""+{valueOf(){return i}})} catch(e) {console.log(e)}
    })
}

function testToString(_x){
    _x.forEach(i=>{
        try {console.log(""+{toString(){return i}})} catch(e) {console.log(e)}
    })
}

function testPropToString(_x){
    _x.forEach(i=>{
        try {console.log(""+{})} catch(e) {console.log(e)}
    })
}



    undefined
    null
    0
    3
    123
    Cannot convert a Symbol value to a string
    true
    [object Object]
 */



    undefined
    null
    0
    3
    123
    Cannot convert a Symbol value to a string
    true
    [object Object]
 */



    undefined
    null
    0
    3
    123
    Cannot convert a Symbol value to a string
    true
    Cannot convert object to primitive value
 */



    [object Object]
    [object Object]
    [object Object]
    [object Object]
    [object Object]
    [object Object]
    [object Object]
    [object Object]
 */



结论

与字符串做加法运算,会尝试将另外一个操作数转成string类型:

  1. 如果另外一个操作数是primitive value则:

  • undefined -> 'undefined'

  • null -> 'null'

  • number -> '0'

  • string -> 不用转变

  • bigint -> '123' n不见了

  • symbol -> Cannot convert a Symbol value to a string

  • boolean -> 'true'

  • object -> ↓↓↓↓↓↓

  1. 如果另外一个操作数是object则检查valueOf方法的返回值:

  • undefined -> 'undefined'

  • null -> 'null'

  • number -> '0'

  • string -> 不用转变

  • bigint -> '123' n不见了

  • symbol -> Cannot convert a Symbol value to a string

  • boolean -> 'true'

  • object -> ↓↓↓↓↓↓

  1. 如果另外一个操作数是object并且valueOf方法不存在则检查toString方法的返回值:

  • undefined -> 'undefined'

  • null -> 'null'

  • number -> '0'

  • string -> 不用转变

  • bigint -> '123' n不见了

  • symbol -> Cannot convert a Symbol value to a string

  • boolean -> 'true'

  • object -> Cannot convert object to primitive value 可以看出来,toString方法的返回值检查和valueOf返回值的检查方法基本上一致,但是如果toString方法还是返回引用类型的话,那就是给机会不中用了!

  1. 如果另外一个操作数是object并且valueOf、toString方法都不存在 这种情况下返回值出奇的统一就是将2和Object.prototype.toString.call(x)的拼接值

总结一下: 如果另外一个操作数x是primitive value则x一定会向string类型尝试转换,记住每一种转换的结果即可; 如果x是引用类型的,则先查valueOf的存在性和其返回值,再查toString的存在性和返回值,最后由Object.prototype.toString.call(x)结束。

1.3 非数字,字符串做加法隐式转换规则

测试程序如下:

console.log(true + true); 
console.log(true + undefined); 
console.log(true + null); 
console.log(true + 123n); 
console.log(true + Symbol(0)); 
console.log(true + {}); 
console.log(true + {valueOf(){return 1}}); 
console.log(true + {toString(){return 1}}); 

console.log(undefined + true); 
console.log(undefined + undefined); 
console.log(undefined + null); 
console.log(undefined + 123n); 
console.log(undefined + Symbol(0)); 
console.log(undefined + {}); 
console.log(undefined + {valueOf(){return 1}}); 
console.log(undefined + {toString(){return 1}}); 

console.log(null + true); 
console.log(null + undefined); 
console.log(null + null); 
console.log(null + 123n); 
console.log(null + Symbol(0)); 
console.log(null + {}); 
console.log(null + {valueOf(){return 1}}); 
console.log(null + {toString(){return 1}}); 

console.log({} + true); 
console.log({} + undefined); 
console.log({} + null); 
console.log({} + 123n); 
console.log({} + Symbol(0)); 
console.log({} + {}); 
console.log({} + {valueOf(){return 1}}); 
console.log({} + {toString(){return 1}}); 

完整的结论

  • bigint不能出现在一般的+运算中!

  • 所有值作为+的操作数的时候都会先转成number或者string类型之后再使用转换值计算

  • symbol不能向number或者string隐式转换,所以symbol也不能参与+运算

  • 如果操作数之一为string类型的,或者转化类型之后为string的,则应该按照【与字符串做加法隐式转换规则 】计算

  • 如果操作数之一为number类型的,并且另一个操作数不为string或者转化类型之后的string,则应该按照【与数字做加法隐式转换规则】计算

  • 如果两个操作数均不为number或者string,或者转换之后的类型军部为number或者string,则只有可能是null undefined boolean,它们都是先转成number类型的再计算

  • 引用类型转成number或者string的方式为,依次检查: valueOf -> toString -> Object.prototype.toString.call(x)的返回值。

看到这,不给作者点个赞?不点赞可能记不住哟~

由于js遵循的是IEEE双精度浮点数标准,也就是使用64bit的二进制数去表示一个浮点数,其中1bit为符号位,11bit为指数位,剩下的52位为尾数。

这就造成了52bit最大只能表示绝对值为2^53 - 1的数字,如果将符号位算进来,就是-(2^53 - 1) -> +(2^53 - 1),也就是-2^53+1 -> 2^53-1

这恰好对应了Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER的值。

也就是说超过这个范围,js就只能用浮点数代替了,就会产生舍入误差了。所以ECMAScript 2020才引入bigint这个新的数据类型来表示这个范围之外的整数。

bigint的原理就是使用非固定长度的二进制数表示整数,因为其长度是不固定的,所以bigint的数据不能参与到和number类型的数据的运算过程中去。

  • 相同点:

    • 两者都是浅拷贝

    • 在实现浅拷贝功能的时候,两者都是后面的值覆盖前面的值

    • 都只会复制ownProperty,包括ownPropertyName和ownPropertySymbol,不涉及原型链上的属性

  • 不同点:

    • 浅拷贝只是扩展运算符的众多用途的一种

    • 扩展运算符产生一个新的对象,而Object.assign是往目标对象上合并属性(不会产生新的对象)

  • 此外:{...obj1, ...obj2}相当于Object.assign({}, obj1, obj2)

  • const原理:在变量名与内存地址之间建立不可变的绑定,当尝试改变变量名的内存地址的时候,由于不可变的绑定的存在会报错

  • 可修改性:对于primitive value修改值会报错,对于引用值来说依然不可以改变这种绑定关系,但是可以对其属性尝试性的进行修改:但需要通过Object.isFrozen来判断这个值是否被冻结了,如果被冻结了,其属性依然是不可以修改的。

补充:Object三傻:freeze seal preventExtensions

先手写一个函数实现new函数的功能:

function myNew (constructor, ...rest) {
    
    if(typeof constructor !== "function") throw new Error('constructor must be a function');
    
    const obj = Object.create(constructor.prototype);
    
    const rst = constructor.apply(obj, rest);
    
    return (rst && (Object(rst) === rst)) ? rst : obj;
}

  • 第二步中,箭头函数没有原型对象,所以constructor.prototype不存在,为undefined,Object.create只接受null或者object类型的,所以这里会报错,这是第一个问题;

  • 第三步中,apply内部会用到this,此时的this应该指向constructor,但是constructor是箭头函数没有this,所以this指向错误;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Web面试那些事儿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值