![17a8766c5e864f600fabb97d2e72d412.png](https://i-blog.csdnimg.cn/blog_migrate/fca3c6315ba3f694860602e29a4f2954.jpeg)
1.JavaScript
规定了几种语言类型
七大类型:Number、Boolean、String、undefined、null、Symbol、Object
详细说一下null与undefined
- null是JavaScript语言的关键字,而undefined是JavaScript预定义的全局变量,不是关键字。并且,在ECMAScript 3中,undefined是可读、可写的变量,可以给它赋任何值,这个错误在ECMAScript 5中做了修正,在该版本中undefined是只读的
- 执行typeof运算,null返回“object”字符串,undefined返回“undefined”字符串;
- undefined在根据需要自行转换为字符串是转换为"undefined",而null转换为"null";
- undefined在根据需要自行转换为数字时转换为NaN,而null转换为0。
- 至于把null和undefined做比较,null == undefined 返回true,null === undefined 返回false。可以认为undefined是表示系统级的、出乎意料的或类似错误的值的空缺,而null是表示程序级的、正常的或在意料之中的值的空缺。如果想把它们赋值给变量或属性或者当做参数传入函数,最好选择使用null。
2.JavaScript
对象的底层数据结构是什么
js基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说 ,更加容易管理内存空间。
js引用类型数据被存储于堆中 (如对象、数组、函数等,它们是通过拷贝和new出来的)。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。
堆&栈
两者都是存放临时数据的地方。
栈是先进后出的,就像一个桶,后进去的先出来,它下面本来有的东西要等其他出来之后才能出来。
堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。对于堆,我们可以随心所欲的进行增加变量和删除变量,不用遵循次序。
栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap) 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。
JS Object原理
![10f3ec6f6d0d58a547614f3a6e14b638.png](https://i-blog.csdnimg.cn/blog_migrate/3f65136908c311ca3db6304820f9fc9a.png)
![e52faeecf3417b271e475287fc360c50.png](https://i-blog.csdnimg.cn/blog_migrate/b583da6c0a52e6e2a13d7bedc4bd27ac.png)
//FixedArray即Descriptor
../../v8/src/runtime/http://runtime-literals.cc 72 constant_properties:
0xdf9ed2aed19: [FixedArray]
– length: 6
[0]: 0x1b5ec69833d1 <String[4]: name>
[1]: 0xdf9ed2aec51 <String[3]: yin>
[2]: 0xdf9ed2aec71 <String[3]: age>
[3]: 18
[4]: 0xdf9ed2aec91 <String[8]: -school->
[5]: 0xdf9ed2aecb1 <String[11]: high school>
//此map对应HiddenClass
../../v8/src/heap/http://heap.cc 3472 map is
0x21528af9cb39: [Map]
– type: JS_OBJECT_TYPE
– instance size: 48
– inobject properties: 3
– back pointer: 0x3e2ca8902311 <undefined>
– instance descriptors (own) #0: 0x3e2ca8902231 <FixedArray[0]>
相关链接:Js底层对象原理 从Chrome源码看JS Object的实现
3.Symbol
类型在实际开发中的应用、可手动实现一个简单的Symbol
应用:由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖
要动手实现简单的Symbol需要了解当调用 Symbol 的时候,会采用以下步骤:
- 如果使用 new ,就报错
- 如果 description 是 undefined,让 descString 为 undefined
- 否则 让 descString 为 ToString(description)
- 如果报错,就返回
- 返回一个新的唯一的 Symbol 值,它的内部属性 [[Description]] 值为 descString
(function() { var root = this; var SymbolPolyfill = function Symbol(description) { // 实现特性第 2 点:Symbol 函数前不能使用 new 命令 if (this instanceof SymbolPolyfill) throw new TypeError('Symbol is not a constructor'); // 实现特性第 5 点:如果 Symbol 的参数是一个对象,就会调用该对象的 toString 方法,将其转为字符串,然后才生成一个 Symbol 值。 var descString = description === undefined ? undefined : String(description) var symbol = Object.create(null) Object.defineProperties(symbol, { '__Description__': { value: descString, writable: false, enumerable: false, configurable: false } }); // 实现特性第 6 点,因为调用该方法,返回的是一个新对象,两个对象之间,只要引用不同,就不会相同 return symbol; } root.SymbolPolyfill = SymbolPolyfill;
详细模拟查看模拟实现 Symbol 类型
4.JavaScript
中的变量在内存中的具体存储形式
js基本类型数据都是直接按值存储在栈中以直接量的形式存储
js引用类型数据被存储于堆中,而引用类型数据的具体引用则在栈中存储
5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作
基本数据类型:Undefined Null Boolean Number String
内置对象:Object是Javascript中所有对象的父对象
数据封装对象:Object Array Boolean Number String
其他对象:Function Argument Math Date RegExp Error
js中的==与===的区别
一个是判断值是否相等,一个是判断值及类型是否完全相等。
要注意相等(==)和全等(===)的区别!全等不会出现类型转换
下面的规则用于判定===运算符比较的两个值是否相等的判断条件
•如果两个值的类型不同,它们就不相同。
•如果两个值是数字,而且值相同,那么除非其中一个或两个都是NaN(这种情况它们不是等同的),否则它们是等同的。值NaN永远不会与其他任何值等同,包括它自身(奇怪的家伙),要检测一个值是否是NaN,可以使用全局函数isNaN()。
•如果两个值都是字符串,而且在串中同一位置上的字符完全相同,那么它们就完全等同。如果字符串的长度或内容不同,它们就不是等同的。
•如果两个值都是布尔型true,或者两个值都是布尔型false,那么它们等同。
•如果两个值引用的是同一个对象、数组或函数,那么它们完全等同。如果它们引用的是不同的对象(数组或函数),它们就不完全等同,即使这两个对象具有完全相同的属性,或两个数组具有完全相同的元素。
•如果两个值都是null或都是undefined,它们完全相同。
6.理解值类型和引用类型
js基本类型数据都是直接按值存储在栈中以直接量的形式存储
js引用类型数据被存储于堆中,而引用类型数据的具体引用则在栈中存储
值类型和引用类型的区别
7.null
和undefined
的区别
同1
8.至少可以说出三种判断JavaScript
数据类型的方式,以及他们的优缺点,如何准确的判断数组类型
JavaScript
数据类型的方式
- typeof 用来判断基本类型,typeof用于引用类型值都为"object"。
- instanceof 用来判断引用类型
- constructor
Object.prototype.isPrototypeOf
Object.prototype.toString
准确的判断数组类型
1.基于 instanceof
a instanceof Array; // true
2. 基于 constructor
a.constructor === Array;// true
3. 基于 Object.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(a);
4. 基于 Object.prototype.toString
`
Object.prototype.toString.apply(a) === '[object Array]';
通常方法④被认为是最稳妥的方法,这也是一些框架用于判断数组类型使用的方法。前三种方法的准确性很容易遭到篡改:
var a = { __proto__: Array.prototype };
以上,前三种方法都返回true,这种不负责任的继承方式使得基于继承的判断方案失效。
9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用
字符串转换
String方法是toString在功能上的超集,String相比toString增加了对null和undefined的支持。
数值转换
转为数值的情况与转为字符串类似,不过对应的两个方法是对象的valueOf和Number构造函数。请注意,NaN也是一个数值
- 6大数据类型,除了null和undefined,都可以直接调用其valueOf方法来尝试转为其本身所代表的原始数据类型,但是相当一部分时候是不会转为数值的,尤其是对象。
Number构造方法,一定会把传入的值转化为数值。Number构造函数对于传入参数的具体操作是:
- 字符串,忽略前导0,转为数值。转换不成功(出现任何不符合数字形式的字符)则为NaN
- 若参数是对象,先调用其valueOf方法,如果返回的不是一个数值,那么调用其toString方法,再将这个字符串作为Number的参数传入,得出最终结果。
- undefined转为NaN,null转为0
- true转为1,false转为0
布尔转换
- 对于数值,NaN和0转为false
- 对于字符串,空字符串转为false
- null和undefined都转为false
- Symbol(ES6)和对象(不算null)全转为true
Symbol只能转布尔值和字符串,转其他类型全报错。
10.出现小数精度丢失的原因,JavaScript
可以存储的最大数字、最大安全数字,JavaScript
处理大数字的方法、避免精度丢失的方法
IEEE754 规定,对于 32 位的单精度浮点数,最高的 1 位是符号位 S,接着的 8 位是指数 E,剩下的 23 位为有效数字 M。
对于 64 位的双精度浮点数,最高的 1 位是符号位 S,接着的 11 位是指数 E,剩下的 52 位为有效数字 M。
位数阶数有效数字 / 尾数单精度浮点数32823双精度浮点数641152
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992(Number.MAX_SAFE_INTEGER+1)
大于 9007199254740992 的可能会丢失精度
JavaScript
可以存储的最大数字:Number.MAX_VALUE
9007199254740992 >> 10000000000000...000 // 共计 53 个 0 9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0 9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0
实际上
9007199254740992 + 1 // 丢失 9007199254740992 + 2 // 未丢失 9007199254740992 + 3 // 丢失 9007199254740992 + 4 // 未丢失
结果如图
![f860f4d3fff1ac022f0426cf7e4b3837.png](https://i-blog.csdnimg.cn/blog_migrate/362fa10213f70439bb9d34adcf922190.png)
以上,可以知道看似有穷的数字, 在计算机的二进制表示里却是无穷的,由于存储位数限制因此存在“舍去”,精度丢失就发生了。
处理方法
对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。
对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)