JavaScript面试题整理(二)

数据类型篇

13、其他值到字符串的转换规则?

  • Null和Undefined类型,null转换为‘null’,undefined转换为‘undefined’
  • Boolean类型,true转换为‘true’,false转换为‘false’
  • Number类型的值直接转换,不过那些极小和极大的数字会使用指数形式
  • Symbol类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误
  • 对普通对象来说,除非自行定义toString()方法,否则会调用toString()(Object.prototype.toString())来返回内部属性[[Class]]的值,如‘[object Object]’。如果对象有自己的toSting()方法,字符串化时就会调用该方法并使用其返回值。

14、其他值到数字值的转换规则?

  • Undefined类型的值转换为NaN

  • Null类型的值转换为0

  • Boolean类型的值,true转换为1,false转换为0

  • String类型的值转换如同使用Number()函数进行转换,如果包含非数字值则转换为NaN,空字符串为0

  •  Symbol类型的值不能转换为数字,会报错

  • 对象(包含数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。

为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先(通过内部操作DefaultValue)J检查该值是否有valueOf()方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用toString()的返回值(如果存在)来进行强制类型转换。

如果valueOf()和toString()均布返回基本类型值,会产生TypeError错误。

15、JS字符串转数字的方法

通过函数parseInt(t),可以解析一个字符串,并返回一个整数,语法为parseInt(tsring,radix)

string:被解析的字符串

radix:表示要解析的数字的基数,默认是十进制,如果radix<2或者>36,则返回NaN

16、其他值到布尔类型的值的转换规则?

以下这些是假值:

  • undefined
  • null
  • false
  • +0、-0、NaN
  • ''、""

假值的布尔强制类型转换结果都为false,从逻辑上说,假值列表以外的都应该是真值。

17、|| 和&& 操作符的返回值?

|| 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。

  • 对于||来说,如果条件判断结果为true就返回第一个操作数的值,如果为false就返回第二个操作数的值
  • &&则相反,如果条件判断结果为true就返回第二个操作数的值,如果为false就返回第一个操作数的值

|| 和 &&返回它们其中一个操作数的值,而非条件判断的结果 

18、Object.is()与比较操作符'==='、‘==’的区别?

‘==’:不全相等,只比较数据,不比较类型,如果两边的类型不一致,则会进行强制类型转换后再进行比较

‘====’:全等,既要比较数据,也比较数据类型,如果两边的类型不一致时,不会做强制类型转换,直接返回false

(1)==主要存在:强制转换成number,null==undefined

" " == 0 //true
"0" == 0 //true
" " != '0' //true
123 == '123' //true
null == undefined //true

(2)Object.is():一般情况下和三等好的判断相同,它处理了一些特殊的情况,比如-0和+0不再相等,两个NaN是相等的

主要的区别就是 +0  != -0  而 NaN==NaN

19、什么时候JavaScript中的包装类型?

在JavaScript中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性和方法时JavaScript会在后台隐式地将基本类型的值转换为对象,例如:

const a = 'abc';

a.length; //3

a.toUpperCase(); //ABC

在访问‘abc’.length时,JavaScript将‘abc’在后台转换成String('abc'),然后再访问其length属性。

JavaScript也可以使用Object函数显式地将基本类型转换为包装类型:

let a = 'abc'

Object(a)  //String{'abc'} 

也可以使用valueOf方法将包装类型倒转成基本类型:

let a = 'abc'

let b = Object(a)

let c = b.valueOf() //abc 

看看如下代码会打印出什么:

let a = new Boolean(false);
if(!a){
    console.log('Oops');
}

 答案是什么都不会打印出来,因为虽然包裹的基本类型是false,但是false被包裹成包装类型后就成了对象,所以其非值为false,所以循环体中的内容不会运行。

20、JavaScript中如何进行隐式类型转换?

首先看ToPrimitive方法,这是JavaScript中每个值隐含的自带的方法,用来将值(无论是基本类型值还是对象)转换为基本类型值。如果值为基本类型,则直接方法会值本身;如果值为对象,看起来就如下所示:

ToPrimitive(obj,type)

type的值为number或者string

(1)当type为number时规则如下:

  • 调用obj的valueOf方法,如果为原始值,则返回,否则下一步
  • 调用obj的toString方法,后续同上
  • 抛出TypeError异常

(2)当type为string时规则如下:

  • 调用obj的toString方法,如果为原始值,则返回,否则下一步
  • 调用obj的valueOf方法,后续同上
  • 抛出TypeError异常 

可以看出两者的主要区别在于调用toString和valueOf的先后顺序。默认情况下:

  • 如果对象为Date对象,则type默认为string
  • 其他情况下,type默认为number

总结上面的规则,对于Date以外的对象,转换为基本类型的大概规则可以概括为一个函数:

let objToNumber = value => Number(valueOf().toString())

objToNumber([]) === 0

objToNumber({}) === NaN

而JavaScript中的隐式类型转换主要发生在+、-、*、/以及==、>、<这些运算符之间。而这些运算符只能操作基本类型值,所以在进行这些运算前的第一步就是将两边的值用ToPrimitive转换成基本类型,再进行操作。

一下是基本类型的值在不同操作符的情况下隐式转换的规则(对于对象,其会被ToPrimitive转换成基本类型,所以最终还是要应用基本类型转换规则)

1.+操作符  

+操作符的两边有至少一个string类型变量时,两边的变量都会被隐式转换为字符串;其他情况下两边的变量都会被转换为数字。

1+'23' //'123'

1+false //1

1+Symbol() //  Cannot convert a Symbol value to a number

'1'+false //'1false'

false+true // 1

2.-、*、\操作符

NaN也是一个数字

1*‘23’   //23

1*false //0

1/'aa  //NaN 

3.==操作符

操作符两边的值都尽量转成number

3 == true //false  3转为number为3,true转为number为1

‘0’ == false //true  ‘0’转为number为0 false转为number为0 

‘0’ == 0 //true  ‘0’转为number为0

4.>和<比较符

如果两边都是字符串,则会比较字符顺序

 'ca'<'bd'  //false

'a' < 'b' //true

其他情况下,转换为数字再比较:

'12' < 13 //true

false > -1 //true 

以上都是基本类型的隐式转换,而对象会被ToPrimitive转换为基本类型再进行转换:

let a = {}

a > 2 //false 

对比过程如下:

a.valueOf()  //{}  ToPrimitive默认type为number,所以先valueOf,结果还是个对象

a.toString() // '[object Object]'

Number(a.toString())  //NaN  根据上面<和>操作符的规则,要转换成数字

NaN > 2  //false   得出比较结果 

 再或者

let a = {name:'Jack'}

let b = {age:18}

a+b  //"[object Object][object Object]"

 运算过程如下:
a.valueOf() // {},上⾯提到过,ToPrimitive默认type为number,所以先
valueOf,结果还是个对象,下⼀步
a.toString() // "[object Object]"
b.valueOf() // 同理
b.toString() // "[object Object]"
a + b // "[object Object][object Object]"

21、+操作符什么时候用于字符串的拼接?

根据ES5规范,如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作。如果其中一个操作数是对象(包括数组),则首先对其调用ToPrimitive抽象操作,该抽象操作再调用[[DefaultValue]],以数字作为上下文。如果不能转换为字符串,则会将其转换为数字类型来进行计算。

简单来说就是,如果+的其中一个操作数是字符串(或者通过以上步骤最终得到了字符串),则执行字符串拼接,否则就执行数字加法。

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字。

22、为什么会有BigInt的提案?

JavaScript中Number.MAX_SAFE_INTEGER表示最大安全数字,计算结果是9007199254740991,即在这个数范围内不会出现精度丢失(小数除外)。但是一旦超过这个范围,JavaScript就会出现计算不准确的情况,这在大数计算的时候,不得不依靠一些第三方库进行解决,因此官方提出了BigInt来解决此问题。

23、object.assign和扩展运算法是深拷贝还是浅拷贝,两者有什么区别?

扩展运算符:

let outObj = {
    inObj:{a:1,b:2}
}
let newObj = {...outObj}
newObj.inObj.a = 2
console.log(outObj);  //{ inObj: { a: 2, b: 2 } }

Object.assign()

let outObj = {
    inObj:{a:1,b:2}
}
let newObj = Object.assign({},outObj)
newObj.inObj.a = 2
console.log(outObj);  //{ inObj: { a: 2, b: 2 } }

可以看出,两个都是浅拷贝。

  • Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它会修改了一个对象,因此会触发ES6 setter。
  • 扩展操作符(...)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是它会复制ES6的symbols属性。

你可能感兴趣的:(JavaScript,javascript,开发语言,ecmascript)