面试题第六期

134 阅读8分钟

1 从浏览器进程角度系统全面回答从输入URL到页面显示发生了什么

浏览器是一个多进程架构 所谓的多进程 就是多个人同时进行一件事
而我们浏览器主要分为 浏览器主线程 GPU线程 渲染线程以及网络线程 加上存储相关的线程

  • 浏览器主线程 负责页面显示 用户交互 子线程的管理 储存功能
  • 渲染线程 主要任务 就是把html css js渲染成网页
  • 网络线程 顾名思义 就是负责网络的线程 网络图片加载
  • 存储相关的线程 就是数据存放的线程

我们分为三个进程 浏览器主线程 渲染线程 网络线程
步骤

  1. 先在浏览器地址栏输入一段东西
  2. 判断这个东西是网址还是关键字 如果是关键字 就直接拼接搜索引擎 发送到网络进程准备进行url请求 如果是网址 直接发送到网络进程准备发起URL请求
  3. 发起url请求前 先查看有没有存储 如果有 并返回资源 如果没有 就发起url请求
  4. 进行dns解析url 获取到url上的域名和对应的服务器的ip地址
  5. 连接服务器 建立起一个tcp的链接 所谓的tcp 就是三次握手四次挥手 构建数据并发送到服务器 服务器收到数据 就会生成一个相应数据返回给我们 就会有响应头响应体
  6. 我们的浏览器会根据响应头的字段content-text 这个字段告诉浏览器 获取到什么类型的数据 这样对页面的解析
  7. 读取完网络进程后 就会准备渲染进程
  8. 渲染进程把html css js进行解析
  9. 是从html解析成tokens tokens解析到dom节点 同时解析css 把css和dom节点构建出render树
  10. 建立网络进程的管道 获取响应体数据 并渲染 然后提交到主线程
  11. 主线程收到确认信息 在把页面的图片等资源经过渲染进程解析出来 渲染到页面 完成了页面内容加载

2.浏览器解析渲染HTML

浏览器从html css js文件开始 到将它们最终以像素输出到屏幕上这一过程 叫关键渲染路径,包括以下几个部分

  1. 构建DOM

    • 将html解析成tokens
    • 将tokens解析成object
    • 将object组合成为一棵DOM树
  2. 构建CSSOM

    • 解析CSS文件 并构建一棵CSSOM树 (过程类似于DOM创建)
  3. 构建render tree

    • 结合DOM树和CSSOM构建出一棵Render树
  4. Layout

    • 计算出元素相对于viewport的相对位置
  5. paint

    • 将render tree转换成像素 显示在屏幕上

注意 这个过程并不是依次进行 而是存在一定交叉的

3 css匹配规则为何从右向左

例如 .a .b .c{color:red;} 我们都以为是从左到右寻找类名 其实恰恰相反 是从右向左寻找类名、因为css太过庞大了 如果从左向右寻找 就会先找外面的类名为a的标签 再从类名为a的标签里寻找类名为b的标签 把不符合的舍弃 这样的话 一个css文件匹配规则或许有上千条 而且对于当前节点来说 大多数规则都匹配不上 到此为止 那如果是从右往左呢 那就会完全不一样了 先从最精准的位置开始 可以更快排查出不合适的节点 从而大大减少资源

10月9日

1 三次握手四次挥手

发请求是采用http请求 建立http链接之前,建立TCP链接-三次握手

  1. 首先客户端向服务器先发起一个链接 会携带一个参数 叫SYN=1 --客户端看服务器在不在
  2. 服务器接受链接 返回客户端两个参数 SYN=1 ACK=1 表示告诉客户端我活得好好的
  3. 客户端收到服务器两个参数 表示 我知道你是可以通信 可以建立链接了

四次挥手 断开链接的时候 需要四次挥手

  1. 客户端向服务器发送一个FIN=1的参数 告诉服务器 我要断开链接了
  2. 服务器收到FIN=1之后 先发一个ACK=1 并没有回发FIN=1 会告诉我知道了 但是我现在还不能断 因为有可能数据还没传输完成
  3. 当服务器传输完成后 客户端会再给服务器发送一个FIN=1 确认要断开了
  4. 这次客户端还会再确认断开链接 经过ZMSAwit阶段 等待关闭

2 js事件循环

js是单线程 所谓的单线程是代码依次一行一行的执行 上一行不执行 下一行没法继续执行 这样的好处就是从上到下依次执行 好理解 但也有一个问题 如果中间的某一行代码出现了错误 下面的代码就执行不了 就被堵塞了 而为了解决堵塞这个问题 js提供了事件循环机制 来模拟多线程 所谓的多线程就是同时执行多个任务 不会出现堵塞问题 谁也不会影响谁 但理解起来颇为麻烦

  1. 同步模式 代码按照顺序依次执行 前面没有执行 后面就不会执行
  2. 异步模式 代码可以同时执行 前面没有执行完 后面也可以执行

为了解决异步模式 js推出了宏任务和微任务

  • 宏任务:
    setTimeout setInterval Ajax DOM事件 script(整体代码)

  • 微任务:
    Promise.then async/await Object.observe MutationObserver

执行顺序是 主线程 微任务 宏任务

  1. 所有的同步任务都在主线程上执行,行成一个执行栈。
  2. 除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记。
  3. 主线程完成所有任务(执行栈清空),就会读取任务列队,先执行微任务队列在执行宏任务队列。

3 闭包

闭包就是通过某种方式获取函数内部的变量 然后暴露到函数外部 并且在将来的某个节点还可以继续操作该变量 闭包可以延长函数的生命周期

  • 优点 可以防止污染我们的全局作用域
  • 缺点 可能会导致内存泄漏的问题
var obj={}
function f(){
    let a=10
    obj.get=function(){
        return a
    }
    obj.set=function(v){
        a=v
    }
}
f()

//调用obj
obj.set(15) // 15

闭包会产生一种 你感觉不到它的存在 但是它确确实实存在着

10月10日

宏任务和微任务

微任务在DOM渲染前执行 比如 Promise.then 宏任务在DOM渲染之后执行 比如setTimeout

作用域链

当一个变量在当前作用域无法找到时,便会尝试寻找其外层的作用域,如果还找不到,再继续往外寻找(只会往外寻找,不会寻找兄弟作用域,更不会往内寻找)。这种如同链条一样的寻找规则便被称为作用域链。

如果找不到变量会怎样?

如果一个变量直到全局作用域也找不到便会执行以下操作。

  1. 非严格模式:隐式声明全局变量
  2. 严格模式:报错

非严格模式

非严格模式下,尝试赋值一个变量时,如果找不到则会隐性声明成全局域的变量。

{
    variable = "我是一个隐性声明的变量"
}
console.log(variable)

输出:

我是一个隐性声明的变量

严格模式

加入 "use strict" 表明是严格模式,严格模式下不论赋值还是使用未事先声明的变量都会报错。

作用域的好处?

  1. 防止命名冲突:你写了一万行的代码文件,如果没有作用域,你要给每个变量取独一无二的名字,屁股想想也知道是种折磨。
  2. 安全性: 变量不会被外部访问,保证了变量值不会被随意修改。你定义在函数内的变量,如果能在几千行之后不小心被修改,脚趾头想想也知道是种折磨。
  3. 更高级的语法:封装、面向对象等的实现离不开对变量的隔离,这是依靠作用域所达到的。

原型

1.什么是原型

在js中,使用构造函数来创建一个对象时, 每一个构造函数中都有一个prototype属性,它的属性值 是一个对象,这个原型对象包含了该构造函数的所有实例共享的属性和方法

对象原型(隐式原型)__proto__.

当用构造函数创建一个实例后,实例对象中包含一个指针(__proto__ 对象原型) ,这个指针指向构造函数的原型对象prototype

原型链

当访问一个对象属性或方法时,首先查找这个对象自身有没有这个属性没有就会去它的原型中查找(prototype原型对象) 还没有就去查找原型对象的原型-直找到Object为止(Object的原型对象的原型为null),这条查找的路径,就是原型链