Git Product home page Git Product logo

fe-interview's Introduction

fe-interview

前端面试、复习

fe-interview's People

Contributors

amyuan23 avatar

Watchers

 avatar

fe-interview's Issues

9. JavaScript数据的内存分配

JavaScript数据类型: number、string、bool、symbol、null、undefined、bigInt

原始类型的数据值都是直接保存在“栈”中的,引用类型的值是存放在“堆”中的。

栈空间都不会设置太大,主要用来存放一些原始类型的小数据。而引用类型的数据占用的空间都比较大,所以这一类数据会被存放到堆中,堆空间很大,能存放很多大的数据。

闭包的数据是存放在堆数据中的。

18.HTTP 发展史

http 0.9

  • 就传个html文件,特简单,只有请求行

http 1.x :

  • 多类型数据传输
  • 持久化链接,同时最多6个链接
  • 分块传输数据
  • 引入Cookie和安全机制
  • 虚拟主机支持,请求头的host字段
  • 使用CDN 的实现域名分片机制

http 1.1的问题: TCP 的慢启动、多条 TCP 连接竞争带宽和队头阻塞。

http 2.0
多路复用技术。多路复用是通过在协议栈中添加二进制分帧层来实现的。

  • 一个域名只使用一个 TCP 长连接和消除队头阻塞问题。
  • 一个长链接里面多路复用。每个请求都有一个对应的 ID。
    下载 (7)

二进制分帧:
首先,浏览器准备好请求数据,包括了请求行、请求头等信息,如果是 POST 方法,那么还要有请求体。
这些数据经过二进制分帧层处理之后,会被转换为一个个带有请求 ID 编号的帧,通过协议栈将这些帧发送给服务器。
服务器接收到所有帧之后,会将所有相同 ID 的帧合并为一条完整的请求信息。
然后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。
同样,二进制分帧层会将这些响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器。
浏览器接收到响应帧之后,会根据 ID 编号将帧的数据提交给对应的请求。

http2.0特性:

  • 可以设置请求的优先级
  • 可以直接将数据提前推送到浏览器: 当用户请求一个 HTML 页面之后,服务器知道该 HTML 页面会引用几个重要的 JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求之后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JavaScript 文件,这对首次打开页面的速度起到了至关重要的作用。
  • 头部压缩

多路复用技术能充分利用带宽,最大限度规避了 TCP 的慢启动所带来的问题,同时还实现了头部压缩、服务器推送等功能,使得页面资源的传输速度得到了大幅提升。使用 HTTP/2 能带来 20%~60% 的效率提升

TCP 的队头阻塞:
如果在数据传输的过程中,有一个数据因为网络故障或者其他原因而丢包了,那么整个 TCP 的连接就会处于暂停状态,需要等待丢失的数据包被重新传输过来。
在 TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。

因http2只维持一个链接,所以随着丢包率的增加,HTTP/2 的传输效率也会越来越差。当系统达到了 2% 的丢包率时,HTTP/1.1 的传输效率反而比 HTTP/2 表现得更好。

13.消息队列和事件循环

JS是单线程的。
每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环系统。

下载 (5)

渲染进程内部会维护多个消息队列,比如延迟执行队列和普通的消息队列。然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。这些消息队列中的任务就是宏任务。

每个宏任务在执行时,会创建自己的微任务队列。
如果在执行微任务的过程中,产生了新的微任务,同样会将该微任务添加到微任务队列中,V8 引擎一直循环执行微任务队列中的任务,直到队列为空才算执行结束。也就是说在执行微任务过程中产生的新的微任务并不会推迟到下个宏任务中执行,而是在当前的宏任务中继续执行。

7.闭包

通过作用域查找变量的链条称为作用域链;JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的。

function foo() {
    var myName = " kk "
    let test1 = 1
    const test2 = 2
    var innerBar = {
        getName:function(){
            console.log(test1)
            return myName
        },
        setName:function(newName){
            myName = newName
        }
    }
    return innerBar
}
var bar = foo()
bar.setName(" gg ")
bar.getName()
console.log(bar.getName())

在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包

闭包怎么回收?
通过垃圾回收机制

6.暂时性死区

1650768588529-d86500c0-ad4f-45bb-a603-4f25a68c8782
块级作用域,声明变量提升,但是初始化并没有提升,在初始化之前使用变量,就会形成一个暂时性死区。

14.手写Promise.all()

function customerPromiseAll(promises) {
    return new Promise((resolve,reject)=>{
        let resultCount = 0;
        //计数器
        let promiseLen = promises.length;
        //传入的promise个数
        let results = new Array(promiseLen);
        //初始化数组用于存放返回结果

        //按顺序执行
        for (let i = 0; i < promiseLen; i++) {
            let curPromise = promises[i]
            if (!isPromise(curPromise)) {
                curPromise = Promise.resolve(curPromise)
            }
            curPromise.then(value=>{
                resultCount++;
                results[i] = value;
                //存储resolve的结果
                //保证执行结果的顺序
                if (resultCount === promiseLen) {
                    return resolve(results)
                    //执行完最后一个promise,则返回
                }
            }
            , error=>{
                reject(error)
                //执行错误直接reject
            }
           )

        }
    }
   )
}

5.let和const与var

在 ES6 之前,ES 的作用域只有两种:全局作用域和函数作用域。var 变量提升会带来一些问题,经典的如:

function foo(){
  for (var i = 0; i < 7; i++) {
  }
  console.log(i);
  }
foo() // for 循环结束之后,变量 i 并没有被销毁。

ES6 引入了 let 和 const 关键字,从而使 JavaScript 也能像其他语言一样拥有了块级作用域。
1650716019854-d23d891e-a0ca-48c7-8e12-1cd25229cb15
块级作用域就是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域

1. 进程与线程

进程是任务调度的最小单位,一个任务就是一个进程,线程是最小的执行单位,一个进程里面可以有多个线程。
几个特点:

  • 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
  • 线程之间共享进程中的数据。
  • 当一个进程关闭之后,操作系统会回收进程所占用的内存。
  • 进程之间的内容相互隔离

JS 是单线程的,JS 和DOM渲染共用一个线程,但是可以开启多进程执行
Chrome 进程架构:
1650421709173-64a9394c-ddf7-413a-a768-e9f933d5a007
16年之后,Chrome为了构建一个更内聚、松耦合、易于维护和扩展的系统,向“面向服务的架构”过渡
1650422064271-e278df04-285e-4bab-a7bc-b5f706aec8b2

nodejs如何开启进程,进程如何通讯?

19.浏览器安全

  1. 页面安全
    同源策略: 两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。

同源策略如何保障安全?
DOM 层面:同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作。
数据层面:同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。
网络层面: 同源策略限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。(解决:CORS 跨域资源共享策略)

XSS攻击:往页面中注入恶意脚本
解决:

  • 服务器对输入脚本进行过滤或转码
  • 充分利用 CSP(内容安全策略,可以通过meta标签配置)
  • 使用 HttpOnly 属性

CSRF 攻击- 陌生链接不要随便点: 跨站请求伪造。利用服务器的漏洞和用户的登录状态来实施攻击

  1. 网络安全
    https 加密: 在传输数据阶段依然使用对称加密,但是对称加密的密钥采用非对称加密来传输。
    数字证书机制
    下载 (8)

  2. 系统安全
    渲染进程采用了安全沙箱, 在渲染进程内部不能与操作系统直接交互

4. JavaScript的执行上下文和调用栈

JavaScript 的执行机制:先编译,再执行。
1650708796748-e4da8f71-32fa-4cf6-be0e-b557392bf4f9
当 JavaScript 执行全局代码的时候,会编译全局代码并创建全局执行上下文。
当调用一个函数的时候,函数体内的代码会被编译,并创建函数执行上下文。
而管理执行上下文的栈称为执行上下文栈,又称调用栈。
下载 (1)

  • 每调用一个函数,JavaScript 引擎会为其创建执行上下文,并把该执行上下文压入调用栈,然后 JavaScript 引擎开始执行函数代码。
  • 如果在一个函数 A 中调用了另外一个函数 B,那么 JavaScript 引擎会为 B 函数创建执行上下文,并将 B 函数的执行上下文压入栈顶。
  • 当前函数执行完毕后,JavaScript 引擎会将该函数的执行上下文弹出栈。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”问题。

17.首屏优化

第一步监控 Web 应用的性能数据,定位 Web 应用的性能瓶颈。

可以使用Performance 和 Lighthouse 两个性能检测工具进行检测。相比较而言,Perfomance更细节,Lighthouse则隐藏了一部分细节,更容易理解。
WechatIMG217

第二步进行优化

1651154546441-4e8a236a-7bae-4171-8ff8-06858fd05b66

  1. 减少关键资源个数
  2. 降低关键资源大小
  3. 降低关键资源的 RTT 时间:
    (RTT 就是这里的往返时延。它是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延。)
    可以使用CDN来减少每次 RTT 时长

8.this

this和作用域链是两套不同的系统,它们之间基本没太多联系。

this 可以通过apply,call,bind函数显示指定。

嵌套函数中的 this 不会从外层函数中继承
箭头函数没有this, 箭头函数里的this相当于变量,继承自外层函数。

10. 垃圾回收

这篇文章讲的很好了:https://juejin.cn/post/6981588276356317214

JavaScript 中的原始数据类型是存储在栈空间中的,引用类型的数据是存储在堆空间中的。

栈数据的回收很简单,当一个函数执行结束之后,出调用栈的时候清理。
堆中的垃圾数据,就需要用到 JavaScript 中的垃圾回收器。

V8 中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。
下载 (3)
新生代的垃圾清理首先要对对象区域中的垃圾做标记;标记完成之后,就进入垃圾清理阶段,副垃圾回收器会把这些存活的对象从对象区域复制到空闲区域中,再把原来的对象区域全部清掉,然后对象区域与空闲区域进行角色翻转,如此循环往复。从而避免内存碎片。
在新生区经过两次垃圾回收依然还存活的对象,会被移动到老生区中。
老生区采用标记 - 清除(Mark-Sweep)的算法进行垃圾回收,回收之后会产生大量的内存碎片,因此还要进行标记 - 整理:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

增量标记算法:
进行垃圾回收时,需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。
为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,这样就不会让用户因为垃圾回收任务而感受到页面的卡顿了。

下载 (4)

11.V8 是如何执行一段 JavaScript 代码的?

第一阶段是分词(tokenize),又称为词法分析,将一行行的源码拆解成一个个token。再进行语法分析,将上一步生成的 token 数据,根据语法规则转为 AST。有了 AST 后,那接下来 V8 就会生成该段代码的执行上下文。再根据AST 生成字节码。然后通过解释器执行字节码,通过编译器来优化编译字节码。

字节码+JIT 技术
1650884458987-21a48819-0c8b-4b11-b806-0bb46a24aaaa

15.手写compose和pipe函数

compose的作用就是组合函数,将函数串联起来执行,前一个函数的输出值是后一个函数的输入值。
第一个函数是多元的(接受多个参数),后面的函数都是单元的(接受一个参数)。

例子:

function fn1(x){
    return x + 1
}
​
function fn2(x){
    return x * 10
}
​
function fn3(x){
    return x - 1
}
​
let x = 10
let result = fn3(fn2(fn1(x))) // 109

// 使用compose
let fn = compose(fn3, fn2, fn1)
let result = fn(x)

实现compose

const compose = (...args) => x => args.reduceRight((res, cb) => cb(res), x);

实现pipe

const pipe = (...args) => x => args.reduce((res, cb) => cb(res), x)

忘记reduce的去mdn 看: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

3.重排与重绘

重排:通过 JavaScript 或者 CSS 修改元素的几何位置属性,浏览器会触发重新布局,这个过程就叫重排。重排需要更新完整的渲染流水线,开销很大。

重绘:例如通过 JavaScript 更改某些元素的背景颜色,就是重绘。重绘省去了布局和分层阶段,开销比较小。

2.从输入 URL 到页面展示,这中间发生了什么

知识大串烧~

  1. 用户输入URL之后,进入页面资源请求过程。浏览器进程会通过进程间通信(IPC)把 URL 请求发送至网络进程。
  2. 网络进程会查找本地缓存是否缓存了该资源(DNS缓存和页面资源缓存)。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。
  3. 网络请求第一步是进行 DNS 解析
  4. 等待TCP队列:同一个域名同时最多只能建立 6 个 TCP 连接,如果满了,则须等待
  5. 3次握手建立TCP链接
  6. 发送http请求,服务器处理请求,返回响应头和响应体。
  7. 如果http响应行的状态码为301、302等重定向信息,则浏览器会跳转到新的地址,重新请求
  8. 请求完毕,4次挥手断开TCP链接。
  9. 拿到响应体数据之后,浏览器进程会发出“提交文档”消息,渲染进程接收到之后会和网络进程建立数据传输管道,从而将响应体数据传输给渲染进程。等数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程。浏览器进程会更新浏览器界面状态。如前进后退的历史状态。
  10. 渲染进程开始页面解析和子资源加载
  11. html 构建DOM树,css构建cssom树,两者结合,形成renderTree
  12. 页面生成完成,渲染进程会发送一个消息给浏览器进程,浏览器接收到消息后,会停止标签图标上的加载动画

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.