一天时间迅速准备前端面试,快速构建初级前端知识体系
wen 2022/6/20 天下无鱼
源链接:https://coding.imooc.com/class/400.html (opens new window)
快速搞定前端技术一面 匹配大厂面试要求
迅速建立初中级前端面试(0 ~ 2年)JS 知识体系 掌握解决面试题的思路与技巧
双越:BAT 资深面试官针对时下面试高频考点,帮你解决面试问题。课程不局限于讲解单一知识点,而是以面试官的角度出发,带你了解前端面试中每个 “门道” 与 “套路”。手把手带你分析考点及解答策略,梳理 JS 考试体系。
# 1. 课程介绍【说说面试的那些事儿】
- 关于面试:基层工程师 -- 基础知识;高级工程师 -- 基础知识 + 项目经验;架构师 -- 解决方案能力。
- 关于基础:工程师的自我修养 -- 基础知识;扎实的基础能让你高效学习新技术。
- 拿到一个面试题,第一时间看到 -- 考点。面对题海看准考点。
- 什么是知识体系:结构化的知识范围,结构化、有组织、易扩展。
# 2. 面试前的准备【要知己知彼,不打无准备之仗】
- 投递简历的几种方式:员工内推、猎头推荐、HR 主动收集。
- 面试的主要环节:一面、二面(交叉面)、三面、HR 面。
- JD 分析:Job Description。职位描述、岗位要求。
# 3. CSS 面试题【不多说了,前端面试 CSS 是必考知识,不过关直接回家】
- 如何理解 HTML 语义化:让人更容易读懂,增加代码可读性; 让搜索引擎更容易读懂(SEO)。
- margin 负值问题:margin-top 负值,元素向上移动;margin-bottom 负值,下方元素向上移动;margin-left 负值,元素向左移动;margin-right 负值,右侧元素向左移动。
- 布局:BFC、float、flex。
- 定位:absolute、relative、fixed、垂直居中对齐。
- line-height 的继承:px -- 直接继承;比例 -- 比例 * 自己的 font-size;百分比 -- 百分比 * 父元素的 font-size。
- 响应式:
window.innerWidth === 100vw
、window.innerHeight === 100vh
。
# 4. JS 基础-变量类型和计算【不会变量,别说你会 JS】
- typeof 能判断的类型:undefined、string、number、boolean、Symbol、【function】、object。
- instanceof:用于检测构造函数的
prototype
属性是否出现在某个实例对象的原型链上。instanceof 是基于原型链实现的。【只能判断出引用数据类型和自定义引用数据类型,不能判断基本数据类型】 Object.prototype.toString.call(new Error("自定义错误")).slice(8, -1); // 输出 Error
。- 函数(function)是特殊引用类型,但不用于存储数据,所以没有 “拷贝、复制函数” 这一说。
- 手写深拷贝。
- 注意某些类型转换的坑:字符串拼接、==、if 语句和逻辑运算 --
&&、||
。 - truly 变量:
!! a === true
;falsely 变量:!! a === false
。
# 5. JS 基础-原型和原型链【三座大山之一,必考!!!】
- 如何用 class 实现继承。
[] instanceof Array --> true
、[] instanceof Object --> true
。typeof Class --> function
。class 实际上是函数,可见是语法糖。- 如何理解 JS 原型:隐式原型:
xiaowen.__proto__
=== 显式原型:People.prototype
。
# 6. JS 基础-作用域和闭包【三座大山之二,不会闭包,基本不会通过】
- 作用域:全局作用域、函数作用域、块级作用域(ES6 新增)。
- 自由变量:一个变量在当前作用域没有定义,但被使用了;向上级作用域一层一层寻找;如果到全局作用域都没找到,则报错。在函数定义时查找。
- 闭包:作用域应用的特殊情况,有两种表现:函数作为参数被传递、函数作为返回值被返回。存在自由变量查找规则。任何闭包的使用场景都离不开这两点:创建私有变量、延长变量的生命周期。【闭包是指那些能够访问自由变量的函数。 自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量】
- this 的取值是在函数执行时决定的。
- 手写 bind / call / apply 函数:
// bind
Function.prototype.myBind = function () {
// 将参数拆解成数组
const args = Array.prototype.slice.call(arguments);
// 获取 this (数组的第一个元素)
const t = args.shift();
// 调用 myBind 的函数
const self = this;
// 返回一个函数
return function () {
return self.apply(t, args);
}
}
// call
Function.prototype.myCall = function (context) {
if (typeof this !== "function") {
throw new Error("Type error");
}
// 首先获取参数
let args = [...arguments].slice(1);
let result = null;
// 判断 context 是否传入,如果没有传就设置为 window
context = context || window;
// 将被调用的方法设置为 context 的属性
// this 即为我们要调用的方法
context.fn = this;
// 执行要被调用的方法
result = context.fn(...args);
// 删除手动增加的属性方法
delete context.fn;
// 将执行结果返回
return result;
};
// apply
Function.prototype.myApply = function (context) {
if (typeof this !== "function") {
throw new Error("Type error");
}
let result = null;
context = context || window;
// 与上面代码相比,我们使用 Symbol 来保证属性唯一
// 也就是保证不会重写用户自己原来定义在 context 中的同名属性
const fnSymbol = Symbol();
context[fnSymbol] = this;
// 执行要被调用的方法
if (arguments[1]) {
result = context[fnSymbol](...arguments[1]);
} else {
result = context[fnSymbol]();
}
delete context[fnSymbol];
return result;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
- 实际开发中闭包的应用:隐藏数据、做一个简单的 cache 工具。
- 注意以下 let 位置的不同:
let a
for (let i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
let i, a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 7. JS 基础-异步【三座大山之三,必考!!!】
- 同步和异步有何不同:基于 JS 是单线程语言;同步会阻塞代码执行;异步不会阻塞代码执行。
- 异步的应用场景:网络请求、定时任务。
- Promise:是异步编程的一种解决方案,解决 callback hell。
Promise.resolve('foo');
// 等同于
new Promise(resolve => resolve('foo'));
Promise.reject('出错了');
// 等同于
new Promise((resolve, reject) => reject('出错了'));
1
2
3
4
5
6
7
2
3
4
5
6
7
- JS 是单线程运行的,异步要基于回调来实现。
# 8. JS 异步进阶【想要进大厂,更多异步的问题等着你】
- event loop:事件循环,事件轮询。就是异步回调的实现原理。callback queue。
- DOM 事件和 event loop 的关系:DOM 事件也是使用回调,基于 event loop。回调 --> 异步 --> event loop。
- DOM 渲染和 event loop 的关系:JS 的执行和 DOM 渲染公用一个线程。先尝试 DOM 渲染,再进行 event loop。微任务在 DOM 渲染前,宏任务在 DOM 渲染后(微任务执行早于宏任务的原因,使用 appendChild / alert 做实验)。
- Promise 有哪三种状态:pending、fulfilled、rejected。【1】变化不可逆。【2】resolved 触发后续 then 回调,rejected 触发后续 catch 回调。【3】then 和 catch 影响状态的变化:then、catch 正常返回 resolved,里面有报错则返回 rejected。
- async-await 语法:是同步语法,彻底消除回调函数,处理异步编程的最终方案。【1】执行 async 函数,返回的是一个 Promise 对象。【2】await 相当于 Promise then。【3】try...catch 可捕获异常,代替 Promise 的 catch。【4】await 后面的代码,即下一排的代码开始,都可以看作是 callback 里面的内容,即异步,会启动 event loop。
- for...of 常用于异步的遍历,for...in、for、forEach 是常规的同步遍历。
function multi(num) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * num);
}, 1000)
})
}
const nums = [1,2,3];
;(async function () {
for (let i of nums) {
// 在 for...of 循环体的内部,遇到 await 会挨个串行计算
const res = await multi(x);
console.log(res);
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 宏任务和微任务。
# 9. JS-Web-API-DOM【学会 DOM,才能具备网页开发的基础】
- JS 基础知识,规定语法(ECMA 262 标准);JS Web API,网页操作的 API(W3C 标准)。前者是后者的基础,两者结合才能真正实际应用。
- DOM 本质:Document Object Model,本质是一棵树。
- DOM 节点操作:获取节点、attribute(修改 html 属性,会改变 html 结构)、property(修改对象属性,不会体现到 html 结构中)。
- DOM 结构操作:新增/插入节点、获取子元素列表(nodeType、nodeName)、获取父元素、删除子元素。
- 优化 DOM 操作的性能:避免频繁的 DOM 操作(
document.createDocumentFragment()
)、对 DOM 查询做缓存(document.getElementsByTagName('p').length
)。
# 10. JS-Web-API-BOM【内容虽然不多,但是你不能不会】
- BOM:Browser Object Model。知识点 --
navigator、screen、location、history
。 - 如何识别浏览器类型:
navigator.userAgent
。 - 分析拆解 url 各个部分:
location
。
# 11. JS-Web-API-事件【事件不会,等于残废,必考!必考!】
- 事件绑定:
element.addEventListener('', event => { })
。 - 事件冒泡:
event.preventDefault()
。 - 事件代理:依赖于事件冒泡。
# 12. JS-Web-API-Ajax【每个工程师必须熟练掌握的技能】
AJAX
全称 (Async Javascript and XML) 即异步的JavaScript
和XML
,是一种创建交互式网页应用的网页开发技术,可以在不重新加载整个网页的情况下,与服务器交换数据,并且更新部分网页。【1】优点:页面无刷新,用户的体验非常好;使用异步方式与服务器通信;把一些服务器负担的工作转嫁到客户端处理;被广泛的支持。【2】缺点:破坏了浏览器前进和后退机制(因为 ajax 自动更新机制);对搜索引擎的支持比较弱;安全性问题不太好(可以用数据加密解决)。- XMLHttpRequest:ajax 的核心 API。
const xhr = new XMLHttpRequest();
// true 表示异步
xhr.open('GET', 'https://api.github.com/users/diego3g', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
}
}
xhr.send(null);
// post 请求
// xhr.send(JSON.stringify({ foo: 'foo' }));
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 浏览器的同源策略:协议、域名(指向主机)、端口,只有这三个完全相同的 URL 才能称之为同源(安全)。加载图片、css、js 可无视同源策略。
<img src='跨域的图片地址' />
<link href='跨域的css地址'/>
<script src='跨域的js地址'> <script/>
1
2
3
2
3
- 实现跨域的常见方式:JSONP、CORS。【1】JSONP:
<script>
可绕过跨域限制、服务器可以任意动态拼接数据返回。【2】CORS:服务器设置 http header。【3】Proxy:使用在开发环境中。 - 解决跨域问题实际上改的是 http 里面哪个参数:header -- Access-Control-Allow-Origin。
# 13. JS-Web-API-存储【内容虽然不多,但不可不会】
- cookie:
document.cookie = 'a=100'
是一个追加的效果,而不是替换。缺点:存储大小,最大 4KB;请求时需要发送到服务端,增加请求数据量;不方便修改。 - localStorage、SessionStorage:HTML5 专门为存储而设计,最大可存 5M;API 简单易用,setItem / getItem;不会随着 http 请求被发送出去。
# 14. http 面试题【前后端分离的时代,网络请求是前端的生命线】
- http 状态码:【1】状态码分类:1xx -- 服务器收到请求、2xx -- 请求成功、3xx -- 重定向、4xx -- 客户端错误、5xx -- 服务端错误;【2】常见状态码:200、301 永久重定向、302 临时重定向、304 资源未修改使用缓存、403 没有权限、404、500、504 网关超时;【3】关于状态码的协议与规范:就是一个约定。
- http methods:post(create)、delete(delete)、patch / put(update)、get(select)。
- Restful API:一种新的 API 设计方法(早已推广使用)。传统 API 设计把每个 url 当作一个功能,Restful API 设计把每个 url 当作一个唯一的资源。如何设计成一个资源:尽量不用 url 参数,用 method 表示操作类型。
- http headers:【1】request headers -- Accept / Accept-Encoding / Accept-Language / Connection / Cookie / Host / User-Agent / Content-type;【2】response headers -- Content-type / Content-length / Content-Encoding / Set-Cookie。【3】缓存相关的 headers -- Cache-Control / Last-Modified / Etag。
- http 缓存:【1】什么是缓存:当浏览器向服务器请求资源的时候,都会率先抵达浏览器缓存,如果浏览器有这个资源请求的副本,就可以直接从浏览器获取资源,而不用去请求服务器;【2】为何需要缓存:为了提高网页打开速度,减少请求次数,降低服务器压力;可以被缓存的静态资源 js css img;【3】缓存策略:强制缓存、协商缓存;【4】强制缓存:Cache-Control -- max-age / no-cache,在 response headers 中,代替比较老的 Expires;【5】协商缓存:服务器端缓存策略,服务器判断客户端资源是否和服务端资源一样,一致则返回 304 否则返回 200 和新资源。请求和响应的时候带着资源标识,在 response headers 中,有两种 Last-Modified、Etag(优先使用)。
- 刷新页面对 http 缓存的影响:【1】正常操作 -- 地址栏输入 url / 跳转链接 / 前进后退:强制缓存有效,协商缓存有效;【2】手动刷新 -- F5 / 点击刷新按钮:强制缓存失效,协商缓存有效;【3】强制刷新 -- ctrl + F5:强制缓存失效,协商缓存失效。
# 15. 开发环境【不会这些,你就会被认定是菜鸟小白,没做过项目】
- 前端开发常用的开发工具:git、调试工具、抓包、webpack / babel、linux 常用命令。
- git:代码版本管理工具,熟悉常用命令。
- chrome 调试工具:Elements、Console、Debugger、Network、Application。
- 抓包工具:fiddler。
- 配置 webpack / babel:@babel/core(@ 表示一个组)。
- 如何配置 webpack 生产环境:
"build": "webpack --config webpack.prod.js"
。 - ES6 模块化规范:import、export。
# 16. 运行环境【这些会了,你就可以飞了】
运行环境:浏览器、nodejs。
网页加载过程:加载、渲染。【1】从输入 url 到渲染出页面的整个过程:DNS 解析、浏览器发起 http 请求、服务器处理 http 请求返回资源、解析 HTML、生成 DOM 树、CSSOM、渲染;【2】window.onload(不靠谱) 和 DOMContentLoaded(推荐在此过程中执行 JS 代码):window.onload 是页面的全部资源加载完才会执行,包括图片视频;DOMContentLoaded 是 DOM 渲染完即可执行,图片视频可能没有加载完。
img.onload = () => {}; window.addEventListener("load", () => {}); document.addEventListener("DOMContentLoaded", () => {});
1
2
3性能优化:原则是,多使用内存、缓存;减少 CPU 计算量;减少网络加载耗时。【1】加载更快:减小资源体积 -- 压缩代码;减少访问次数 -- 合并代码;缓存;SSR;CDN。【2】渲染更快:css 放在 head,js 放在 body 最下面;尽早执行 js;懒加载;减少对 DOM 的频繁操作 -- 缓存 DOM 查询结果,多次 DOM 操作的合并。
节流、防抖:手写。
// throttle
const div = document.getElementById('div');
function throttle(fn, delay = 200) {
let timer = null;
return function () {
if (timer) {
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
div.addEventListener('drag', throttle(function (e) {
console.log(e.offsetX, e.offsetY);
}));
// debounce
const input = document.getElementById('input');
function debounce(fn, delay = 500) {
// timer 是闭包中的
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
input.addEventListener('keyup', debounce((e) => {
console.log(e.target);
console.log(input1.value);
}, 1000));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- 安全:【1】XSS 跨站脚本攻击:通过嵌入
<script>
脚本,注入恶意代码,进行控制和获取数据;使用替换特殊字符来预防,Cookie 设置 http-only 后 JS 无法获取 Cookie。【2】XSRF 跨站请求伪造:利用身份验证的漏洞,使用 post 接口 / 增加 token 验证来预防。
# 17. 课程总结【很有必要带你避免面试犯低级错误】
- 面试技巧。
# 18. 真题模拟【我是来告诉你答案是什么】
- 手写深度比较:
// 判断是否是对象或数组
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
// 全相等(深度)
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 一次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 函数声明和函数表达式:函数声明会在代码执行前预加载,函数表达式不会。
// 函数声明
function fn () {}
// 函数表达式
const fn = function () {}
1
2
3
4
2
3
4
- new Object() 和 Object.create() 的区别:
new Object() 等同于 {},原型 Object.prototype
Object.create(null),没有原型
Object.create({...}),可以指定原型
1
2
3
2
3
- 正则表达式。
- 如何捕获 JS 中的异常:
try...catch...finally
/window.onerror
。 - window.requestAnimationFrame():MDN (opens new window)。
- CI / CD:Continuous Integration -- 持续集成 、Continuous Delivery -- 持续交付、Continuous Deployment -- 持续部署。