每日面试题 1.16
Object.prototype.toString.call 是如何判断变量的类型的
ES3中给出的解释
在toString方法被调用时,会执行下面的操作步骤:
-
获取this对象的[[Class]]属性的值.
-
计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串.
-
返回第二步的操作结果Result(2).
[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的
[[Class]] 一个字符串值,表明了该对象的类型.
然后给了一段解释:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。
ES5中给出的解释
在toString方法被调用时,会执行下面的操作步骤
- 如果this的值为undefined,则返回"[object Undefined]".
- 如果this的值为null,则返回"[object Null]".
- 让O成为调用ToObject(this)的结果.
- 让class成为O的内部属性[[Class]]的值.
- 返回三个字符串"[object ", class, 以及 "]"连接后的新字符串.
ES5中,[[Class]]属性的解释更加详细:
所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是除了"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"之外的的任何字符串.[[Class]]内部属性是引擎内部用来判断一个对象属于哪种类型的值的.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。
总而言之,要获取一个对象的真实的内置类型,我们需要通过获取[[Class]]的属性值,在es5之前,该属性值只能通由Object.prototype.toString来访问,因此,通过Object.prototype.toString.call(arr)改变tostring方法的this指向,从而获得对象的内置类型。
JavaScript的垃圾回收机制是怎样的,简述一下
什么是JS中的垃圾
对于前端开发来说,js中内存管理是自动的,当创建对象、数组、函数等时就会自动给分配空间,当后续代码开发过程中对象不再被引用,或者不能从根上访问到时(通过引用、作用域链)这些都被称为垃圾。
垃圾回收机制
GC(垃圾回收机制)算法就是垃圾回收器在工作时查找和回收所遵循的规则。
常见算法
引用计数
核心思想:通过引用计数器来维护引用数。设置引用数,判断引用计数是否为0;当引用关系发生改变时,引用计数器就会修改引用数字,当引用数字为0时,GC会立即工作将当前的对象空间进行回收。
例如
const a=100;
const b=20;
const arr=[a,b];
function fn(){
const num = 0;
}
fn()
//循环引用的对象 计数算法缺点
//无法回收循环引用的对象(计数永远不为0所以不能被回收)
function fn(){
const obj1={}
const obj2={}
obj1.name=obj2
obj2.name=obj1
}
fn()
代码执行完毕后,fn函数中的常量num 的引用数字会为0,常量a,b还在被arr 使用,所以他们的引用数字肯定不是0,那么当整段代码执行完毕后,只有num 计数为0,它就会被当做垃圾进行清理。
标记清除
核心思想:分标记和清除两个阶段完成,第一阶段会遍历所有的对象找到活动对象(也称为可达对象),第二阶段仍是遍历所有的对象,清除没有被标记的对象,回收相应的空间并清除所有标记。同时它会将回收的空间放在空闲列表中,方便后续程序在这个中申请空间使用。
标记整理
核心思想:标记整理算法可以说是标记清除的增强,第一阶段与标记清除算法一致,第二阶段清除未标记对象前,会先执行整理,移动对象的位置,使回收的空间产生连续。
V8的标记清除
在大部分的应用场景:一个新创建的对象,生命周期通常很短。所以,V8里面,GC处理分为两大类:新生代和老生代。
新生代的堆空间为1M~8M,而且被平分成两份(to-space和from-space),通常一个新创建的对象,内存被分配在新生代。当to-space满的时候,to-space和form-space交换位置(此时,to空,from满),并执行GC.如果一个对象被断定为,未被引用,就清除;有被引用,逃逸次数+1(如果此时逃逸次数为2,就移入老生代,否则移入to-space)。
老生代的堆空间大,GC不适合像新生代那样,用平分成两个space这种空间换时间的方式。老生代的垃圾回收,分两个阶段:标记、清理(有Sweeping和Compacting这两种方式)。
标记,采用3色标记:黑、白、灰。步骤如下:
GC开始,所以对象标记为白色。
根对象标记为黑色,并开始遍历其子节点(引用的对象)。
当前被遍历的节点,标记为灰色,被放入一个叫 marking bitmap 的栈。在栈中,把当前被遍历的节点,标记为黑色,并出栈,同时,把它的子节点(如果有的话)标记为灰色,并压入栈。(大对象比较特殊,这里不展开)
当所有对象被遍历完后,就只剩下黑和白。通过Sweeping或Compacting的方式,清理掉白色,完成GC。
什么是强引用与弱引用
-
强引用是使用最普遍的引用。引用并计数
如果一个对象具有强引用,那垃圾回收器绝不会回收它,哪怕是程序的内存不足让程序异常终止 也不会靠随意回收具有强引用的对象来解决内存不足的问题。 -
弱引用:只引用 不计数
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
Map与WeakMap的区别有哪些
- map对key强引用,当map引用了一个key的时候,(内存堆空间的)实际key内容不会被垃圾回收掉,(有内存泄漏风险)
weakmap对key弱引用,实际的key可能在某次垃圾回收操作时被清除掉,导致weakmap中的这对key-value也会消失掉。 - WeakMap只接受对象作为key(null除外),如果设置其他类型的数据作为key,会报错。
- 由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。
- 没有clear()方法
- 不能遍历
null 与 undefined 有什么区别
- 定义上的区别
null表示"没有对象",即该处不应该有值。主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址
典型用法是:
(1)作为函数的参数,表示该函数的参数不是对象。
(2)作为对象原型链的终点。
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。是所有没有赋值变量的默认值,自动赋值.典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
- null和undefined转换成number数据类型
null 默认转成 0
undefined 默认转成 NaN