面试基础复习

75 阅读6分钟

一、javascript的数据类型 一共有八种:

7种基础类型:undefined、Null、Boolean、String、Number、Symbol、BigInt 1种引用类型:Object

二、数据类型检测

  1、typeof

     能判断基本类型

     对于引用类型,除了 function 会判断为 OK 以外,其余都是 'object'

  2、instanceof

     可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型

  3、Object.property.toString

三、this指向问题 三、闭包

闭包其实就是一个可以访问其他函数内部变量的函数

闭包产生的本质就是:当前环境中存在指向父级作用域的引用。

 闭包的作用:

1、可以读取函数内部的变量,

2、让这些变量的值始终保持在内存中。

 

     下面这段代码输出什么?

for(var i = 1; i <= 5; i ++){

  setTimeout(function() {

    console.log(i)

  }, 0)

}

 

输出5个6

原因:

  1. setTimeout 为宏任务,由于 JS 中单线程 eventLoop 机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 setTimeout 中的回调才依次执行。
  2. 因为 setTimeout 函数也是一种闭包,往上找它的父级作用域链就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 已经就是 6 了,因此最后输出的连续就都是 6。

解决办法:

// 立即执行函数  

for(var i=0;i<=5;i++){

    (function(j){

        setTimeout(function(){

            console.log(j)

        },0)

    })(i)

}

 

// 使用let

for(let i=0;i<=5;i++){

    setTimeout(function(){

     console.log(i)

    },0)

}

 

// 定时器传入第三个参数

for (var i=0;i<=5;i++){

    setTimeout(function(j){

      console.log(j)

    },0,i)

}

 

 

立即执行函数解决闭包:blog.csdn.net/weixin_4362…

五、  浏览器中的eventloop 开始,任务先进入 Call Stack(调用栈)

同步任务直接在栈中等待被执行,当调用异步函数时,会发送到浏览器Api

API 一直等到指定的时间将此操作送回到Event Queue(事件队列) 等待

当 Call Stack 中没有任务,就从 Event Queue 中拿出一个任务放入 Call Stack

Eventloop指的就是一个循环

    它不停检查 Call Stack 中是否有任务需要执行,如果没有,就检查 Event Queue,从中弹出一个任务,放入Call Stack 中,如此往复循环。

三、  宏任务和微任务

在 EventLoop 中,每一次循环称为一次 tick,主要的任务顺序如下:

1.         执行栈选择最先进入队列的宏任务,执行其同步代码直至结束;

2.         检查是否有微任务,如果有则执行直到微任务队列为空;

3.         如果是在浏览器端,那么基本要渲染页面了;

4.         开始下一轮的循环(tick),执行宏任务中的一些异步代码,例如 setTimeout 等。

MacroTask(宏任务)

  • script全部代码、setTimeoutsetIntervalsetImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/OUI Rendering

MicroTask(微任务)

  • Process.nextTick``(Node独有)PromiseObject.observe(``废弃)MutationObserver(具体使用方式查看这里

六、      Javascript是如何被浏览器引擎编译、执行的?

1.         Parse 阶段:V8 引擎负责将 JS 代码转换成 AST(抽象语法树);

2.         Ignition 阶段:解释器将 AST 转换为字节码,解析执行字节码也会为下一个阶段优化编译提供需要的信息;

3.         TurboFan 阶段:编译器利用上个阶段收集的信息,将字节码优化为可以执行的机器码;

4.         Orinoco 阶段:垃圾回收阶段,将程序中不再使用的内存空间进行回收。

通过V8引擎,先把js代码转化成AST,然后通过解释器将AST转成字码节,在通过编译器将字码节转成机器码,最后在进行垃圾回收, 至此整个js的执行就完成了。

 

 

七、垃圾回收

对于简单的数据类型,内存是保存在栈(stack)空间中的;复杂数据类型,内存保存在堆(heap)空间中。简而言之,基本就是说明以下两点。

· 基本类型:这些类型在内存中会占据固定的内存空间,它们的值都保存在栈空间中,直接可以通过值来访问这些;

· 引用类型:由于引用类型值大小不固定(比如上面的对象可以添加属性等),栈内存中存放地址指向堆内存中的对象,是通过引用来访问的。

因此总结来说:栈内存中的基本类型,可以通过操作系统直接处理;而堆内存中的引用类型,正是由于可以经常变化,大小不固定,因此需要 JavaScript 的引擎通过垃圾回收机制来处理

 

V8引擎将堆内存分为两类 新生代的回收机制和老生代的回收机制

 

 新生代内存回收:

在V8引擎的内存结构中,新生代主要用于存放存活时间较短的对象。新生代内存是由两个semispace(半空间)构成的,内存最大值在64位系统和32位系统上分别为32MB和16MB,在新生代的垃圾回收过程中主要采用了Scavenge算法。

 

    在Scavenge算法的具体实现中,主要采用了Cheney算法,它将新生代内存一分为二,每一个部分的空间称为semispace,也就是我们在上图中看见的new_space中划分的两个区域,其中处于激活状态的区域我们称为From空间,未激活(inactive new space)的区域我们称为To空间。这两个空间中,始终只有一个处于使用状态,另一个处于闲置状态。我们的程序中声明的对象首先会被分配到From空间,当进行垃圾回收时,如果From空间中尚有存活对象,则会被复制到To空间进行保存,非存活的对象会被自动回收。当复制完成后,From空间和To空间完成一次角色互换,To空间会变为新的From空间,原来的From空间则变为To空间。

老生代内存回收:

 标记清除:

通过名字你就可以理解,标记清除分为两个阶段:标记阶段和清除阶段。

首先它会遍历堆上的所有的对象,分别对它们打上标记;然后在代码执行过程结束之后,对使用过的变量取消标记。那么没取消标记的就是没有使用过的变量,因此在清除阶段,就会把还有标记的进行整体清除,从而释放内存空间。

听起来这一切都比较完美,但是其实通过标记清除之后,还是会出现上面图中的内存碎片的问题。内存碎片多了之后,如果要新来一个较大的内存对象需要存储,会造成影响。对于通过标记清除产生的内存碎片,还是需要通过另外一种方式进行解决,因此这里就不得不提到标记整理策略(Mark-Compact)了。下面我们就来看看标记整理策略是如何帮助清除内存碎片的问题的。

标记整理

经过标记清除策略调整之后,老生代的内存中因此产生了很多内存碎片,若不清理这些内存碎片,之后会对存储造成影响。

为了方便解决浏览器中的内存碎片问题,标记整理这个策略被提出。这个策略是在标记清除的基础上演进而来的,和标记清除来对比来看,标记整理添加了活动对象整理阶段,处理过程中会将所有的活动对象往一端靠拢,整体移动完成后,直接清理掉边界外的内存。

八、事件委托

事件委托是利用事件的冒泡原理,从事件最深的节点开始,逐步向上传播事件

九、事件冒泡、阻止冒泡

1、事件开始时由最具体的元素接收,然后逐级向上传播,一直传播到window对象

2、stopPropagation