收集

计算机网络

1.以什么为基准去衡量什么时候使用base64

ascii码用于编码二进制数据,共128个字符,其中0到31和127为控制字符(非打印字符),无法在互联网中传输,如果要传输这32个字符数据,则需将其编码为可打印字符,base64就是这种编码技术之一,也就是将二进制数据转为base64数据

Base64,就是使用64个可打印字符来表示二进制数据的方法。Base64的索引与对应字符的关系如下表所示:0~63分别对应了唯一一个字符,比如18对应的是S。


image.png

编码过程:字符数据 --- ascii码 --- 二进制数据 --- base64


image.png

js中可使用btob()和atob()方法来编码译码base64

base64优缺点
优点:可使二进制数据在http中传输
缺点:3字节数据MAn转为base64后变成4字节TWFu,体积增大四分之一,解码编码需要额外工作量

一般图片请求的请求头会包含Request URL、Accept-Encoding、Accept-Language、Cache、Control、Connection、Cookie、Host、Pragma、Referer、User-Agent等信息,体积一般在400Bit到1000Bit之间

结合base64体积增大四分之一计算,则图片资源大小在4000B以下时,使用base64较为合理

2. 画出SSL四次握手过程

image.png

1 客户端发送client hello(ssl/tls等加密算法列表)
2 服务端接收后返回server hello(加密算法子集)、证书(公钥、颁发机构、过期时间、域名等信息)
3 客户端验证证书、取出公钥、通过公钥加密一个随机值为会话秘钥,传送该加密信息
4 服务端通过私钥解密该加密信息,取出会话秘钥,再加密该会话秘钥返回给客户端
5 客户端验证通过后,后续使用该会话秘钥传输加密内容

3. 请问SSL握手时有对称加密和非对称加密吗? 如何优化这一层?

客户端通过公钥加密会话秘钥,服务端通过秘钥解密该会话秘钥,这个过程是非对称加密
客户端再完成交换后使用会话秘钥加密内容,服务端收到后通过会话秘钥进行解密,这个加密方式是对称加密

如果将所有的HTTP连接变为HTTPS连接,则明显RSA的解密最先成为瓶颈。因此,RSA的解密能力是当前困扰HTTPS接入的主要难题。

1、CDN接入

选择使用 CDN 作为 HTTPS 接入的入口,将能够极大减少接入延时。

2、会话缓存

虽然前文提到 HTTPS 即使采用会话缓存也要至少1*RTT的延时,但是至少延时已经减少为原来的一半,明显的延时优化;同时,基于会话缓存建立的 HTTPS 连接不需要服务器使用RSA私钥解密获取 Pre-master 信息,可以省去CPU 的消耗。如果业务访问连接集中,缓存命中率高,则HTTPS的接入能力讲明显提升。

3、硬件加速

为接入服务器安装专用的SSL硬件加速卡,作用类似 GPU,释放 CPU,能够具有更高的 HTTPS 接入能力且不影响业务程序的。测试某硬件加速卡单卡可以提供35k的解密能力,相当于175核 CPU,至少相当于7台24核的服务器,考虑到接入服务器其它程序的开销,一张硬件卡可以实现接近10台服务器的接入能力。

4、远程解密

本地接入消耗过多的 CPU 资源,浪费了网卡和硬盘等资源,考虑将最消耗 CPU 资源的RSA解密计算任务转移到其它服务器,如此则可以充分发挥服务器的接入能力,充分利用带宽与网卡资源。远程解密服务器可以选择 CPU 负载较低的机器充当,实现机器资源复用,也可以是专门优化的高计算性能的服务器。当前也是 CDN 用于大规模HTTPS接入的解决方案之一。

5、SPDY/HTTP2

前面的方法分别从减少传输延时和单机负载的方法提高HTTPS 接入性能,但是方法都基于不改变HTTP 协议的基础上提出的优化方法,SPDY/HTTP2 利用TLS/SSL 带来的优势,通过修改协议的方法来提升HTTPS 的性能,提高下载速度等。

4. 一个 tcp 连接能发几个 http 请求

http1.0 一个tcp连接只能发送一个http请求, 可手动设置connection为keep-alive来使tcp保持连接

http1.1 默认设置connection为keep-alive,一个tcp连接可发送多次http请求,理论上在keep-alive-timeout设置的超时时间之内可以无限次发送http请求,所以需控制好这个超时时间,避免服务器端口被长期占用,tcp的keep-alive是用于发送心跳包,保证连接的可靠性,多个心跳包没收到,随即关闭连接
如何得知请求已接受完成,并开始超时计时呢? http1.1中使用content-length来标识内容长度,接收到请求时判断内容长度是否达到length,达到则认为接受完成,如果内容过大,有分片情况,则判断最后一个分片的长度是否为0
http1.1只能单路单用,浏览器默认tcp最大同时连接数为6个,如果页面需要同时请求10个资源,那么前6个会并行加载,剩余4个串行

http2.0中增加了多路复用,可使一个tcp连接同时并发多个http请求

5. 前端浏览器输入URL后发生什么?html文件获取,它是如何传输的?

输入url后

  1. 首先浏览器会查看浏览器缓存的DNS列表,如果命中直接返回对于的ip地址
  2. 否则会继续查看系统中的host文件和系统缓存中是否有已解析后的结果,有则返回
  3. 如果还没查到,则会去当地DNS服务器(LDNS)去查询,查到则返回,一般80%的域名可在该服务器查询到并解析
  4. 否则LDNS继续向根服务器(root)查询,root会返回顶级域名服务器ip
  5. LDNS去顶级域查询到二级域名服务器ip
  6. LDNS去二级域名服务器查询三级域名服务器ip

A记录解析域名到一个固定的IP,Cname记录解析域名到另一个域名,称为别名

修改host文件、海外DNS解析被墙、DNS劫持都可导致DNS解析到错误的IP上(DNS劫持可通过修改本机系统DNS设置、路由器设置、运营商服务器被劫持等,无法完全杜绝,可加速服务器缓存来优化)

最终返回完整ip地址,通过ip地址和端口号浏览器开始建立TCP连接,访问对应的web服务器一般为nginx,web服务器接收到请求后,开始分析路径,根据web服务器配置的router去寻找对应的服务,如果最终分析是html请求,nginx服务器会自动处理头信息和返回体并返回给客户端,客户端进行构建渲染

6. http1.0、1.1、2.0、3.0的区别

三者的差别主要体现在tcp的连接数上

http1.0在建立tcp连接后只能发送一个http请求,tcp随即关闭,后续再请求需重新建立tcp连接,属于短连接模式

http1.1在1.0的基础上增加了keep-alive模式,同时设置请求头connection来控制,close代表关闭,timeout设置超时时间,在建立tcp连接后,在超时时间范围内,客户端可发送多次http请求,属于长连接模式,同时1.1支持单独发送请求头信息到服务器,待接受到服务器返回100返回码后继续发送请求体

http2.0在1.1的基础上增加了多路复用、请求头压缩、服务器推送等功能,多路复用可使在建立完tcp连接后,多次多并发的发送http请求,1.1的并发只能同时支持浏览器限制的6个,每个都需建立新的tcp连接

HTML/CSS

1. CSS下载解析会不会阻塞DOM树渲染

css下载解析不会阻塞DOM树渲染,是两个不同的线程解析,但是会影响render树的构建,render需要等待css下载解析完

2. 有没有可能让JS下载解析不阻塞DOM树构建

可以使用在script标签中使用async 或 defer = "true",async可异步并行加载js,加载完成后就执行,无论声明顺序如何;defer同样是异步加载,但它会在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成,顺序执行

JS基础

1. 什么是浅复制和深复制?有什么区别?如何实现Object的深复制

复制对象时,浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据,其中属性存储和引用指针在栈内存中,数据存在堆内存中

深拷贝就是对对象以及对象的所有子对象进行拷贝

// 递归
function copy(sourceObj) {
    const targetObj = {};
    Object.keys(sourceObj).forEach((key) => {
        const value = sourceObj[key];
        const type = Object.prototype.toString.call(value).slice(8, -1);
        if (type === 'Array') {
            targetObj[key] = [];
            value.forEach((arrItem) => {
                targetObj[key].push(arrItem);
            });
        } else if (type === 'Object') {
            targetObj[key] = copy(value);
        } else {
            targetObj[key] = value;
        }
    });
    return targetObj;
}

2. 知道什么是事件委托吗?

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件

无需绑定多个li 节约内存、优化性能,同时未来某个元素插入,不必重新绑定事件

3. new和instanceof的内部机制

new两个作用,一是将this指向自身对象,而是将自身对象的原型指向构造函数的prototype

function Test(name) {
    this.name = name;
}

function new2(...params) {
    const obj = Object.create(Test);
    Test.apply(obj, params);
    return obj;
}

new2('xiao') // Test {name: "xiao"}
new Test('xiao') // Test {name: "xiao"}

a instanceof b 判断b.prototype是否出现在a的原型链上

    const xiao = new Test('xiao');
    xiao instanceof Test // true

vue

1. vue的初始化,发生了什么
执行new Vue()后,vue会首先进入初始化阶段(初始到create),该阶段会初始化一些属性、事件、响应式数据,具体步骤为:

  1. 初始化实例属性(initLifecycle),比如parent、root、$children等供外部使用的属性,以及_watcher、_isDestoryed、_isBeingDestoryed等供内部使用的属性

  2. 初始化事件(initEvents),将在父组件中注册的事件添加到子组件的事件系统中

  3. 初始化render(initRender)初始化渲染相关的属性以及方法,slots、vnode、_c、$createElement

---beforeCreate--

  1. 初始化inject(initInjections), inject和provide一起使用,他们允许祖先组件向其所有子孙后代注入依赖

  2. 初始化状态(initState),props、methods、data、computed、watch

  3. 初始化provide(initProvide)

---created--

2. vue的模板解析,是如何进行的?如何形成AST?render函数的生成?什么是依赖收集?什么是patch?数据更新策略等

通过三种解析器(html解析器、过滤器解析器、文本解析器)对模板进行解析,其中最核心的是html解析器,通过词法分析(正则匹配)找到元素的开头、属性、内容、注释、结束等信息,找到时分别触发start、end、chars、comment等钩子函数,通过栈结构来保存AST结果对象,while循环来解析,如果在解析中存在文本变量,则需二次调用文本解析器处理,循环结束后,可生成AST树结构

在经过优化器标记静态节点后,代码生成器开始递归每一个AST节点来生成代码字符串,不同类型节点分别调用_c(元素) _v(文本) _e(注释)来生成对应的字符串,最后生成一段类似with(this) {return _c('div', {attrs: {'id': 'el'}})}的字符串作为render函数

依赖在vue中就是watcher,在vue挂载阶段会将组件(vue实例)中的watcher收集起来,包括模板编译实例化的watcher以及调用$watch方法后实例化的watcher,watcher实例化时会将自身this保存在全局某个位置并通过访问data属性,触发data的getter,该getter函数会在全局中将watcher取出在存放在dep中,而后当该数据触发setter时通知dep中的watcher执行渲染操作,这个过程称为依赖收集

patch是vnode进行对比,然后根据对比来新增、移动、删除、修改节点以及渲染为真实dom的一个过程,子节点进行优化循环对比新增、修改的节点会插入到未处理的节点之前,循环结束后未被处理的oldchildren将直接删除,通过document.createElement来创建真实dom元素节点,创建完成后通过apendChild方法来插入到dom中

3. Virtual Dom 的概念以及优势在哪里

虚拟dom就是一个虚拟节点树,在vue中是由vnode组成的一棵树,vnode其实就是一个js对象,用于描述真实dom,新生成的虚拟节点树会和上一次生成的进行对比,只渲染不同的部分;

首先虚拟dom的创建、更新、移动、删除等操作是在js引擎中完成,速度会比直接通过操作dom来说快很多,同时虚拟dom可以缓存,在渲染时只需对比新旧节点的差异,然后将少量节点做真实dom操作

至于vue2.0为何开始引入虚拟dom,是因为1.0的检测颗粒太细,模板中每一个状态都会生成一个watcher来观察变化,当节点很多时,会造成大量的内存开销和追踪依赖开销,引入虚拟dom后,检测颗粒变为中等,每一个组件(实例)模板中只会生成一个watcher来观察变化,状态变化时会通知到组件,然后组件内部再通过虚拟dom来进行对比和渲染页面

虚拟dom会触发新建vnode、对比、计算、比1.0直接状态更新触发节点更新的方式,多了更多的步骤,相当于是用时间换空间

4. 响应式即变化侦测的原理

vue通过侦测变化来实现响应式系统,侦测变化主要的是通过es5提供的object.definepropty方法来实现,该方法可以设置对象属性的getter和setter,当该属性被访问时触发get函数添加watcher依赖,当属性值修改时触发set函数通知watcher更新视图,vue会在初始阶段会通过observer对data中的所有属性递归设置getter和setter,这种方式无法追踪新增属性和删除属性,也无法侦听到数组的push、pop、reverse、splice、shift、unshift等更改数组本身的操作,对象使用set和delete来解决,而数组使用拦截器的方式来解决,拦截器继承自Array.prototype,修改push等方法使在原本功能不变的基础上添加其他行为,比如发送变化通知,然后修改data中为数组类型的值的原型为拦截器,不支持proto方式设置的浏览器则直接添加到数组的属性上,数组中的值也会循环丢入observer中处理为响应式数据,无法监听arr[0] = 1这种操作

6. vue.js的生命周期钩子之间的区别

初始化自定义事件
beforeCreate
初始化props、data、方法、计算属性
created
模板编译,el自动挂载或mount手动挂载 beforeMount 开始挂载,创建虚拟dom创建真实dom插入到文档中 mounted 状态发生改变 beforeUpdate 虚拟dom对比更新真实dom updated 调用destory()
beforeDestroy
卸载依赖追踪、子组件与事件监听器
destroyed 已卸载

7. 各种API内部实现原理

8. 指令实现原理

9. v-if 和v-for为什么不能一起用? v-for中为何要加上key?

v-for比v-if拥有更高的优先级,即使部分元素被设置为v-if为false,渲染时还是会遍历整个列表,建议使用计算属性过滤掉列表后再渲染,或者将v-if添加到父元素ul中

在模板中使用key在模板解析后,在patch过程中会建立唯一key和节点索引index之间的对应关系,那么在列表更新列表触发patch对比查找时,不需要在旧子节点oldchildren中循环查找,可直接通过key获取节点,从而提高性能,在有很多子节点的列表中更明显

10. keep-alive、$nextTick

11.组件间传递值

前端性能
说下前端性能优化

首先,前端性能是一个前端中涉及很广、也很重要的一点,大概可以三类来优化,页面HTML/CSS相关、JS相关、以及网络相关;

HTML/DOM/CSS

  1. 非页面依赖的js文件加载放在body后执行,或者通过defer属性来异步加载,保证js的加载和执行不会阻塞到主渲染进程
  2. 元素的新增、删除、移动都会触发浏览器对页面dom的重排或重绘,减少页面的重排重绘,可使需要经常更改的元素脱离文档流、display:none后进行一系列操作后再display:block等方式
  3. 动画是个很耗性能的东西,尽量用css3来实现动画,浏览器对css3动画有优化,可通过设置transform3d的方式来开启GPU进一步提高渲染性能,如果非得用js来实现动画,可使用js的requestAnimationFrame方法来实现,该方法的第一个参数传递一个回调函数,该回调函数会在屏幕下一个刷新周期结束后执行,屏幕刷新频率(一般设置为60HZ)是根据当前系统性能来动态调整的,这样可保证动画不会掉帧、卡顿;还有svg、webGl、canvas等动画的实现

JS

631385108

  1. 避免内存占用过高,全局变量挂载的对象、定时器、事件、闭包中的变量;使用过的变量,会被浏览器通过标记清除法进行垃圾回收;内存占用过高会导致性能变差甚至应用崩溃,可通过浏览器工具来查看当前内存占用

  2. 减少作用域查找、dom元素查找、原型链查找的成本,尽量使用局部变量、当前对象上的属性访问以及存储dom元素引用到一个变量上

  3. 避免使用eval、with等可修改当前作用域的方法,作用域修改会导致浏览器作用域相关优化失效,99%的情况下会极大影响性能同时也有安全风险,另外1%可参考vue的模板编译后的with,这里的with只影响了虚拟dom的性能,对比可能出现的大量的编译器代码来说,是可以接受的

  4. 函数节流、防抖,避免触发大量操作,可使用loadash中的方法,也可以自己实现

// 防抖
function debounce(fn, wait) {
    let timer = null;
    function debounced() {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            clearTimeout(timer);
            fn();
        }, wait)
    }
    return debounced;
}

// 节流
function throttle(fn, wait) {
    let flag = false;
    let timer = null;
    function throttled() {
        if (flag) return;
        flag = true;
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            clearTimeout(timer);
            flag = false;
            fn();
        }, wait)
    }
    return throttled;
}
  1. 避免出现死循环、循环次数过多,对算法进行优化,递归算法尾部调用优化

网络

  1. http缓存,强制缓存、协商缓存

强制缓存的返回头有expires(http1.0)、Cache-control两种,expires的值是一个过期时间,在过期时间内强制缓存该请求结果, Cache-control值有no-cache(浏览器不做强制缓存检查)、no-store(浏览器和中间代理服务器都不能缓存资源)、private(资源不允许被中间代理服务器缓存)、public(资源允许被中间服务器缓存)、must-revalidate(可以缓存,但是使用之前必须先向源服务器确认)、proxy-revalidate(要求缓存服务器针对缓存资源向源服务器进行确认)、
s-maxage(缓存服务器对资源缓存的最大时间)、max-age=1000(相对最大缓存时间)等多种字段控制,Cache-Control 对缓存的控制粒度更细,包括缓存代理服务器的缓存控制,它的优先级比expires高

协商缓存是客户端和服务器通过http请求头的设置来协商是否缓存资源的方案,在返回头中主要是Last-Modified和Etag。
初次请求服务器后,浏览器会将服务器返回的Last-Modified的值和返回内容进行缓存,如果该请求没有被强制缓存,在下次请求时浏览器会将之间存储的修改时间值通过If-Modified-Since字段发送给服务器,服务器对比该值和文件最后的修改时间,如果是一样,则返回304,不一样则返回200返回码以及新的内容和最新的Last-Modified,从而实现缓存和更新

Etag的原理差不多,请求头中通过If-None-Match字段携带上一次请求返回的etag值,服务器对比该值和通过文件内容计算的Etag值,一样则返回304,否则返回200以及最新的内容和最新的Etag

缓存的一些缺点
expires的设置不稳定,用户更改系统时间,则出现误差
last-modified无法精确到秒
etag需耗费服务器资源来生成hash,在多台服务器负载均衡的情况下会失去意义
在强制缓存期间,如何得知服务器资源的更新并更新本地资源(html不缓存,其他资源加上版本号)

  1. 雪碧图、base64、iconfont

雪碧图将多张小图合并到一张大图上,通过设置背景位置的方式来展示图标,通过这种方式来达到减少http请求提高性能,需合成图片、维护成本较高

iconfont将svg图片通过工具转成字体文件,使用时可当做普通字体来使用,设置font-size 、color等,体积比图片小,并可以自由伸缩不会模糊,对图标有要求,需是纯色、大小在固定返回内、图形需闭合等等

base64方式是将小图片转为base64格式字符串,打包进css或者js文件中,减少http请求,达到提高性能的目的,缺点是会将原始数据体积增加1/4,适用于小于4kb左右的图片,否则得不偿失

  1. 代码压缩、tinypng、gzip压缩、webpack优化

代码可结合webpack或其他打包工具来压缩,webpack中使用的uglify插件

tinypng可对png图片、jpg图片进行无损压缩,可压缩80%

在nginx服务端需开启gzip压缩,并设置压缩倍数

当js文件过大并且有重复代码时webapck可进行拆包和提取,通过dllPlugin来将vue、vue-router等网页依赖的基础模块抽离出来打包成一个单独的base.js文件(利用浏览器缓存下来),通过commonChunkPlugin将多个chunk中的公共部分提取出来为common.js,最后通过webapck-bundle-analyzer插件来查看各个模块大小及其依赖关系

  1. nginx服务器、cdn服务器、dns服务器

多台nginx服务器使用F5进行负载均衡、使用nas磁盘同步资源更新
平安内部cdn加外部供应商cdn服务器,采用回源的方式拉取nginx文件
dns服务器由内部统一解决

  1. 懒加载、预加载、按需引入

懒加载在需要的时候再加载,随再随用,可减少首页压力,vue中的路由懒加载
预加载,预先加载一些资源,比如动画图片,避免在放动画时在加载,影响体验
按需引入,比如按需引入loash函数、element-ui组件等,避免整体引入

  1. http2.0、http3.0

http2.0在1.0的基础上增加二进制数据、多路复用、服务器推送、请求头压缩等,可共用同一个tcp连接多路并发http请求

HTTP3.0,也称作HTTP over QUIC。HTTP3.0的核心是QUIC(读音quick)协议,由Google在2015年提出的SPDY v3演化而来的新协议,传统的HTTP协议是基于传输层TCP的协议,而QUIC是基于传输层UDP上的协议,可以定义成:HTTP3.0基于UDP的安全可靠的HTTP2.0协议。

减少了TCP三次握手及TLS握手时间、多路复用丢包时的线头阻塞问题等

浏览器

  1. webworker可开启另一个线程处理复杂计算问题,线程间通过事件消息机制相互联系,传递时使用Transferable对象可避免双倍内存,通过new Worker('worker.js')的方式开启子线程,脚本受同源限制,无法访问主进程中的dom对象,通过close方法关闭子线程

  2. pwa

pwa翻译过来就是渐进式web应用,使用serviceWorker来拦截页面请求,并根据网络是否可用判断是否使用缓存数据或者更新缓存数据。catheStorage中存储缓存数据,它们还允许访问推送的通知和后台的API。

维护一个json文件并通过<link rel="manifest" href="./manifest.json">的方式引入,来创建桌面小图标

核心技术

  1. Web App Manifest 通过manifest.json设置 PWA 的启动画面的图标和颜色等

  2. Service Worker 是 PWA 中最重要的概念之一,它是一个特殊的 Web Worker,独立于浏览器的主线程运行,特殊在它可以拦截用户的网络请求,并且操作缓存,还支持 Push 和后台同步等功能。Service Worker 通过 Cache Storage 、Cache API 操作本地缓存,以及通过 fetch API 请求服务器端数据,不管是否有网络连接,或者站点发生了 404 、500,都可以让用户看到特殊定制的错误页面,而不是浏览器的默认 404 页面。

  3. App Shell 和 App Skeleton

以 Vue 的项目举例,AppShell 包含:

入口 HTML 文件

打包好的 Vendor JS 文件

导出的 CSS 文件

如上图所示,App Shell 渲染出了 header 部分,那么正文部分在加载数据之前,都是白屏,这对于用户来说体验非常不好,有一个名词叫骨架屏(App Skeleton),在渲染出数据之前,在白屏位置占位,尽量不出现长时间的白屏。

App Skeleton 需要在最短的时间内渲染给用户,所以,一般情况下,会将 App Skeleton 编译到 HTML 里,就像下面的代码,期望浏览器在加载完 HTML 之后就能先显示骨架屏。

PWA 全称是 Progressive Web Apps,意味着是渐进式的,也就是在现有的基础上进行逐渐添加,从而改善用户体验,并不需要推倒重来,对整个站点进行改造

PWA 在 2017 年初,仅仅 Chrome 和 Firefox 支持 PWA,经过一年的发展,国内主流浏览器都已经支持 PWA,iOS 在 新发布的11.3 版本中也支持了 PWA。

离线包方案

load.png

1.Node里面的模块是什么?

node中的模块分为两类、核心模块、文件模块,其中最常用的是文件模块,包含项目中文件、node_modules中的包,核心模块由nodejs提供,例如fs模块、os模块、net模块等等,nodejs中通过require可导入其他模块、也可以通过mudule.exports导出自身

2.require的模块加载机制

文件模块和核心模块不一样

文件模块分为路径分析(相对路径、绝对路径、自定义模块路径)、文件定位(js、json、node后缀名顺序,package.json中的main,默认index.js)、编译执行几步,

node在启动时会编译核心模块保存在内存中,require时直接在内存中加载

js文件通过fs模块加载后编译,json文件通过json.parse()直接处理后赋值给模块对象的exports属性,node文件不需要编译直接通过dlopen方法加载,加载后将模块的exports对象和node模块产生联系并返回给调用者;js文件编译过程中对模块用函数进行头尾包装,隔离作用域,通过vm原生模块的runInThisContext()方法来执行,函数参数列表传递require、exports、module、__fliename、__dirname,函数执行后回传module的exports属性,使外界可访问

模块加载后都会缓存,下次require时绝对优先从缓存中获取,缓存 > 内存 > 文件定位查找 > node_modules查找

核心模块在node启动时会进行编译,js核心模块代码会被转成字符串通过c++中的数组存储在内存中,在require引入时从内存读取并添加头尾包装

c++内建模块通常不被用户调用,而是被js核心模块调用,require时无法查找,直接从内存中获取,也无需编译,直接执行,process.binding方法将内建模块内部变量和方法导出,改方法会新建一个空的exports对象,并填充对象,实现导出,js核心模块就是通过这个方式导出的

  1. exports和module.exports的区别

exports其实就是module.exports,exports通过形参的方式传入,直接复制形参会改变形参的引用,切断和module的引用

  1. node事件循环的流程

node启动后会开启一个类型于while的循环,每一次循环称为一个tick,在每一个tick中会询问观察者是否有待处理事件,如果有则取出对应回调函数执行,并进入下一个tick,直到没有事件处理退出循环,观察者在node中有多种,网络IO观察者、文件IO观察者等,node的事件循环是典型的生产者/消费者模式,网络请求就是事件的生产者,事件循环就是消费者

异步调用时,首先封装一个请求对象,对象中包含数据和回调函数,将请求对象丢入底层libuv的线程池等待执行,线程可用时执行请求对象中的IO操作,完成后将结果放在请求对象中,通知观察者,事件循环取出回调并执行

除了网络、文件IO外,setImmediate、settimout、setinterval和process.next函数也会触发异步调用,定时器会有一个专门的定时器观察者,和IO不同的是它不需要线程池的参与,定时器观察者内部会维护一颗红黑树,调用定时器时会向树中插入自身,每次tick时会从树中迭代检查定时器是否超时,超时则形成一个事件,立即执行回调函数,由于其他任务执行需要消耗时间,所以定时器的时间会不准

nextTick的方式会更轻量,直接将回调函数放入队列中,在下轮tick时取出执行

事件循环是异步的核心

5.V8的内存限制是多少,为什么这么设计

32位操作系统下是0.7G,64位操作系统是1.4G buferr等内部模块可突破限制,也可以设置这个大小

垃圾回收会引起js线程暂停执行,对1.5G内存进行一次垃圾回收需要50毫秒以上,做一次全量式垃圾回收需要1秒以上,这种花销会引起性能直线下降,所以控制内存使用,并且在浏览器的使用情况下,1.4G已经完全足够

  1. V8垃圾回收

V8将内存分为新生代和老生代两部分,新生代存储短时间内会被销毁的对象,老生代存储长时间存活的对象,新生代采用scavenge算法,将内存一分为二,from和to两部分,对象存入form中,将存活的对象从from中复制到to中,非存活对象直接丢弃,to和from交换,经过一个scavenge还未被回收的或者to空间的占用超过25%,对象会被放入老生代中,老生代才用标记清除算法来处理对象,将存活的对象进行标记,然后删除未被标记的对象,删除后会产生非连续空间,在空间不足时,使用mark-compact进行移动,移动完成清理边界外的内存,全量垃圾回收会时应用停顿时间过长,顾采用增量的方式步进,回收一下,应用执行一会

7.内存泄漏

应当被回收的对象没有被回收,并常驻在老生代中,称为内存泄漏

常见三种,一种是缓存,比如全局对象或闭包对象越来越大,得不到回收,一种是队列消费不及时,队列中元素的产生的速度大于消费的速度,第三种是作用域未释放(闭包,每一个模块都是);

内存泄漏导致内存占用持续增加,最终可达到超出限制,应用崩溃;

应当使用redies来缓存进程间可共享状态也可以让垃圾回收更高效;使用node-headdump来生成内存快照,然后放在谷歌浏览器中分析,通过流的方式来处理大文件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容