前面提到了类型判断,JavaScript 的变量类型也是面试中经常考察的知识点。本节收集了一些类型相关的面试真题。
1. JavaScript 中有几种基本类型数据?请再列举几个引用对象。
答案:
7 种,分别为:Boolean,Null,Undefined,Number,BigInt,String,Symbol。
列举几种引用对象:
普通对象 Object
数组对象 Array
正则对象 RegExp
函数 Function
解读:
这道题目是几乎每次面试必考的题目,一般作为开胃菜被面试官提出来。将它们分为三组,答案并不难记:
- 表示空:
Undefined,Null - 基础三大件:
Number,String,Boolean - 两个新类型:
Symbol,BigInt
顺便一提,BigInt 的作用是提供了一种方法来表示大于 253 - 1 的整数。
2. 请说一下JavaScript中的typeof操作符的用法?
答案:
typeof用来返回一个值的变量类型,对于不同类型的变量其返回值如下:
typeof undefined === 'undefined'
typeof true === 'boolean'
typeof 78 === 'number'
typeof 'hey' === 'string'
typeof Symbol() === 'symbol'
typeof BigInt(1) === 'bigint'
typeof new String('abc') === 'object'
typeof null === 'object'
typeof function(){} === 'function'
typeof {name: 'Jack'} === 'object'
解读:
注意typeof是用来返回值的类型,而不是返回**变量的类型,**因为 JavaScript 中的变量是没有类型的,比如:
let a = 1;
console.log(typeof a) // 'number'
a = '1'
console.log(typeof a) // 'string'
a变量是没有类型的,但是赋给a变量的值确是有类型的。
typeof对于大多数对象都会返回object,一个例外是函数,对于函数会返回function。另一个要注意的是typeof对于null会返回object,这是一个历史悠久的 bug,但是由于无数的网站已经默认了这个 bug,所以现在也无法对其进行修正了。所以使用typeof判断null的方法是:
var a = null;
(!a && typeof a === "object"); // true
3. 什么是 JavaScript 中的包装类型?
答案:
在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:
const a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
在访问'abc'.length时,JavaScript 将'abc'在后台转换成String('abc'),然后再访问其length属性。
解读:
JavaScript也可以使用Object函数显式地将基本类型转换为包装类型:
var a = 'abc'
Object(a) // String {"abc"}
也可以使用valueOf方法将包装类型倒转成基本类型:
var a = 'abc'
var b = Object(a)
var c = b.valueOf() // 'abc'
请猜测如下代码会打印出什么:
var a = new Boolean( false );
if (!a) {
console.log( "Oops" ); // never runs
}
答案是什么都不会打印,因为虽然包裹的基本类型是false,但是false被包裹成包装类型后就成了对象,所以其非值为false,所以循环体中的内容不会运行。
4. 如何使用Object.prototype.toString来判断值的类型,为什么使用它可以判断值的类型?
答案:
在任何值上调用Object.prototype.toString方法,都会返回一个 [object NativeConstructorName]格式的字符串。每个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。
Object.prototype.toString.call({name:'Jack'}) // [object Object]
Object.prototype.toString.call(function(){}) // [object Function]
Object.prototype.toString.call(/name/) // [object RegExp]
而对于基本类型:
Object.prototype.toString.call('abc') // [object String]
Object.prototype.toString.call(12) // [object Number]
Object.prototype.toString.call(true) // [object Boolean]
基本类型值是没有构造函数的,为什么也能返回构造函数名呢?这是因为在toString被调用时 JavaScript 将基本类型值转换成了包装类型。
而对于null和undefined:
Object.prototype.toString.call( null ); // "[object Null]"
Object.prototype.toString.call( undefined ); // "[object Undefined]"
虽然 JavaScript 中没有Null()和Undefined构造器,但是 JavaScript 也为我们处理这这两种情况。
5. JavaScript 中如何进行显式类型转换?
JavaScript 的类型转换的结果总是得到string、number和boolean类型的一种。
string和number
string 和 number 类型的值进行互相转换,分别使用String和Number函数:
const a = 18
const b = String(18) // '18'
const c = Number(b) // 18
number 向 string 类型转换还可以使用toString方法:
const a = 18
const b = a.toString() // '18'
string 向 number类型转换还可以使用一元操作符+:
const a = '18'
const b = +a //18
string向number类型转换还可以使用parseInt:
const a = '18px'
const b = parseInt(a) // 18
const c = Number(a) // NaN
可以看出,parseInt与Number不同的地方在于,parseInt允许传入非数字字符 (例如px),其从左往右解析,遇到非数字字符就会停下。而Number不允许传入非数字字符。
- 任意值转换成
boolean
使用Boolean函数来显式地将任意值转成boolean,如:
const a = '123'
const b = undefined
const c = 0
Boolean(a) // true
Boolean(b) // false
Boolean(c) // false
实践中最常用的方法是使用!!符号:
const a = '123'
const b = undefined
const c = 0
!!a // true
!!b // false
!!c // false
解读:
注意题目中的类型转换是这种形式:String(123),而不是这样:new String(123),因为第一个才是进行包装类型转换,而第二个是生成实例。
关于一元操作符+还有一个用法,那就是将日期对象转换成number
var date = new Date( "Mon, 1 Mar 2020 08:53:06" )
+date // 1583013186000
6. JavaScript 中如何进行隐式类型转换?
解答:
首先要介绍ToPrimitive方法,这是 JavaScript 中每个值隐含的自带的方法,用来将值 (无论是基本类型值还是对象)转换为基本类型值。如果值为基本类型,则直接返回值本身;如果值为对象,其看起来大概是这样:
/**
* @obj 需要转换的对象
* @type 期望的结果类型
*/
ToPrimitive(obj,type)
type的值为number或者string。
当type为number时规则如下:
- 调用
obj的valueOf方法,如果为原始值,则返回,否则下一步; - 调用
obj的toString方法,后续同上; - 抛出
TypeError异常。
当type为string时规则如下:
- 调用
obj的toString方法,如果为原始值,则返回,否则下一步; - 调用
obj的valueOf方法,后续同上; - 抛出
TypeError异常。
可以看出两者的主要区别在于调用toString和valueOf的先后顺序。默认情况下:
- 如果对象为 Date 对象,则
type默认为string; - 其他情况下,
type默认为number。
总结上面的规则,对于 Date 以外的对象,转换为基本类型的大概规则可以概括为一个函数:
var objToNumber = value => Number(value.valueOf().toString())
objToNumber([]) === 0
objToNumber({}) === NaN
而 JavaScript 中的隐式类型转换主要发生在+、-、*、/以及==、>、<这些运算符之间。而这些运算符只能操作基本类型值,所以在进行这些运算前的第一步就是将两边的值用ToPrimitive转换成基本类型,再进行操作。
以下是基本类型的值在不同操作符的情况下隐式转换的规则 (对于对象,其会被ToPrimitive转换成基本类型,所以最终还是要应用基本类型转换规则):
+操作符
当+操作符的两边有至少一个string类型变量时,两边的变量都会被隐式转换为字符串;其他情况下两边的变量都会被转换为数字。
1 + '23' // '123'
1 + false // 1
1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number
'1' + false // '1false'
false + true // 1
-、*、\操作符
这三个操作符是为数字操作而设计的,所以操作符两边的变量都会被转换成数字,注意NaN也是一个数字
1 * '23' // 23
1 * false // 0
1 / 'aa' // NaN
- 对于
==操作符
操作符两边的值都尽量转成number:
3 == true // false, 3 转为number为3,true转为number为1
'0' == false //true, '0'转为number为0,false转为number为0
'0' == 0 // '0'转为number为0
- 对于
<和>比较符
如果两边都是字符串,则比较字母表顺序:
'ca' < 'bd' // false
'a' < 'b' // true
其他情况下,转换为数字再比较:
'12' < 13 // true
false > -1 // true
以上说的是基本类型的隐式转换,而对象会被ToPrimitive转换为基本类型再进行转换:
var a = {}
a > 2 // false
其对比过程如下:
a.valueOf() // {}, 上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
a.toString() // "[object Object]",现在是一个字符串了
Number(a.toString()) // NaN,根据上面 < 和 > 操作符的规则,要转换成数字
NaN > 2 //false,得出比较结果
又比如:
var a = {name:'Jack'}
var 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]"
总结:
由于 JavaScript 的弱类型语言特性,它过于灵活的类型转换规则确实给开发者带来不少烦恼。特别是隐式转换,其实在实际开发中并不会遇到这么多题目中的情况,但是类似题目确实是面试中的常考题,建议大家多看多记。