前端面试题(基础篇)

说在前面:

  • 本面试题是本人在工作和找工作中总结出来的面试题,主要用于上家的新人招聘。因此:
    • 问的问题与业务关联程度比较高,更偏向于前端基础、性能优化、故障定位等。
    • 上家要招的职级并不是很高,因此面试题难度不算高,大致相当于前端1-3年经验的水平
  • 部分与我另一篇博客相同或类似,但毕竟随着成长以及技术的发展重视的东西也在改变。
  • 答案仅供参考,部分答案待补充

基础篇

语义化

问:HTML语义化是什么意思?有什么作用?
参考答案:
简单的来说,语义化就是让该做某件事的东西来做那件事。比如,HTML中的各级标题用H1等表示。同样的例子还有header,footer,site等标签。
作用:

  1. 首先是对维护者友好,维护你代码的人,能通过你的HTML代码轻松理解你的意图;
  2. 其次是对搜索引擎友好,搜索引擎不会抓取你的CSS属性,所以,语义化能让搜索引擎更好的抓到你想表达的东西,更容易让搜索引擎理解你的网站架构;
  3. 另外就是对用户友好,当然大部分用户都只是用眼睛看你的网站,所以可以通过CSS样式来达到这个目的。但是,盲人是没法看到的,他们只能通过辅助设备来实现,但同样的,这些设备只能识别语义化的HTML。

HTTP状态码

提问1:HTTP常见的状态码有哪些?分别表示什么意思?
参考答案1:(以下必答。能讲到301、405、503等更佳)

  • 200:成功
  • 302:重定向
  • 304:未修改
  • 403:服务器禁止访问
  • 404:找不到请求的资源
  • 500:服务端错误

提问2: HTTP状态码分为哪五类?各表示什么意思?

  • 1xx:信息获取
  • 2xx:成功
  • 3xx:重定向
  • 4xx:错误访问
  • 5xx:服务端错误

提问3:HTTP状态码中,4xx和5xx有什么不同?

  • 4**是请求错误,例如未经授权的请求(403),错误的请求地址(404),错误的请求方法(405)
  • 5**是服务端错误,例如脚本运行出错(500)

缓存

提问: 说一下浏览器缓存策略

参考答案:讲到浏览器本地缓存、根据Last-Modified和Etag去请求判断是否304就差不多了。能讲到缓存/代理服务器、cdn更佳。

  • 请求资源前,先查看缓存中是否有未过期且未修改的相同资源,如果有,直接在缓存中获取而不是向服务器索求;如果没有,并且服务器允许缓存,则将资源缓存在本地。
  • 相关字段及作用:
    • Expires:服务器允许浏览器在这个时间前使用该资源缓存
    • Cache-control:作用和Expires类似,但优先级更高,且可选值更多。值可以是public、private、no-cache、no-
      store、no-transform、must-revalidate、proxy-revalidate、max-age。其中,max-age值最常用。
      • Public指示响应可被任何缓存区缓存。
      • Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
      • no-cache指示请求或响应消息不能缓存
      • no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
      • max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
      • min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
      • max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
    • Last-Modified / If-Modified-Since:配合Cache-Control使用。
      • Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
      • If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头
        If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
    • Etag /
      If-None-Match:也要配合Cache-Control使用。注意,Etag优先级比Last-Modified高,服务器会优先比对Etag。
      • Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
      • If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match
        (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。

性能

提问:首页白屏时间如何判断?
答案:三种场景都可近似模拟,但越标准越接近最正确答案。不同标准对应不同水平

答案1:window.onload事件中使用 requestAnimationFrame 函数,用当前时间减去 navigationStart。具体代码:

window.onload = () => {
    requestAnimationFrame(() => {
        const time = Date.now() - window.performance.timing.navigationStart;
        console.log(time);
    })
}

答案2:windows.onload中直接使用当前时间减去 navigationStart。
答案3:js末尾执行时间减去第一个js执行时间

如果回答了答案3,可以追问:
追问1:第一个js执行前还有哪些时间是没考虑进去的?

  1. 浏览器解析时间
  2. DNS查询时间
  3. 重定向时间
  4. HTML下载时间
  5. 等等。言之有理即可

追问2:js末尾执行时间在什么情况下会出现不准确情况?

  1. 异步函数比较多的时候。例如接口,或者事件回调
  2. 其他有理答案

提问2:如何减少这个时间?

  1. 参照后面进阶性能优化问题的答案,基本通用
  2. 后端首屏直出(对react和vue则是同构)
  3. 其他有理答案

https

提问1:https有什么作用?
参考答案:
服务端客户端双向认证,防止数据被窃听或篡改。
说浏览器有安全锁让用户放心也算,但不是主要。

提问2:怎么实现这些作用的?
参考答案:
在http层之外增加了一层TLS层,对数据进行加解密。客户端服务端生成对称加密公私钥,交换公钥,用自己的私钥解出内容。中间人无法得到双方公钥,故而无法加解密,也就无法窃听和篡改。

提问3:有什么弊端?
参考答案:
1. 握手次数增加,计算需要时间,导致响应时间变长
2. 客户端服务端都需要计算公私钥,和加解密,需要消耗大量的资源(尤其是服务端)
3. 服务端的并发数减少(由前两点得出的结论)
4. 其他有理有据的答案

提问4:如何优化?
参考答案:

  1. 配置服务器,缩短握手轮数
  2. 升级成http2.0
  3. 其他有理有据的答案

http2

提问:http2有什么优化点?
参考答案:

  1. 头部压缩——减少包大小(对带有大量cookie的http请求尤为有效)
  2. 服务端推送(减少HTTP请求数)
  3. 管道复用(同域请求只需要一遍三次握手和一次慢启动,大大减少响应时间)

网络安全

提问:有哪些常见的网络攻击方式?攻击手段具体是怎样的?如何防御?(社工手段不考虑)

  • csrf
    • 攻击手段:浏览器在访问一个域的时候,会自动带上这个域的cookie,从而在用户不知情的情况下伪造用户完成敏感操作
    • 防御手段
      • Referer白名单判断
      • 全站post
      • CORS策略控制
      • csrf token
  • xss
    • 攻击手段:提交一段js代码,或者欺骗用户点击带有js代码的链接,从而获取用户信息或伪造用户进行操作
    • 防御手段
      • 输入的表单进行标签转义
      • 输出到页面时过滤标签
      • 去除jsonp
  • DDos
    • 攻击手段:制造大量无意义请求,使服务器无暇处理真正的请求
    • 防御手段:
      • 调整服务器配置,提升服务器并发容量
      • 对服务器添加防刷模块,对恶意请求ip的请求进行过滤
      • 应用对请求进行识别,无意义请求直接终止
      • 其他有理有据答案,如增加服务器

前端进阶

请求

提问:前端发起一个请求,从发起请求到返回结果发生了什么?

答案:不要求全答,但越详细越好。可看情况追问

  • 浏览器
    • 判断链接是否合法
    • 将链接解析成URI各个部分,如协议,域名,端口,路径,参数,锚点等
  • DNS
    • 如果请求的域名不是ip形式,需要去查找该域名的DNS信息
    • 查找路径为 浏览器缓存-主机-DNS服务器-更上层DNS服务器……找到即返回,没找到则直到最顶部DNS服务器,再找不到则返回报错
    • 查找的方式为所处局域网内广播寻找
  • 握手
    • TCP三次握手四次挥手
    • TLS握手、协商密钥
  • 包传递
    • HTTP、TLS、TCP、IP等各层依次封包
    • 路由、交换机寻址
    • 慢启动、拥塞避免、快速重传、快速恢复
    • 网络节点
    • 正向代理服务器
    • 缓存服务器
    • 反向代理服务器
    • CDN
  • 源站
    • 服务器处理
    • 路径寻找
    • 应用处理
    • 返回数据
  • 原路返回

埋点

提问1:有什么埋点方案?
参考答案:
1. 被动埋点——所有节点统一加事件委托,用户点击(或其他交互)节点都会发送带有相应信息的埋点
2. 主动埋点——需要时再调用埋点发送函数
3. 自动埋点——检测事件,如onload等事件(一般用于pv、uv的统计)
提问2:各有什么优缺点?
提问3:如何实现埋点?

性能优化

提问1:前端常用的性能优化方案有哪些?越多越好。
参考答案:

  1. 压缩(js、css、图片、混淆)
  2. gzip压缩
  3. 合并文件(如雪碧图)
  4. data image
  5. 异步加载(js、css)
  6. 懒加载(图片)、预加载
  7. 其他有道理的优化方案

提问2:以上方案可能会带来什么问题

  • 压缩(js、css、图片、混淆)
    • 图片压缩之后导致模糊
      • 追问:如何取舍;
        • 尽量无损压缩;
        • 控制压缩率[一般60%-70%]);
    • 混淆之后线上难以定位问题
  • gzip压缩
    • 服务器CPU压力
  • 合并文件
    • 原子性变差(改一处地方得更新整个大文件)
      • 如何优化
        • dll包(或common chunk),即公共部分或极少改动部分单独打包
  • data image
    • 无法缓存;
    • 客户端CPU压力
    • 增加包体积
  • 异步加载(js、css)
    • 异步加载过程中页面交互可能不及时
  • 懒加载(图片)
    • 用户等待时间加长(可用默认图片或加载图提升用户体验)
  • 其他有道理的优化方案

框架

提问1:虚拟DOM的实现原理

提问2:vue和react有何不同

代码题

安全加固

已知jq的ajax函数,对其进行csrf和重放攻击安全加固。(假设后端可以满足所需任意要求)

// 源码已做脱敏处理
import Cookies from 'js-cookie';

// 客户端和服务端的时间偏差
let timeOffset = 0;
const timeOffsetCookieKey = 'timeOffset',
    timeCookieKey = 'time',
    csrfCookieKey = 'csrfToken';

// 毫秒转成秒
function getSecondTimestamp () {
    return Math.floor(Date.now() / 1000)
}
if (Cookies.get(timeOffsetCookieKey)) {
    timeOffset = +Cookies.get(timeOffsetCookieKey);
} else if (Cookies.get(timeCookieKey)) {
    timeOffset = Cookies.get(timeCookieKey) - getSecondTimestamp();
    Cookies.set(timeOffsetCookieKey, timeOffset);
}

// 获取服务端的实时时间计算偏差,假设接口返回了服务端时间
$.ajax({
    url: '//www.test.com/t',
    success: function (timestamp) {
        timeOffset = timestamp - getSecondTimestamp();
        Cookies.set(timeOffsetCookieKey, timeOffset);
    }
})

// 自定义加密函数。可用加盐哈希,或其他加密方式。此处涉敏不列
function encrypt () {

}
// 生成随机串
function getRandStr (data) {
    return data
        ? data.toString(16)
        : Math.random().toString(16).replace('0.', '');
}
function generateRandStr (length) {
    let randStr = getRandStr() + getRandStr(Date.now())
    while (randStr.length < length) {
        randStr += getRandStr()
    }
    return randStr.substr(0, length)
}
// 生成签名
function generateSign (data) {
    const keyArr = Object.keys(data)
    keyArr.sort()
    let signStr = '';
    for (let i = 0; i < keyArr.length; i++) {
        // undefined会导致生成串错误
        if (typeof(data[keyArr[i]]) !== 'undefined') {
            signStr += keyArr[i] + data[keyArr[i]];
        }
    }
    return encrypt(signStr)
}

const fn = jQuery.ajax;
jQuery.ajax = (arg) => {
    const setting = Object.assign({}, arg);

    setting.data = setting.data || {};
    setting.type = setting.type || 'get';

    const queryUrl = setting.url || ''

    // csrf token
    const csrfToken = Cookies.get(csrfCookieKey);
    if (csrfToken) {
        setting.data[csrfCookieKey] = csrfToken;
    }

    if (setting.dataType !== 'script') {
        // 全站POST,原先的请求类型放到参数中
        setting.data.method = setting.type
        setting.type = 'post';

        setting.data.nonce = generateRandStr(32);
        setting.data.timestamp = getSecondTimestamp() + timeOffset;
        setting.data.sign = generateSign(setting.data);

        setting.dataType = '';

        // 添加withCredentials支持
        setting.xhrFields = {
            withCredentials: true
        };
        setting.beforeSend = function (xhr) {
            xhr.withCredentials = true;
        }
    }
    fn(setting);
};

lazyman实现

实现一个LazyMan,可以按照以下方式调用:

LazyMan(“Hank”)输出:
Hi! This is Hank!

LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper

以此类推。

参考答案:见LazyMan

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值