1.JavaScript规定了几种语言类型?
- Number
- String
- Boolean
- undefined
- Null
- Object
- Symbol
- Set
- Map
- BigInt(一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数)
2.JavaScript对象的底层数据结构是什么,在内存中的具体存储形式?
-
JavaScript基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说 ,更加容易管理内存空间。
-
JavaScript引用类型数据被存储于堆中 (如对象、数组、函数等,它们是通过拷贝和new出来的)。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。
3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol。
Symbol表示独一无二的值(需要注意的是通过 Symbol()方法创建值的时候不能使用 new 操作符,原因是通过 new 实例化的结果是一个 object 对象,而不是原始类型的 symbol)
- 实际开发中,当遇到比较复杂的对象或是需要多人共同维护的对象类型数据,容易产生属性名的重复,使用symbol可以避免这样的问题:
const opt1 = Symbol('key');
const opt2 = Symbol('key');
const obj = {
[opt1]: 'value',
[opt2]: 'value',
}
// obj:{Symbol(key): "value", Symbol(key): "value"}
- 第二种情况是模拟类中的私有变量
// a.js
const privateKey = Symbol('key')
class myClass{
constructor(){
this.name = 'test'
// 因为Symbol独一无二的特性,此处的属性只能在a.js中被访问,
// 新建出来的实例也无法访问该属性,本质上是无法访问privateKey。
this[privateKey] = 'strange'
}
}
4.基本类型对应的内置对象,以及他们之间的装箱拆箱操作?
如 String、Number、Boolean、Object、Function、Array、Date、RegExp、Error。实际上这些是一些内置函数,这些内置函数可以当做构造函数来使用,从而创建一个对应子类型的新对象。
- 所谓的装箱,是指将基本数据类型转换为对应的引用类型的操作。而装箱又分为隐式装箱和显式装箱。
- 拆箱就和装箱相反。是指把引用类型转换成基本的数据类型。通常通过引用类型的valueOf()和toString()方法来实现。 具体不展开,详见 juejin.cn/post/684490…
5.null和undefined的区别
-
undefined的字面意思是:未定义的值。在试图读取未定义或者定义了未赋值的变量时会返回。
-
null的字面意思是:空值。在内存中可以表示为栈中的指针地址没有指向堆中的内存对象。(在JS最初的实现中,中的值是由一个表示类型的标签和实际数据值表示的,对象的类型标签是0。由于null代表的是空指针0x00,因此,null的类型标签也成为了 0,typeof null就错误的返回了"object")
6.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型?
- typeof
- 优点:书写简单,易于理解
- 缺点:只能区分基本数据类型,无法判断复杂数据类型,如null,Array,Date等(function有效)
- instanceof
- 优点:可以判断一个实例是否是其父类型或者祖先类型的实例
- 缺点:左侧必须为对象(无法判断null,undefined),当原型链发生变动时会判断错误
- Object.prototype.toString.call
- 优点:可以直接输出数据类型字符串,易于判断,适用所有数据类型
- 缺点:书写麻烦 判断数组:Object.prototype.toString.call(new Array()) === '[object Array]'
7.出现小数精度丢失的原因,JavaScript可以存储的最大数字、最大安全数字,JavaScript处理大数字的方法、避免精度丢失的方法?
原因:JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。1位用来表示符号位,11位用来表示指数,52位表示尾数。
因为在计算机最底层,数值的运算和操作都是采用二进制实现的,所以计算机没有办法精确表示浮点数,而只能用二进制近似相等的去表示浮点数的小数部分。
0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
0.1+0.2 = 0.01001100110011001100110011001100110011001100110011001110
转十进制0.30000000000000004
当进行计算或其他操作时时,四舍五入(逢1进,逢0舍)将会导致最终的运算结果存在偏差。
而大整数也存在同样的问题,因为表示尾数的尾数只有52位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),即十进制9007199254740992。(ES2015的BigInt就是用来解决大数问题)
可以引入Math.js等一些专门处理此问题的三方库来解决精度丢失。