Loading

每日面试题 1.16

Object.prototype.toString.call 是如何判断变量的类型的

ES3中给出的解释

在toString方法被调用时,会执行下面的操作步骤:

  1. 获取this对象的[[Class]]属性的值.

  2. 计算出三个字符串"[object ", 第一步的操作结果Result(1), 以及 "]"连接后的新字符串.

  3. 返回第二步的操作结果Result(2).

[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性.在规范中,[[Class]]是这么定义的

[[Class]] 一个字符串值,表明了该对象的类型.

然后给了一段解释:

所有内置对象的[[Class]]属性的值是由本规范定义的.所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值.[[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。

ES5中给出的解释

在toString方法被调用时,会执行下面的操作步骤

  1. 如果this的值为undefined,则返回"[object Undefined]".
  2. 如果this的值为null,则返回"[object Null]".
  3. 让O成为调用ToObject(this)的结果.
  4. 让class成为O的内部属性[[Class]]的值.
  5. 返回三个字符串"[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的区别有哪些

  1. map对key强引用,当map引用了一个key的时候,(内存堆空间的)实际key内容不会被垃圾回收掉,(有内存泄漏风险)
    weakmap对key弱引用,实际的key可能在某次垃圾回收操作时被清除掉,导致weakmap中的这对key-value也会消失掉。
  2. WeakMap只接受对象作为key(null除外),如果设置其他类型的数据作为key,会报错。
  3. 由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。
  4. 没有clear()方法
  5. 不能遍历

null 与 undefined 有什么区别

  1. 定义上的区别

null表示"没有对象",即该处不应该有值。主动释放一个变量引用的对象,表示一个变量不再指向任何对象地址

典型用法是:

(1)作为函数的参数,表示该函数的参数不是对象。

(2)作为对象原型链的终点。

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。是所有没有赋值变量的默认值,自动赋值.典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

  1. null和undefined转换成number数据类型

null 默认转成 0

undefined 默认转成 NaN

posted @ 2021-01-16 19:26  不吃苦瓜^  阅读(128)  评论(0编辑  收藏  举报