一、 看代码,说输出

  1. var a = 1;
    (() => {
     console.log(a);
     a = 2;
    })();
    // 输出 1
    var a = 1;
    (() => {
     console.log(a);
     var a = 2;
    })();
    // 输出 undefined,变量提升
    var a = 1;
    (() => {
     console.log(a);
     let a = 2;
    })();
    // 输出 “Uncaught ReferenceError: Cannot access 'a' before initialization”
  2. if (1 == '1') { console.log('1') }
    if (1 === '1') { console.log('2') }
    if ([1] == [1]) { console.log('3') }
    if ([1] === [1]) { console.log('4') }
    if (0.1 + 0.2 == 0.3) { console.log('5') }
    if (0.1 + 0.2 === 0.3) { console.log('6') }
    // 输出 1
    // 因为 1 == '1': '1' 会转变成数字 1;
    //     [1] !== [1] : 引用类型 ==,会转化为 ===;
    //     0.1 + 0.2 !== 0.3 : 浮点数相加不精确相等
  3. window.name = 'ByteDance';
    function A () {
     this.name = 123;
    }
    A.prototype.getA = function(){
      console.log(this);
      return this.name + 1;
    }
    let a = new A();
    let funcA = a.getA;
    funcA();
    // 输出 window对象
    // funcA() 返回值为'ByteDance1'

    二、写代码

  4. 实现 flatten 函数
    var arr = [1,[2,3],[4,[5,[6]]],7];
    => [1,2,3,4,5,6,7]
    function flatten(arr) {
    }
    (1) 方法1,调用数组方法 flat
    function flatten(arr) {
     return arr.flat(Infinity)
    }
    (2) 方法2, 递归法
    function flatten(arr) {
     if(!Array.isArray(arr)) {
         return arr
     }
     var ret = []
     for(let ele of arr) {
         // 如果元素不是数组
         if(!Array.isArray(ele)) {
             ret.push(ele)
         } else  {
             ret = ret.concat(flatten(ele))
         }
     }
     return ret
    }

三、问答题

1. 网络分层模型

OSI参考模型
从上到下依次是:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

  • 应用层:为应用程序提供服务并规定应用程序中通信相关的细节。包括文件传输、电子邮件、远程登录等协议;
  • 表示层:将应用处理的信息转换为适合网络传输的格式,或将来自下一层的数据转换为上层能够处理的格式。因此它主要负责数据格式的转换;
  • 会话层:负责建立和断开通信连接,以及数据的分隔等数据传输相关的管理;
  • 传输层:传输层,管理两个节点之间的数据传输。起着可靠传输的作用;
  • 网络层,将数据传输到目标地址。目标地址可以是多个网络通过路由器连接而成的某一个地址。因此这一层主要负责寻址和路由选择;
  • 数据链路层,负责物理层面上互连的节点之间的通信传输。将0、1序列划分为具有意义的数据帧传送给对端;
  • 物理层,负责0、1比特流与电压的高低、光的闪灭之间的互换;

2. 传输层有哪些协议

TCP 和 UDP 协议

3. tcp 协议和 udp 协议各自的特点

tcp: 首部开销大,固定长度20个字节; 可靠,面向连接; 面向字节流;
udp: 首部开销小,固定长度8个字节; 不可靠,面向无连接; 面向报文;

4. HTTP各个版本了解多少,他们之间的差异

(1)HTTP/0.9: HTTP协议的最初版本,功能简陋,仅支持请求方式 GET, 并且仅能请求访问 HTML 格式的资源。
(2)HTTP/1.0: 请求行必须在尾部添加协议版本字段(http/1.0);必须包含头消息。相对与0.9版本,增加了请求方式 POST 和 HEAD;不再局限于0.9的HTML格式,根据Content-Type可以支持多种数据格式,同时支持 cache ,就是客户端在规定时间内访问统一网站,直接访问 cache 即可。其次,HTTP请求和回应的格式也变了。除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。其他新增功能还包括状态码、多字符集支持、多部份发送、权限(authorization)、缓存、内容编码等。 但是1.0版本的工作方式是每次TCP连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keepalive
(3)HTTP/1.1: 最大的变化就是引入了持久连接,即 TCP 连接默认不关闭,可以被多个请求复用,不用声明Conneions:keep-alive。1.1版本的持续连接,一个 TCP 连接允许多个 HTTP 请求;加入了管道机制,在同一个 TCP 连接里,允许多个请求同时发送,增加了并发性,进一步改善了 HTTP 协议的效率。新增了请求方式PUT、 PATCH、OPTIONS、DELETE等。另外客户端请求的头信息新增了 Host 字段,用来指定服务器的域名。有了 Host 字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
(4)HTTP/2.0: 增加双工模式,即不仅客户端能够同时发送多个请求,服务端也能同时处理多个请求,解决了队头堵塞的问题。另外也增***务器推送的功能,即不经请求服务端主动向客户端发送数据。

5. HTTP 协议是网络模型那一层的协议, DNS 协议是哪一层的协议

两者都是应用层的协议

6. 讲讲 DNS

  1. DNS(Domain Name System 域名系统):是一个将 ip 地址映射成为域名或者将域名映射成为 ip 的一种服务。

    DNS占用53号端口,同时使用 TCP 和 UDP 协议。DNS 在区域传输的时候使用 TCP 协议,其他时候使用 UDP 协议。域名解析时使用 UDP 协议,因为一般域名查询返回的内容不超过512个字节,用 UDP 传输即可。不用经过三次握手,这样 DNS 服务器负载更低,响应更快。

  2. DNS 解析过程:

    首先看浏览器缓存 -> 查看本机 host -> 查看家里的路由器 -> 问路由器,若路由器认识认识这个域名,就返回一个IP,然后计算机访问这个IP -> 若路由器不认识,继续问上一层路由器(自己家楼的路由器)-> 问这个城市级别的路由器,即 LDNS 服务器(本地 DNS 解析器) -> 如果 LDNS 不认识这个域名,则向全球DNS解析器(gDNS服务器) -> 最后找到结果后就会把 IP 和域名进行缓存加到上面的层(除了本机 host,其不缓存)

7. 讲讲 Expires 和 Cache-Control

两者都是进行强缓存时有服务端返回的头部字段,Expires 时 HTTP/1.0时间使用的,而Cache-Control是HTTP/1.1时期采用的。
Expires: Wed, 21 Oct 2015 07:28:00 GMT, 交代了过期时间,过期了就得向服务端发请求。但是服务器的时间和浏览器的时间可能并不一致,所以可能出错,导致其在HTTP1.1版本中被抛弃。
Cache-Control:这个字段和 Expires 本质的不同在于它没有采用具体的过期时间点,而是采用过期时长来控制缓存,对应的字段是 max-age。
Cache-Control:max-age=3600
代表这个响应返回后在3600秒内可以直接使用缓存。
Cache-Control 不只 max-age 这一个属性,还有一些关键属性:

  • public: 客户端和代理服务器都可以缓存;
  • private: 只能浏览器缓存,中间的代理服务器不能缓存;
  • no-cache: 跳过当前的强缓存,发送 HTTP 请求,即直接进入协商缓存阶段;
  • no-store:不进行任何形式的缓存;

Expires 和 Cache-Control 同时存在时,优先考虑 Cache-Control

8. 讲讲强缓存和协商缓存

强制缓存整体流程比较简单,就是在第一次访问服务器取到数据之后,在过期时间之内不会再去重复请求。实现这个流程的核心就是如何知道当前时间是否超过了过期时间。
强制缓存的过期时间通过第一次访问服务器时返回的响应头获取。在 http 1.0 中通过 Expires 响应头来实现,在 http 1.1 中通过Cache-Control来实现。
强缓存只有首次请求时才会跟服务器通信,读取缓存资源时不会发出任何请求,资源的状态码 Status 为200,资源的 Size 为 from memory 或者 from disk, http 1.1版本的实现优先级会高于http 1.0版本的实现。

协商缓存与强缓存的不同之处在于,协商缓存每次读取数据时都需要与服务器通信,并且会增加缓存标识。在第一次请求服务器时,服务器会返回资源,并且返回一个资源的缓存标识,一起存到浏览器的缓存数据库。当第二次请求资源时,浏览器会首先将缓存标识发送给服务器,服务器拿到标识后判断标识是否匹配,如果不匹配,表示资源有更新,服务器会将新数据和新的缓存标识一起返回到浏览器;如果缓存标识匹配,标识资源没有更新,并且返回304状态码,浏览器就读取本地缓存服务器中的数据。其在 http 1.0 1.1 版本中也有不同的实现方式。
http 1.0
第一次请求资源 服务器 Last-Modified作为缓存标识key,value为资源最后修改的时间 -> 客户端
后续请求时 客户端 If-Modified-Since请求头 -> 服务器
服务器会讲 If-Modified-Since 中携带的时间与资源修改的时间匹配,如果不一致,服务器会返回新的资源,并且将 Last-Modified 值更新,作为响应头发送给浏览器。如果时间一致,标识资源没有更新,服务器返回304状态码,浏览器拿到响应状态码后从本地缓存数据库中读取缓存资源。
http 1.1
在 http 1.1版本中,服务器通过 Etag 来设置响应头缓存标识。Etag 的值有服务端生成。在第一次请求时,服务器会将资源与 Etag 一并返回到浏览器,浏览器将两者缓存到本地缓存数据库。在第二次请求时,浏览器会将 Etag 信息放到 If-None-Match 请求头去访问服务器,服务器收到请求后,会将服务器中的文件标识与浏览器发来的标识进行对比,如果不相同,服务器返回更新的资源和新的 Etag,如果相同,服务器返回304状态码,浏览器读取缓存。
协商缓存每次请求都会与服务器进行交互,第一次是拿数据和标识的过程,第二次开始,就是浏览器询问服务器资源是否有更新的过程。每次请求都会传输数据,如果命中缓存,则资源的 Status 状态码为 304 而不是 200。同样的,一般来讲为了兼容,两个版本的协商缓存都会被实现,http 1.1 版本的实现优先级会高于http 1.0 的版本实现。

9. 协商缓存的两种缓存tag

Last-Modified: Fri, 12 May 2006 18:53:33 GMT
这个值就是服务器传来的资源最后修改的时间
Etag 是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过相应头把这个值给浏览器。
服务器收到 If-None-Match 后,会跟服务器上该资源的 Etag 进行比对。
两者对比:

  1. 在精准度上:Etag 优于 Last-Modified。Last-Modified 两大缺陷:(1)如果编辑了文件,有撤销了编辑,则服务器的 Last-Modified 会变动,尽管内容其实并没有改变;(2)Last-Modified 能够感知的时间单位是秒,如果文件在1秒内改变了多次,那么 Last-Modified 就感知不到了。
  2. 在性能上:Last-Modified 优于 Etag,也很简单理解,Last-Modified仅仅记录的是一个时间点,而 Etag 需要根据文件的具体内容生成哈希值。
    如果两种方式都支持的话,服务器优先考虑 Etag。

10. 讲讲进程和线程

对于操作系统来说,一个任务就是一个进程,比如打开一个浏览器就是打开一个浏览器进程,打开一个记事本就是打开一个记事本进程,打开两个记事本就是启动了两个记事本进程,打开一个word就是启动了一个word进程。
有些进程还不止同时干一件事,比如word,他可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程。
由于每个线程至少要干一件事,所以,一个进程至少有一个线程。
线程是最小的执行单元,而进程至少由一个线程组成。
进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows上,一个运行的.exe就是一个进程。
线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程都有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
进程与线程的区别总结
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看作轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小
包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程奔溃后,在保护模式下不会对其他进程产生影响,但是一个线程奔溃整个进程都死掉。所以多进程要比对线程健壮
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

11. 谈谈对 React 的理解

12. 讲一下声明式与命令式的区别

*命令式编程:告诉机器如何做,并得到自己想要的结果。
*声明式编程:告诉机器您想得到什么,让机器自己计算该如何做。