前端面试笔记 🍜

197 阅读13分钟

HTML

如何理解 HTML 语义化

HTML 语义化就是使用正确的标签, 比如段落就写 p 标签,标题就写 h1 标签,文章就写 article 标签,视频就写 video 标签,等等。总之核心就是反对大篇幅的使用无语义化的 div + css + span,而鼓励使用 HTML 定义好的语义化标签。

meta viewport

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

viewport 可以翻译为窗口或视图,主要用于移动端。主要属性:

  • width:控制 viewport 的宽度
  • initial-scale:默认缩放比例
  • minimum-scale:最小缩放比例
  • maximum-scale:最大缩放比例
  • user-scalable:是否允许手动缩放

用过哪些 HTML 5 标签

  • h1, h2 ...:文档标签
  • a 超链接:
  • div:通用的容器,没有特殊含义
  • header:页面或章节的头部(Logo、标题、导航栏目录...)
  • footer:页面或章节的尾部(版权信息、法律信息、反馈建议...)

SEO 友好

SEO 即 引擎优化,能让百度、谷歌搜索排名靠前。

如何让 SEO 友好?

  • 静态url,便于搜索引擎抓取
  • 避免使用框架
  • 符合W3C标准代码
  • titlemetacontenth1a 标签写好

CSS

两种盒模型

  • 标准盒模型:box-sizing: content-box; 只是对content-box设置了宽高,那么这个元素的真正宽高还要加上内边距(padding)、边距(border)、外边距(margin),于是我们布局的时候就要好好计算一下了。
  • IE 盒模型:box-sizing: border-box; 常用的盒模型。content部分包含了内边距(padding)和边距(border),把整个盒子看作了一个整体,给整个盒子定义了宽高。

Flex 怎么用

display: flex;

css-tricks.com/snippets/cs…

BFC 是什么

BFC(Block Formatting Context)就是块级格式化上下文,就是一个独立的容器,形成一个独立的空间,不受外部影响,也不影响外部。

  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed; position不为 relative 和 static)
  • 行内块元素(元素的 display 为 inline-block)
  • overflow 值不为 visible 的块元素; overflow为auto、scroll和hidden
  • 弹性元素(display为 flex 或 table-cell 或 inline-flex元素的直接子元素)

CSS 选择器优先级

  <style>
    #box a {
      color: green !important; // 第一优等生(!important)
    }
    #box a { // 第三优等生(ID选择器)
      color: green;
    }
    [class="box"] a { // 第五优等生(class选择器)
      color: gold;
    }
    .box a { // 第四优等生(class选择器 比上面等级高,因为靠后覆盖了)
      color: brown;
    }
    a { 第七优等生
      color: red;
    }
    p a { // 第六优等生
      color: yellow;
    }
    * { // 最后一名
      color: red;
    }
  </style>
  
  <p id='box' class="box"> 
      <a style="color: red"> // 第二优等生(内联样式)
          hi
      </a>
  </p>
  • 越具体优先级越高
  • 同样优先级 写在后面的, 覆盖写在前面的
  • !important 优先级最高,但是要少用

JS

ES6 语法

  • letconst
  • 箭头函数
  • Promise
  • 展开操作符
  • 默认参数
  • importexport
  • 析构赋值

Promise

  • 介绍:异步操作返回的对象,用来传递异步操作的消息。
  • 优势:解决了回调地狱,不会导致难以维护,合并多个异步请求,节约时间。
  • 状态:pending 初始、fulfilled 成功、rejected 失败
  • Promise 用法:
function getPromise(URL) {
  let promise = new Promise(function (resolve, reject) {
    let req = new XMLHttpRequest();
    req.open("GET", URL);
    req.onload = function () {
      if (req.status == 200) {
        resolve(req.response);
      } else {
        reject("There is an Error!");
      }
    };
    req.send();
  });
  return promise;
}
  • Promise.all 用法
const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'one');
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'two');
});
// p1 和 p2 都成功才能调用
Promise.all([p1, p2]).then(values => {
    console.log(values) // ['one','two']
})
  • Promise.race 用法
// 比赛谁跑得快,无论是先成功还是先失败
const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'one');
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 2000, 'two');
});
// p1 和 p2 都成功才能调用
Promise.race([p1, p2]).then(values => {
    console.log(values) // 'one'
})
  • Promise.allSettled 用法:解决了 Promise.all 的痛点,即永远不会被 reject
  • Promise.prototype.finally() 用法:无论结果是Resolved或者是Rejected都需要执行的代码提供了一种方式,避免同样的语句需要在then()和catch()中各写一次的情况。
  • Promise 缺点:
    • 无法中途取消。
    • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
    • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Axios

基于 Promise 的 HTTP 库。

优点

  • 安全性高
  • 支持请求取消
  • 批量发送多个请求
  • 支持 Promise 所有的 API
  • 可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON 类型的数据

防抖、节流

  • 防抖:触发事件后n秒后才执行函数,如果n秒内执行,则重新计算函数执行事件。(立即施行 与 非立即执行)
  • 节流:无论怎么点击,每过n秒只执行一次函数

call、apply、bind

// call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。
const numbers = [1, 2, 3]; 
const maxInNumbers = Math.max.apply(Math, numbers); // 3
const maxInNumbers = Math.max.call(Math, 1, 2, 3);  // 3

// bind 接受的参数跟 call 一致,只是 bind 不会立即调用,它会生成一个新的函数,你想什么时候调就什么时候调。
function f1(p1, p2){ 
    console.log(this, p1, p2); 
} 
let f2 = f1.bind({name:'liang'});      //f2为f1绑定了this之后的新函数
let f3 = f1.bind({name:'liang','hi'})  //f3为f1绑定了this之后的新函数,并且传入了一个参数‘hi’ 
f2(); // {name:'liang'} undefined undefined 
f3(); // {name:'liang'} "hi" undefined

闭包

函数 和 函数内部能访问到的变量 的总和,就叫一个闭包。如下,local 变量和 bar 函数就组成了一个闭包。

用途:隐藏一个变量,防止变成全局变量受到外界干扰。

缺点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题;在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

function foo() { 
    let local = 1;
    function bar() { 
        local++;
        return local;
    } 
    return bar;
} 
const func = foo();
func() // 2

立即执行函数

创建一个独立的作用域,这个作用域里面的变量,外面访问不到(即避免「变量污染」)。

(function(){alert('我是匿名函数')})()

同源、跨域、JSONP、CORS

  • 同源:协议、域名、端口 都相同
  • 跨域:不同源的两个源受到浏览器阻止
  • CORSAccess-Control-Allow-Origin
  • JSONP:由于浏览器不支持CORS(IE小于9),并且JSONP只能解决GET,无法POST。

深拷贝、浅拷贝

深拷贝:

JSON.parse(JSON.stringify(data)); // 不能处理函数( [1, 2, function(){}] )

浅拷贝:

data.slice(); // 仅仅复制指针值,没有复制指向的对象。

DOM

事件委托

事件委托可以理解为【事件】和【委托】。【事件】就是指onclick或onmouseover等,【委托】就是让别的元素来完成自己本该完成的事件,也就是通过事件冒泡。

不必为每个元素分配一个处理程序,而是将单个处理程序放在他们的共同组建身上。

优点:减少监听器

缺点:不支持冒泡的事件不能使用事件委托。

HTTP

HTTP 状态码

200OK请求成功, 一般用于GET与POST请求
304Not Modified请求的资源未修改,服务器不会返回任何资源
400Bad Request客户端请求的语法错误,服务器无法理解
403Forbidden服务端拒绝客户端的请求
404Not Found服务端无法根据客户端的请求找到资源
500Internal Server Error服务端内部错误

HTTP 缓存有哪几种

  • ETag:通过对比浏览器和服务器资源的特征值(如MD5)来决定是否要发送文件内容,如果一样就只发送 304(not modified)
  • Expires:设置过期时间(绝对时间)
  • CacheControl:设置过期时间(相对时间)

HTTP 请求方法

  • GET:用于获取资源
  • HEAD:和 GET 类似,只不过返回的没有具体的内容,用于获取报头
  • POST:提交请求体(数据)到指定提交、创建资源
  • PUT:客户端发送数据来修改文档的内容
  • DELETE:请求服务器删除指定数据
  • PATCH:类似 PUT,用来对已知资源进行局部更新

GET vs POST

  • GET 参数通过 URL 传递,POST 是放在 request body 里,因此 POST 更安全
  • GET 请求参数有上线,POST 没有
  • GET 请求会保存在cache、收藏栏、浏览器历史记录里,POST 不会
  • GET 请求回退时无害的,而 POST 会再次提交请求
  • GET 的参数类型只接受ASCII字符,POST 无限制

Session vs Cookie

  • Cookie 存在浏览器的文件里,Session 存在服务器的文件里
  • Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里

Cookie vs LocalStorage

  • 主要区别是 Cookie 会被发送到服务器,而 LocalStorage 不会
  • Cookie 一般最大 4k,LocalStorage 可以用 5Mb 甚至 10Mb(各浏览器不同)

LocalStorage vs SessionStorage

LocalStorage 一般不会自动过期(除非用户手动清除),而 SessionStorage 在会话结束时过期(如关闭浏览器)

Vue

Vue 与 MVVM

Vue 并不完全遵循 MVVM。

MVVM 指 Model(数据) 变更触发 View(视图) 更新时必须通过 ViewModel(Vue实例)。同理,View 变更触发 Model 也必须通过 ViewModel(使用 v-model)。

而 Vue 可以通过 ref 拿到 dom 对象,去操作视图,这一点,保留了 MVC,违背了 MVVM,总之就是结合了两者优势。

Vue 与 React 区别

Vue 是自动挡,React 是手动挡。React 推荐函数式编程,是单向数据流,当然也可以双向,通过setState等。有人说 Vue 是双向数据流,非也。Vue 也是单向,只是 v-model 用了语法糖(change和input事件以及value属性)。

Vue3 与 Vue2 区别

  • Vue3 速度快,体积小。 tree-shaking,将无用的模块剪辑,仅打包需要的
  • Composition API:可与 Options API 一起使用,可复用,更易维护
  • 拥抱 TypeScript
  • diff算法 优化(静态标记:在发生变化的位置添加flag标记,下次发生变化的时候直接找该地方进行比较)
  • vue2中,数据劫持是通过Object.defineProperty,这个 API 有一些缺陷,并不能检测对象属性的添加和删除。vue3是通过proxy监听整个对象,那么对于删除还是监听当然也能监听到
  • 组建支持多个根节点(<template>...</template>
  • Teleport

Vue3 - Teleport

任意门,能够把模块移动到其他位置。eg:有ABC三个组件,分别是父、子、孙。孙组件里有个button,点击后添加触发弹窗。如果不用 Teleport,父组件和子组件的UI会受到影响,如果通过任意门把该弹窗组件放在外出,就不用受到影响,这也是 UI 框架常用的手段。

SPA(单页面应用)首屏加载慢如何解决

SPA(Single Page Application)。一开始(首屏)就会加载所有必需的代码,这样一来,以后用户的每一个动作都不会重新加载页面。

如何优化首屏加载速度?

  • 减小入口文件积
// 路由懒加载,在 `vue-router` 配置路由中,采用动态加载路由(函数式)的形式。
routes:[ 
    path: 'Blogs',
    name: 'ShowBlogs',
    component: () => import('./components/ShowBlogs.vue')
]
  • 静态资源本地缓存

采用HTTP缓存,设置Cache-ControlLast-ModifiedEtag等响应头

  • 使用SSR

Server Side,服务端渲染,组件或页面通过服务器生成html字符串,再发送到浏览器

  • UI框架按需加载
  • 图片资源的压缩

v-show 与 v-if 区别

  • 作用是相通的,都能控制元素在页面上的显示。
  • v-show 隐藏通过 css:display: none,dom 元素依旧还在。v-if 则把整个dom元素添加或删除。
  • v-if 有更高的切换消耗,且其是惰性的,如果一开始条件为false,是不会操作的。
  • 如果追求性能,总体上 v-show 是更好的选择,除非需要 else if,那就用 v-if

v-if 和 v-for 区别及优先级

v-forv-if 层级高。不要把这两个放在一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)。应该在外层放一层 template,然后在内部进行循环。

<template v-if="isShow">
    <p v-for="item in items">
</template>

watch / computed / methods

  • watch 是监听,computed 是计算,methods 是方法。
  • computed 有缓存,methodswatch 无缓存。methods 每次渲染都会执行,

.sync / v-model

实现自组件和父组件之间的双向绑定。父组件通过 props 传值给子组件,子组件通过 $emit 传值给父组件。.syncv-model 都是语法糖。

生命周期

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • activated
  • deactivated
  • beforeDestroy
  • destroyed

组件间通信

  • props / $emit:父子组件
  • $emit / on:任意组件
  • provide / inject:祖先组件
  • eventBus:任意组件
  • VueX

数据响应式

修改数据时,视图重新渲染。

Vue2 使用 Object.defineProperty 把属性转换为 gettersetter,由于无法检测到对象属性的添加和删除,就有了 this.$set

Vue3 使用 refreactive 来创建响应式数据。

VueX

用于管理数据的一个工具,用于实现组件之间的数据共享。

state:存放共享变量的地方 getter:获取数据,不会修改 state,只起到包装作用。类似于computed。 mutations:存放修改 state 的方法。 actions:存放修改 state 的方法,在 mutations 上做异步的操作。 commit:调用这些操作的 API

Vue Router

路由模式:

  • hash:# 符号之前的包含在请求中,因此即使后端没有对路由全覆盖,也不会产生404。
  • history:依赖服务器配置,因此如果 URL 匹配不到任何静态资源,则返回之前页面。
  • abstract:支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

TS

简介

TS 是强类型的 JS 超集,可以实现静态类型检查。其不能直接在浏览器上运行,需要通过编译器转换为 JS 再运行。

Webpack

XSS / CSRF

什么是 XSS,如何预防

Cross Site Scripting,跨站脚本攻击。在Vue中当用v-html在填入等地区时,容易触发。

如何攻击

  • 表单输入、onClick等情况下,黑客输入代码,获取你的cookie或sessionID

如何预防

  • 输入过滤,例如:邮箱,电话号码,用户名,密码等
  • httpOnly,在 cookie 中设置 HttpOnly 属性后,js脚本将无法读取到 cookie 信息
  • 转义 HTML,对于引号,尖括号,斜杠等

什么是 CSRF,如何预防

Cross-site request forgery,跨站请求伪造。

攻击过程

  1. 用户小张登录安全网页A,验证通过,A网站给小张发送cookie许可。
  2. 小张在没有访问A网站的情况下,登录黑客网站B,不小心点击了按钮,B网站向A网站发送请求。
  3. B网站偷到小张的cookie后访问A,A网站不知情,黑客成功入侵。

如何预防

  • 验证码
  • token

XSS 与 CSRF 区别

  • 通常CSRF是由XSS实现的,比如用户A去了黑客网站,黑客网站通过按钮或通过命令行发起请求。
  • XSS是代码注入、没有过滤造成的问题;CSRF时HTTP问题,即发起请求的时候自动带上受害者的cookie。

手撕代码

清除浮动

.clearfix:after{ 
    content: ''; 
    display: block; /*或者 table*/ 
    clear: both; 
} 
.clearfix{ 
    zoom: 1; /* IE 兼容*/ 
}

垂直居中

display: flex;
justify-content: center;
align-items: center

防抖

let timerID = null;
const debounce = (e) => {
    timerID && clearTimeOut(timerID);
    timerID = setTimeOut(() => {
        const value = e.target.value;
        ...
    }, 500)
}

数组去重

// 开发时用法
const newArr = [...new Set(arr)]
// 面试时用法 (哈希Map)
const arr = [1,1,3,3,2];
function removeDup(arr) {
    let map = new Map();
    let newArr = [];
    for(let o = 0; o < arr.length; o++) {
        if(map.has(arr[o])) {
            map.set(arr[o], true);
        } else {
            map.set(arr[o], false);
            newArr.push(arr[o]);
        }
    }
    return newArr;
}

手写 AJAX

//创建 XMLHttpRequest 对象
let ajaxReq = XMLHttpReuqest();
//规定请求的类型、URL 以及是否异步处理请求。
ajaxReq.open('GET', url, true);
//发送请求
ajaxReq.send();
//接受服务器响应数据
ajaxReq.onreadystatechange = function() {
    if(ajaxReq.status === 200 && ajaxReq.readyState === 4) {
        console.log(ajaxReq.responseText);
    }
}

快排

function quickSort(arr) {
    if(arr.length <= 1) return arr;
    let pivotIndex = Math.floor(arr.length / 2);
    let pivot = arr.splice(pivotIndex, 1)[0];
    let left = [], right = [];
    for(let o = 0; o < arr.length; o++) {
        arr[o] < pivot ? left.push(arr[o]) : right.push(arr[o]);
    }
    return quickSort(left).concat([pivot],quickSort(right));
}

冒泡排序

function bubbleSort(arr) {
    let done = false;
    for(let o = 0; o < arr.length - 1; o++) {
        if(arr[o] > arr[o+1]) {
            [arr[o], arr[o+1]] = [arr[o+1], arr[o]];
            done = false;
        }
        if(o === arr.length - 2) {
            if(done) return arr;
            o = -1;
            done = true;
        }
    }
}