19

面试血泪史

不要问我面的是不是架构师。我只是面的低级前端。总结不易点个赞吧。

css

1. GPU加速

GPU英文全称Graphic Processing Unit,中文翻译为“图形处理器”。与CPU不同,GPU是专门为处理图形任务而产生的芯片。从这个任务定位上面来说,不仅仅在计算机的显卡上面,在手机、游戏机等等各种有多媒体处理需求的地方都可以见到GPU的身影。

对于GPU来说,它的任务是在屏幕上合成显示数百万个像素的图像——也就是同时拥有几百万个任务需要并行处理,因此GPU被设计成可并行处理很多任务,而不是像CPU那样完成单任务。

因此CPU和GPU架构差异很大,CPU功能模块很多,能适应复杂运算环境;GPU构成则相对简单。

GPU包含了比CPU更多的处理单元,更大的带宽,使得其在多媒体处理过程中能够发挥更大的效能。例如:当前最顶级的CPU只有4核或者6核,模拟出8个或者12个处理线程来进行运算,但是普通级别的GPU就包含了成百上千个处理单元,高端的甚至更多,这对于多媒体计算中大量的重复处理过程有着天生的优势。

css 硬件加速:

浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中transform 是不会触发 repaint 的,这一点非常类似3D绘图功能,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。

3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint。然后由GPU的复合器操纵整个动画的执行。最后当动画结束时,再次执行 repaint 操作删除复合图层。

缺点:

  • 添加重绘是需要提升元素层级到复合层。 有时这是非常慢的(即我们得到一个全层重绘,而不是一个增量)。
  • 绘图层必须传输到GPU。 根据这些层的数量和尺寸,转移也可能非常慢。这可能导致元素在低端和中端市场设备上闪烁。
  • 每个复合层都消耗额外的内存。内存是移动设备上的宝贵资源。过多的内存使用可能会导致浏览器崩溃。
  • 如果你不考虑隐式合成,而使用慢速重绘,除了额外的内存使用,浏览器崩溃的几率也非常高。
  • 我们会有视觉假象,例如在Safari中的文本渲染,在某些情况下页面内容将消失或变形。

2. 层叠上下文

层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。

所以一个页面中往往不仅仅只有一个层叠上下文(因为有很多种方式可以生成层叠上下文),在一个层叠上下文内,我们按照层叠水平的规则来堆叠元素。

层叠上下文顺序:

  1. 首先先看要比较的两个元素是否处于同一个层叠上下文中:
          1.1如果是,谁的层叠等级大,谁在上面(怎么判断层叠等级大小呢?——看“层叠顺序”图)。
          1.2如果两个元素不在统一层叠上下文中,请先比较他们所处的层叠上下文的层叠等级。
  2. 当两个元素层叠等级相同、层叠顺序相同时,在DOM结构中后面的元素层叠等级在前面元素之上。

image.png

JS

1. 包装类跟普通类有什么区别?new String() 和String()的区别

答::js 提供了3个包装类 分别是 new String()new Number()new Boolean()。由于基础类型不能添加属性和方法,js的包装类的作用是将基础类型包装成一个对象,这样就可以有属性和方法。

tips:当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法;调用完以后,在将其转换为基本数据类型。

2. promise.then 是怎么实现链式调用的

: 通过重新return 一个new Promise 来实现链式调用

3. 多调用几次bind 比如bind a bind b 最后this指向谁 为啥

答: 永远指向第一次调用bind时传入的上下文,因为bind之后的调用都是绑定在这个上下文上。

4. v8引擎回收机制简述

答: v8垃圾回收主要通过两个策略:

  • 标记清除
  • 引用计数

    标记清除是js最常用的垃圾回收机制。垃圾回收程序运行的时候,会标记内存中存储的所有变量。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

    引用计数是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为 1。如果同一个值又被赋给另一个变量,那么引用数加 1。类似地,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减 1。当一个值的引用数为 0 时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。垃圾回收程序下次运行的时候就会释放引用数为 0 的值的内存。(以上摘自js红宝书第四版)

5. v8回收算法运行时会阻塞js吗?为什么

答: 会阻塞。

6. 怎么优化垃圾回收机制

答: https://www.cnblogs.com/cheng... 总结就是多用新生代算法

7. 作用域链简述。怎么能获取函数内部变量?

答: 作用域链就是变量向上查找过程。可以通过函数内部return 一个携带函数内部变量的闭包使得外部可以访问函数内部的变量

8. 闭包简述,怎么避免内存泄漏

答: 无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包,闭包包含在函数创建时作用域中的所有变量,类似于背包,函数定义附带一个小背包,他的包中存储了函数创建时作用域中的所有变量。及时将指针指向null可以避免内存泄漏。

9. class 类可以枚举吗?类 instanceof Function 输出什么?

答: 类的内部所有定义的方法,都是不可枚举的类的数据类型就是函数,类本身就指向构造函数。 代码如下:

class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
class Fn{}
Fn instanceof Function // true

const a = new Fn()
a instanceof Function // false

webpack

1. webpack 是怎么实现分模块打包的?

答: 可以通过splitChunks 实现。

webpack 中以下三种常见的代码分割方式:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 动态导入:通过模块的内联函数调用来分离代码。
  • 防止重复:使用 splitChunks 去重和分离 chunk。 第一种方式,很简单,只需要在 entry 里配置多个入口即可。

splitChunks 代码拆分

splitChunks: {
    // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
    chunks: "async",
    // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
    minSize: 30000,
    // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
    minChunks: 1,
    // 表示按需加载文件时,并行请求的最大数目。默认为5。
    maxAsyncRequests: 5,
    // 表示加载入口文件时,并行请求的最大数目。默认为3。
    maxInitialRequests: 3,
    // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
    automaticNameDelimiter: '~',
    // 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。
    name: true,
    // cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。
    // 模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自 
    // node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10 // 缓存组优先级
        },
        // 
    default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true  // 可设置是否重用该chunk
        }
    }
}

通过 cacheGroups,我们可以定义自定义 chunk 组,通过 test 条件对模块进行过滤,符合条件的模块分配到相同的组。

2. webpack4 的tree-shaking是什么?怎么实现的?在什么情况下会失效?为什么?

答: tree-shaking本质是webpack打包时用来舍弃无用的代码。

工作原理: 在ES6以前,我们可以使用CommonJS引入模块:require(),这种引入是动态的,也意味着我们可以基于条件来导入需要的代码

let module
if(true){
    module = require('a')
}else{
    module = require('b')
}

CommonJS规范无法确定在实际运行前需要或者不需要某些模块,所以CommonJS不适合tree-shaking机制

ES6的import语法可以完美使用tree shaking,因为可以在代码不运行的情况下就能分析出不需要的代码。

因为tree shaking只能在静态modules下工作。ECMAScript 6 模块加载是静态的,因此整个依赖树可以被静态地推导出解析语法树。

side effects是指那些当import的时候会执行一些动作,但是不一定会有任何export

tree shaking 不能自动的识别哪些代码属于side effects,因此手动指定这些代码显得非常重要。如果所有代码都不包含副作用,我们就可以简单地将该属性标记为false,来告知 webpack,它可以安全地删除未用到的export导出。

总结: ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块。再判断那些模块和变量未被使用或者引用,进而删除对应代码。

另外,webpack中可以在项目package.json文件中,添加一个 “sideEffects” 属性,手动指定由副作用的脚本。

3. env 知道吗?是用来干什么的?项目需要单独安装吗?为什么?

env是nodejs里内置的一个对象,可以利用process.env拿到当前项目运行环境的信息。不需要独立安装,因为是nodejs的内置对象。

4. import 和 require 的区别

答:

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  3. CommonJs 是单个值导出,ES6 Module可以导出多个
  4. CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
  5. CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined

5. 知道什么是静态分析吗?

答: es modules可以在代码不运行的情况下对代码进行分析,可以知道哪些模块有没有被使用。

6. webpack babel是如何工作的?

答:

    1. 词法解析 。将字符串形式的代码转换为Tokens(令牌),Tokens 可以视作是一些语法片段组成的数组。
    1. 语法解析。把Tokens转换为抽象语法树AST
    1. 转换阶段。会对 AST 进行遍历,在这个过程中对节点进行增删查改。Babel 所有插件都是在这个阶段工作, 比如语法转换、代码压缩。
    1. 输出阶段。将经过转换的AST通过babel-generator再转换成js代码,过程就是深度优先遍历整个AST,然后构建可以表示转换后代码的字符串。同时这个阶段还会生成Source Map

7. webpack plugins的执行时机?

答: 加载文件完成后,输出文件前,不同的plugins有不同的执行时机。

8. webpack优化首屏加载

node

1. koa 源码了解过,是怎么实现的?

答: koa通过对http模块的封装,在内部实现了一个context上下文的概念,把res跟req都放在ctx上面,并且对req和res进行优雅的setter/getter处理,调用方式更简单。

洋葱模型通过将中间件数组里的异步方法通过dispatch去递归调用,由于在app.use中去调用next方法时去调用下一个中间件。

洋葱模型实现伪代码

function compose(middlewares){
    return function(){
        return dispatch(0)
        function dispatch(i){
            let fn = middlewares[i]
            if(!fn) return Promise.resolve()
            return Promise.resolve(fn(function next(){
                // promise 完成之后在执行下一个
                return dispatch(i+1)
            }))
        }
    }
}

2. koa 洋葱模型

答: 见上

3. cdn加速是怎么做的?

答: 简单的说就是缓存+负载均衡

  1. 浏览器向域名解析服务器发出解析请求,由于CDN 对域名解析过程进行了调整,所以用户端一般得到的是该域名对应的 CNAME 记录,此时浏览器需要再次对获得的 CNAME 域名进行解析才能得到缓存服务器实际的IP 地址。 注:在此过程中,全局负载均衡DNS 解析服务器会根据用户端的源IP 地址,如地理位置(北京还是上海)、接入网类型(电信还是网通)将用户的访问请求定位到离用户路由最短、位置最近、负载最轻的Cache 节点(缓存服务器)上,实现就近定位。定位优先原则可按位置、可按路由、也可按负载等。
  2. 再次解析后浏览器得到该域名CDN 缓存服务器的实际IP 地址,向缓存服务器发出访问请求。
  3. 缓存服务器根据浏览器提供的域名,通过Cache 内部专用DNS 解析得到此域名源服务器的真实IP 地址,再由缓存服务器向此真实IP 地址提交访问请求。
  4. 缓存服务器从真实IP 地址得到内容后,一方面在本地进行保存,以备以后使用,同时把得到的数据发送到客户端浏览器,完成访问的响应过程。

4. cdn源服务器文件修改,负载均衡还有作用吗?

答: cdn一般用来存静态资源。拿网站来说,当用户访问网站时静态资源从cdn加载。cdn向源服务器请求资源并缓存,这个请求过程是周期性的,自动的,称为回源。 当你更新了一个文件,现在正巧还没到cdn自动更新的时候,如果想让用户马上看到新的就得手动刷cdn,一般cdn控制台都有此选项。

5. 负载均衡有哪些模式

  1. 轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
  2. weight权重 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。权重越高,在被访问的概率越大。
  3. ip_hash 如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。
  4. fair(第三方) 按后端服务器的响应时间来分配请求,响应时间短的优先分配。
  5. url_hash(第三方) 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

6. 脱离了nginx怎么配置负载均衡

答: Redis,Zookeeper (ps:nt问题)

7. a服务器node服务怎么访问b服务器的脚本

答: (大佬跟我说的)用RPC RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

8. 经过网关转发下websocket还可以一直保持心跳吗

答: 不能

9. 前端页面设置强缓存,但是有东西更新了。怎么保证用户的页面是最新的

答:

  1. 静态资源设置协商缓存。
  2. 在静态资源后面配置版本号,时间戳等。
  3. (希望大家可以帮忙提供一下更好的方案)

10. node 事件循环

答:

node中的微观,宏观任务

  1. 常见的 macro-task 比如:setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作等。
  2. 常见的 micro-task 比如: process.nextTick、new Promise().then(回调)等。

microtask 在事件循环的各个阶段之间执行。

  • timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
  • I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
  • idle, prepare 阶段:仅node内部使用
  • poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
  • check 阶段:执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socket 的 close 事件回调
  1. timers阶段

timers 阶段会执行 setTimeout 和 setInterval 回调,并且是由 poll 阶段控制的。 同样,在 Node 中定时器指定的时间也不是准确时间,只能是尽快执行。

  1. callbacks阶段
    此阶段执行某些系统操作的回调,例如 TCP 错误。
  2. 轮询 poll 阶段
  • 计算应该阻塞并 I/O 轮询的时间
  • 处理轮询队列 (poll queue) 中的事件

当事件循环进入轮询 (poll) 阶段并且没有任何计时器调度 (timers scheduled) 时,将发生以下两种情况之一:

  • 如果轮询队列 (poll queue) 不为空,则事件循环将遍历其回调队列,使其同步执行,直到队列用尽或达到与系统相关的硬限制为止
  • 如果轮询队列为空:如果已通过 setImmediate 调度了脚本,则事件循环将结束轮询 poll 阶段,并继续执行 check 阶段以执行那些调度的脚本。如果脚本并没有 setImmediate 设置回调,则事件循环将等待 poll 队列中的回调,然后立即执行它们。
  1. 检查阶段 check

此阶段允许在轮询 poll 阶段完成后立即执行回调。 如果轮询 poll 阶段处于空闲,并且脚本已使用 setImmediate 进入 check 队列,则事件循环可能会进入 check 阶段,而不是在 poll 阶段等待。

  1. close callbacks 阶段

setImmediate vs setTimeout

setImmediatesetTimeout 相似,但是根据调用时间的不同,它们的行为也不同

  • setImmediate 设计为在当前轮询 poll 阶段完成后执行脚本。
  • setTimeout 计划在以毫秒为单位的最小阈值过去之后运行脚本。

11. node在require的时候发生了哪些事

答:

  1. 拿到要加载的文件绝对路径。没有后缀的尝试添加后缀
  2. 尝试从缓存中读取导出内容。如果缓存有,返回缓存内容。没有,下一步处理
  3. 新建一个模块实例,并输入进缓存对象
  4. 尝试加载模块
  5. 根据文件类型,分类处理
  6. 如果是js文件,读取到文件内容,拼接自执行函数文本,用vm模块创建沙箱实例加载函数文本,获得导出内容,返回内容
  7. 如果是json文件,读取到文件内容,用JSON.parse 函数转成js对象,返回内容
  8. 获取导出返回值。

http

1. http 和 https的区别

答: HTTPS就是将HTTP运行在TLS/SSL的加密安全措施下。

  • https需要申请CA证书
  • https更安全。运用了加密手段
  • https端口443 http是80

2. udp和tcp的区别

答: 见我的另一篇文章TCP/IP

3. http3.0是基于udp的,为什么udp面向无连接还会选择udp?

答: 因为udp高效。而且在应用层解决了udp的不可靠性问题。

4. http3.0怎么解决udp的丢包问题?

答: http3不仅仅只是简单将传输协议替换成了 UDP。还基于 UDP 协议在「应用层」实现了 QUIC 协议。它具有类似 TCP 的连接管理、拥塞窗口、流量控制的网络特性,相当于将不可靠传输的 UDP 协议变成“可靠”的了,所以不用担心数据包丢失的问题。而且, QUIC 协议会保证数据包的可靠性,每个数据包都有一个序号唯一标识。当某个流中的一个数据包丢失了,即使该流的其他数据包到达了,数据也无法被 HTTP/3 读取,直到 QUIC 重传丢失的报文,数据才会交给 HTTP/3。

5. tcp除了你刚刚说的窗口控制,还有哪些控制?

答: 重发控制,流控制,拥塞控制

6. tcp重发机制是基于哪个时间节点

答: 引入两个概念:

  • RTT(Round Trip Time):往返时延,也就是数据包从发出去到收到对应 ACK 的时间。RTT 是针对连接的,每一个连接都有各自独立的 RTT。
  • RTO(Retransmission Time Out):重传超时,也就是前面说的超时时间。

我一般认为是两倍的RTT。

7. http chunked的作用

服务器生成HTTP回应是无法确定消息大小的,这时用Content-Length就无法事先写入长度,而需要实时生成消息长度,这时服务器一般采用Chunked编码。在进行Chunked编码传输时,在回复消息的头部有transfer-coding并定为Chunked,表示将用Chunked编码传输内容。

编码使用若干个chunk组成,由一个标明长度为0的chunk结束。每个chunk有两部分组成,第一部分是该chunk的长度,第二部分就是指定长度的内容,每个部分用CRLF隔开。分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。

8. tcp是怎么保证数据传输的可靠性?

TCP主要提供了检验和、序列号/确认应答、超时重传、最大消息长度、滑动窗口控制等方法实现了可靠性传输。

1. 检验和:通过检验和的方式,接收端可以检测出来数据是否有差错和异常,假如有差错就会直接丢弃TCP段,重新发送。TCP在计算检验和时,会在TCP首部加上一个12字节的伪首部。检验和总共计算3部分:TCP首部、TCP数据、TCP伪首部

2. 序列号/确认应答: 只要发送端有一个包传输,接收端没有回应确认包(ACK包),都会重发。或者接收端的应答包,发送端没有收到也会重发数据。这就可以保证数据的完整性。

使用窗口控制中,如果出现段丢失,先考虑确认应答未能返回的情况。在这种情况下,数据已经到达 对端,是不需要再进行重发的。

然而,在没有使用窗口控制的时候,没有收到确认应答的数据都会被重发。其次考虑发送时丢失情况,接收主机如果收到一个自己应该接收的序号以外的数据时,会针对当前为止收到数据返回确认应答。

而发送端主机如果连续3次收到同一个确认应答、就会将其所对应的数据进行重发。这种机制比之前提到的超时管理更加高效,因此也被称作高速重发控制

3. 流控制: TCP提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。这就是所谓的流控制。它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小

4. 拥塞控制: 如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题。为此TCP引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据。

慢启动:在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加至拥塞窗口;线性增长达到网络拥塞时立即把拥塞窗口置回1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半。

image.png

9. tcp 传输的有序性

发送主机每次发送数据时,TCP就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认,如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等,接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。

10. 线程,进程

1. 线程与进程的区别?

  • 地址空间: 同一进程的线程共享本进程的地址空间,进程是独立的地址空间。
  • 资源拥有: 同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
  • 健壮性: 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  • 性能: 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
  • 执行过程: 每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  • 基本区别: 线程是处理器调度的基本单位,但是进程不是。

2. 多线程是什么,以及优劣

为了解决负载均衡问题,充分利用CPU资源.为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而不互相干扰.为了处理大量的IO操作时或处理的情况需要花费大量的时间等等,比如:读写文件,视频图像的采集,处理,显示,保存等

多线程的好处:

1.使用线程可以把占据时间长的程序中的任务放到后台去处理

2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度

3.程序的运行效率可能会提高

4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了。

多线程缺点:

(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。

(2)对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。

(3)线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。

(4)对公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,从而使前一个线程的参数被修改;另外 ,当公用变量的读写操作是非原子性时,在不同的机器上,中断时间的不确定性,会导致数据在一个线程内的操作产生错误,从而产生莫名其妙的错误,而这种错误是程序员无法预知的。

为什么JavaScript是单线程?

因为 Javascript 作为一门浏览器端的脚本语言,主要的任务就是处理用户的交互,而用户的交互无非就是响应 DOM 上的一些事件/增删改 DOM 中的元素。

对于响应事件是异步处理的,但事件循序也是在单线程中进行的,所有的(可能不太准确)都是被加入到 macro 事件队列中的,一次事件循环也只处理一个事件响应。

所以说 Javascript 被设计成单线程,主要的原因还是在于操作 DOM ,包括在异步的事件处理器中操作 DOM。

设想,如果 Javascript 被设计为多线程的程序,那么操作 DOM 必然会涉及到资源的竞争,那么这么语言必然会被实现的非常臃肿,那么在 Client 端中跑这么一门语言的程序,资源消耗和性能都将是不乐观的,同时在 Client 实现多线程还真没有那么刚性的需求;但是如果设计成单线程,并辅以完善的异步队列来实现,那么运行成本就会比多线程的设计要小很多了。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

性能监控

1. 捕获错误的方法有哪些

1) try catch

try...catch只能捕获到同步的运行时错误,对于语法和异步错误无能为力,捕获不到

2) window.onerror与window.addEventListener('error')捕获js运行时错误

3) 资源加载错误使用addEventListener去监听error事件捕获

4) promise错误不适用catch 可以使用window.addEventListener('unhandledrejection')方法进行捕获处理

5) "script error”

有时也被称为跨域错误。当网站请求并执行一个托管在第三方域名下的脚本时,就可能遇到该错误。最常见的情形是使用 CDN 托管 JS 资源。 解决方案:

script标签添加 crossorigin="anonymous" 属性。添加跨域 HTTP 响应头。之后即可通过 window.onerror 捕获跨域脚本的报错信息。

2. h5白屏问题分析

可能出现的问题:

  • 用户没打开网络
  • DNS域名劫持
  • http劫持
  • cdn或是其他资源文件访问出错
  • 服务器错误
  • 前端代码错误
  • 前端兼容性问题
  • 用户操作出错

手写题

1. 判断靓号:4个及以上连续重复递增

// 判断4个连续重复数字
var reg1 = /(\d)\1{3,}/
// 判断4个连续递增
var reg2 = /(?:0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)){5}\d/

React

react 面试考点见我的react面试考点文章React面试


greet_eason
482 声望1.4k 粉丝

技术性问题欢迎加我一起探讨:zhi794855679