前端面试题

120 阅读11分钟

HTTP缓存:

强缓存

  • Expires:[<http-date>(绝对时间)]
  • Cache-Control(通用消息头):[max-age=<seconds>(相对时间),no-cache(协商缓存验证),no-store(不使用任何缓存)]
//重新加载: 请求通过 `If-None-Match` 和 `If-Modified-Since` 进行验证,状态码304或200。
fetch("https://developer.mozilla.org/static/js/ga.js", {
    headers:{
        'Cache-Control':'max-age=0'
    }
});
fetch("https://developer.mozilla.org/static/js/ga.js", { cache: "no-cache" });

//强制重新加: 服务器不进行新鲜度验证,状态码200。
fetch("https://developer.mozilla.org/static/js/ga.js", {
    headers:{
        'Cache-Control':'no-cache'
    }
});
fetch("/", { cache: "reload" });

协商缓存

  • Last-Modified:资源最近修改时间,由服务器告诉浏览器。
  • If-Modified-Since:资源最近修改时间,由浏览器告诉服务器。
  • Etag:资源标识,由服务器告诉浏览器。
  • If-None-Match:缓存资源标识,由浏览器告诉服务器。
  • Vary:让代理服务器的缓存命中更多的决定因子,而不仅仅是依据请求 URL 和请求方法来决定是否命中。
  • Age:对象在缓存代理中存贮的时长,以秒为单位。

一般情况下html文件不进行缓存(Cache-Control:no-cache),js.css等静态文件采用缓存。

cookie

同站(same-site)/第一方(first-party):有效顶级域名和二级域名相同(www.a.taobao.com,www.b.taobao.com)

跨站(cross-site)/第三方(third-party):有效顶级域名或二级域名不相同(www.taobao.com,www.baidu.com,b.github.io,a.github.io)

Set-Cookie:[<cookie-name>=<cookie-value>; Domain=<domain-value>; Max-Age=;Expires=; Secure; HttpOnly]

  • Domain:指定cookie所属域名,默认是当前域名。
  • Path:指定cookie在哪个路径下生效,默认是 '/' 。
  • MaxAge:cookie失效的时间,单位秒。(相对时间)
  • Expires:cookie过期时间。(绝对时间)
  • Secure:该cookie仅被使用安全协议传输(https,ssl)。
  • HttpOnly:仅用于网路传输,禁止JS脚本读取到该cookie的信息。
  • SameSite:可以让Cookie在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
    • Strict:禁用第三方cookie
    • Lax: 特定条件下允许第三方cookie(外部站点导航到源站)
    • None: 完全允许第三方cookie

设置规则:在当前域名下,只能设置当前域以及父域的cookie,不能设置子域下的cookie。

访问规则:cookie挂载在某个域下,只有在此域名下或者此域名的子域下才能获取cookie。

事件循环(Event Loop)

164974fa4b42e4af_tplv-t2oaga2asx-zoom-in-crop-mark_3024_0_0_0.awebp

宏任务

  • script
  • setTimeout/setInterval
  • setImmediate
  • I/O
  • UI事件
  • postMessage

微任务

  • Promise
  • async/await
  • process.nextTick(NodeJs)
  • Object.observe(已废弃)
  • MutaionObserver
async function async1() { 
    console.log('1');
    await new Promise((resolve) => { 
        console.log('2');
        resolve();
    }).then(() => { 
        console.log('3'); 
    }) 
    console.log('4');
} 
async1();

//执行顺序: 1 2 3 4
setTimeout(function () { 
    console.log('6');
 }, 0) 
console.log('1');
async function async1() { 
    console.log('2');
    await async2();
    console.log('5');
} 
async function async2() { 
    console.log('3');
} 
async1();
console.log('4');

//执行顺序: 1 2 3 4 5 6
console.log('1');
async function async1() { 
    console.log('2');
    await 'await的结果';
    console.log('5');
} 
async1();
console.log('3');
new Promise(function (resolve) { 
    console.log('4');
    resolve();
}).then(function () { 
    console.log('6');
})

//执行顺序 1 2 3 4 5 6
setTimeout(function () { 
    console.log('9');
}, 0) 
console.log('1');
async function async1() { 
    console.log('2');
    await async2();
    console.log('8');
} 
async function async2() { 
    return new Promise(function (resolve) { 
        console.log('3');
        resolve();
    }).then(function () { 
        console.log('6');
    }) 
} 
async1();
new Promise(function (resolve) { 
    console.log('4');
    resolve();
}).then(function () { 
    console.log('7');
}) 
console.log('5');

//执行顺序 1 2 3 4 5 6 7 8 9
new Promise(function (resolve,reject) {
    console.log(1);
    new Promise(function (resolve,reject) {
        console.log(2);
        setTimeout(function(){
            resolve(3);
            console.log(4);
        })
    }).then(function(data){
        setTimeout(function(){
            console.log(5);
        })
        console.log(data);
    })
    setTimeout(function(){
        resolve(6);
        console.log(7);
    })
}).then(function(data){
    console.log(data);
    setTimeout(function(){
        console.log(8);
    })
    console.log(9);
})
    
//执行顺序 1 2 4 3 7 6 9 5 8

渲染原理

1732ebc671aa7dd6_tplv-t2oaga2asx-zoom-in-crop-mark_3024_0_0_0.awebp

  • GUI渲染线程: GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
  • JavaScript引擎线程: JavaScript 引擎线程主要负责解析 JavaScript 脚本并运行相关代码。
  • 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。
  • 定时器触发器: 我们日常使用的setIntervalsetTimeout 就在该线程中。
  • Http请求线程: 在 XMLHttpRequest 在连接后是通过浏览器新开一个Http请求线程请求。

1732ec3500972dcc_tplv-t2oaga2asx-zoom-in-crop-mark_3024_0_0_0.awebp

  • 构建DOM树:构建DOM过程中,如果遇到<script>标签,渲染引擎会停止对HTML的解析,而去加载执行JS代码,原因在于JS代码可能会改变DOM的结构。
  • 样式计算:根据CSS代码确定每个DOM节点的计算样式(computed style)。
  • 生成布局树:主线程会遍历DOM及相关元素的计算样式,构建出包含每个元素的页面坐标信息及盒子模型大小的布局树(跳过隐藏的元素display: none)。
  • 生成图层树:浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。
    • 显示合成:
      • HTML根元素
      • position不为static并且设置了z-index属性
      • opacity|transform|filter|isolation
      • will-change
      • 需要剪裁的地方
    • 隐式合成:当出现一个合成层后,层级顺序高于它的堆叠元素就会发生隐式合成。

回流(重排)

对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生回流过程。

  • 一个 DOM 元素的几何属性变化(width,height,padding,margin,left,top,border等)
  • 使 DOM 节点发生增减或者移动
  • 读写 offset族、scroll族和client族属性的时候
  • 调用 window.getComputedStyle 方法

1732ec388e85bd2d_tplv-t2oaga2asx-zoom-in-crop-mark_3024_0_0_0 (1).awebp

重绘

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:colorbackground-colorvisibility等)。

1732ec3b24ec43c9_tplv-t2oaga2asx-zoom-in-crop-mark_3024_0_0_0.awebp

重绘不一定导致回流,但回流一定发生了重绘。

Web Worker

  • Worker:一个专用 worker 仅仅能被首次生成它的脚本使用。
//main.js
    
//将函数转成URL对象
function fn2workerURL(fn) {
  const blob = new Blob([`(${fn.toString()})()`], { type: 'text/javascript' });
  return URL.createObjectURL(blob);
}

const worker = new Worker('./worker.js'); 
worker.onmessage = function(e) {
    const data = e.data;
    console.log('Message received from worker');
}
worker.postMessage({ type: 'start', payload: { msg: 'send msg to worker' } }); // 发送信息给
worker.terminate(); //终止 worker
//worker.js
    
importScripts('foo.js'); //引入脚本与库

self.onmessage = function(e) { 
    const data = e.data;
    console.log('Message received from main script');
    postMessage({ type: 'connect', payload: { msg: 'send msg to main' } });

};
self.close(); 
  • SharedWorker:共享 worker 可以同时被多个脚本使用(window, iframes 或者甚至是多个worker)。
//main.js

const myWorker = new SharedWorker('worker.js');
myWorker.port.onmessage = function(e) {
  const data = e.data;
  console.log('Message received from worker');
}
//worker.js

onconnect = function(e) {
  const port = e.ports[0];
  port.onmessage = function(e) {
    const data = e.data;
    port.postMessage(workerResult);
  }
}
  • ServiceWorker:控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。
//app.js

const registerServiceWorker = async () => {
  if ("serviceWorker" in navigator) {
    try {
      const registration = await navigator.serviceWorker.register("/sw.js", {
        scope: "/",
      });
      if (registration.installing) {
        console.log("正在安装 Service worker");
      } else if (registration.waiting) {
        console.log("已安装 Service worker installed");
      } else if (registration.active) {
        console.log("激活 Service worker");
      }
    } catch (error) {
      console.error(`注册失败:${error}`);
    }
  }
};
registerServiceWorker();
//sw.js

const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",
      "/star-wars-logo.jpg",
      "/gallery/bountyHunters.jpg",
      "/gallery/myLittleVader.jpg",
      "/gallery/snowTroopers.jpg",
    ])
  );
});

const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
  // 首先,尝试从缓存中获取资源
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  // 然后尝试从网络中获取资源
  try {
    const responseFromNetwork = await fetch(request);
    // 响应可能会被使用
    // 我们需要将它的拷贝放入缓存
    // 然后再返回该响应
    putInCache(request, responseFromNetwork.clone());
    return responseFromNetwork;
  } catch (error) {
    const fallbackResponse = await caches.match(fallbackUrl);
    if (fallbackResponse) {
      return fallbackResponse;
    }
    // 当回落的响应也不可用时,
    // 我们便无能为力了,但我们始终需要
    // 返回 Response 对象
    return new Response("Network error happened", {
      status: 408,
      headers: { "Content-Type": "text/plain" },
    });
  }
};

self.addEventListener("fetch", (event) => {
  event.respondWith(
    cacheFirst({
      request: event.request,
      fallbackUrl: "/gallery/myLittleVader.jpg",
    })
  );
});

微前端

微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。

qiankun

  • JS沙箱:把 js 代码包裹了一层 function,然后再把内部的 windowProxy 包一层,这样内部的代码就被完全隔离了。
  • CSS隔离:提供了两种样式隔离方案shadow dom(外界和内部完全隔离)和自己实现的scoped(类似Vue的scoped css)。
  • 存在的问题:挂在body的弹窗样式设置不上

wujie

  • JS沙箱:将子应用的 js 放置在 iframe中运行,实现了应用之间 windowdocumentlocationhistory 的完全解耦和隔离。
  • CSS隔离:将子应用的 dom 放置在 webcomponent + shadowdom 的容器中,除了可继承的 css 属性外实现了应用之间 css 的原生隔离。

BFC

BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。

BFC规则

  • BFC就是一个块级元素,块级元素会在垂直方向一个接一个的排列
  • BFC就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签
  • 每个元素的margin的左边, 与包含块border的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此
  • BFC的区域不会与float 元素重叠
  • 垂直方向的距离由margin决定, 属于同一个BFC的两个相邻的标签外边距会发生重叠
  • 计算BFC的高度时,浮动元素也参与计算

触发BFC

  • 根元素或其它包含它的元素
  • 浮动 float: left/right/inherit
  • 绝对定位元素 position: absolute/fixed
  • 行内块 display: inline-block
  • 表格单元格 display: table-cell
  • 表格标题 display: table-caption
  • 溢出元素 overflow: hidden/scroll/auto/inherit
  • 弹性盒子 display: flex/inline-flex
  • display: flow-root(显示创建BFC)

BFC应用

  • 解决块级元素垂直方向margin重叠
  • 解决浮动元素高度塌陷问题
  • 清除浮动

Babel

16d8d0cd5a3f3a0c_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.awebp

  • @babel/core:是babel的核心,主要起串联的作用,功能包括加载配置、调用@babel/parser解析AST、调用@babel/traverse遍历并操作AST、调用@babel/generator生成代码。
  • @babel/parser:将源代码解析为AST。(JSX,Typescript,Flow以及最新的ECMAScript规范)
  • @babel/traverse:对AST进行深度优先遍历,转换插件会通过它获取感兴趣的AST节点,对节点继续操作。
  • @babel/generator:将AST转换为源代码,支持SourceMap。
  • @babel/types:AST节点构造器和断言。
  • @babel/template:模板引擎,可以将字符串代码转换为AST。

Webpack

graph LR
解析入口文件 --> 加载插件 --> 加载loader --> 构建依赖树 --> 文件写入 --> 监听文件变化;
监听文件变化 -- 重新编译 --> 解析入口文件;
  • loader
    • pre: 前置loader
    • normal: 普通loader
    • inline: 行内loader
    • post: 后置loader

d876b4cba8884e9885a85f4379133c99_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.webp

Web安全

XSS

XSS (Cross-Site Scripting),跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。

  • 反射型:非持久型 XSS 漏洞,一般是通过给别人发送带有恶意脚本代码参数的 URL,当 URL 地址被打开时,特有的恶意代码参数被 HTML 解析、执行。
  • 存储型:持久型 XSS 漏洞,一般存在于 Form 表单提交等交互功能,如文章留言,提交文本信息等,黑客利用的 XSS 漏洞,将内容经正常功能提交进入数据库持久保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行。

防御措施:

  • CSPContent-Security-Policy内容安全策略,CSP本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。
  • 转义字符:用户的输入永远不可信任的,最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义。
  • HttpOnly:这是预防XSS攻击窃取用户cookie最有效的防御手段。

CSRF

CSRF(Cross Site Request Forgery),即跨站请求伪造,是一种常见的Web攻击,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。

1688030a24702301_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.image

防御措施:

  • SameSite:可以对 Cookie 设置 SameSite 属性。
  • Referer:通过检查http包头referer的值是不是这个页面,来判断是不是CSRF攻击。
  • CSRF Token:发送请求时在HTTP 请求中以参数的形式加入一个随机产生的token,并在服务器建立一个拦截器来验证这个token。
  • 验证码:应用程序和用户进行交互过程中,特别是账户交易这种核心步骤,强制用户输入验证码,才能完成最终请求。

点击劫持

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。

防御措施:

  • X-FRAME-OPTIONSX-Frame-Options 响应头是用来给浏览器指示允许一个页面可否在 <frame><iframe><embed> 或者 <object> 中展现的标记。
  • JavaScript防御:当通过 iframe 的方式加载页面时,攻击者的网页直接不显示所有内容(self!==top)。

URL跳转漏洞

其原理是黑客构建恶意链接(链接需要进行伪装,尽可能迷惑),发在QQ群或者是浏览量多的贴吧/论坛中。安全意识低的用户点击后,经过服务器或者浏览器解析后,跳到恶意的网站中。

1688e946b82fbd84_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.image

防御措施:

  • referer限制:如果确定传递URL参数进入的来源,我们可以通过该方式实现安全限制,保证该URL的有效性,避免恶意用户自己生成跳转链接。
  • Token验证:通过在生成的链接里加入用户不可控的Token对生成的链接进行校验,可以避免用户生成自己的恶意链接从而被利用。

SQL注入

SQL注入是一种常见的Web安全漏洞,攻击者利用这个漏洞,可以访问或修改数据,或者利用潜在的数据库漏洞进行攻击。

1688ee787da295af_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.image

防御措施:

  • 严格限制Web应用的数据库的操作权限
  • 后端代码检查输入的数据是否符合预期
  • 对进入数据库的特殊字符(',",\,<,>,&,*,; 等)进行转义处理,或编码转换
  • 所有的查询语句建议使用数据库提供的参数化查询接口(即不要直接拼接SQL语句)

OS命令注入攻击

OS命令注入攻击指通过Web应用,执行非法的操作系统命令达到攻击的目的。只要在能调用Shell函数的地方就有存在被攻击的风险。倘若调用Shell时存在疏漏,就可以执行插入的非法命令。

1688f285cb8da6c7_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.image

防御措施:

  • 后端对前端提交内容进行规则限制(比如正则表达式)
  • 在调用系统命令前对所有传入参数进行命令行参数转义过滤
  • 不要直接拼接命令语句,借助一些工具做拼接、转义预处理