前言
最近不少同学开始秋招了,一些大厂带动了提前批的风气,秋招开始出奇的早。
老话说得好:
❝基础不牢,地动山摇。
❞
于是我和我的小伙伴们一起为大家整理了一些面试可能涉及到的基础知识点,希望能帮助大家在秋招斩获 offer。「文中可能存在错误的地方,如果存在错误,还请大家及时指出」。
❝「本文将会持续更新,建议大家收藏,面试前拿出来捡一捡知识点。」
❞
「温馨提醒:文末还有重要内容!」
HTML
HTML5 新增了哪些标签?
炽翎:
template
section
nav
article
aside
header
footer
main
canvas
svg
video
audio
source
track
menu
等。
柒柚:
datalist
embed
等。
块级标签
炽翎:
div
p
h1-h6
ul
ol
dl
li
header
footer
aside
section
article
form
table
等。
行内标签
炽翎:
span
b
q
i
a
em
label
等。
替换标签(可设置宽高)
炽翎:
img
canvas
svg
video
iframe
input
button
等。
link
标签有些什么作用,加载 CSS 的时候与 @import
方法有什么区别?
炽翎:
link
标签可以实现资源加载、DNS 预解析等功能- 与
@import
方法相比,首先是加载时间的不同,link
标签不会被 DOM 解析阻塞,可以并行加载资源,而@import
加载的资源必须等到 DOM 解析结束后才能被加载;其次是资源引入方法的适用范围,link
标签可以通过 JavaScript createElement 创建并插入到文档中,实现动态引入,而@import
方法只能在 CSS 使用。
柒柚:
- link 是 HTML 提供的外部链接元素,不仅可以加载样式文件,也可以加载其他文件比如 tab 上的图片,脚本。@import 是 css 语法,只能用于加载样式文件。
- 多个 link 引入的 css 将会被同时加载,先完成加载的优先解析。@import 引入的 CSS 将在页面加载完毕后才被加载。
- @import 是 CSS2.1 才有的语法,故只可在 IE5+ 才能识别;link 标签作为 HTML 元素,不存在兼容性问题。
- 可以通过 JS 操作 DOM ,插入 link 标签来改变样式;由于 DOM 方法是基于文档的,无法使用@import 的方式插入样式。
- link 引入的样式权重大于@import 引入的样式。
src
和 href
的区别?
炽翎:
src
指的是source
,属性中可以设置src
的标签多为替换标签,用于将资源加载后替换该标签的内容,src
加载的资源是会阻塞 DOM 解析的。href
指的是hypertext reference
,属性中可以设置href
的标签多为引用或链接标签,用于引用互联网上的其他资源或设置锚点,href
引用的资源不会阻塞 DOM 解析,而是会并行加载。
script
标签的 async
属性和 defer
属性了解吗?
炽翎:
async
:并行加载,加载完成立即执行加载的资源,会打断 DOM 解析。defer
:并行加载延迟执行,加载完成后等待 DOM 解析完成后执行加载的资源,不会打断 DOM 解析。- 区别:
async
加载完成后会立即执行加载的资源,因此资源的执行顺序不可控,先加载完成的资源先执行;defer
加载完成后会将加载的资源按照加载开始顺序放到队列中,等待 DOM 解析完成后依次执行,资源执行属性可控。
HTML 标签语义化的好处是什么?
炽翎:
- 无障碍:方便无障碍引擎对网页内容进行解析。
- SEO:语义清晰,对搜索引擎爬虫友好。
- 可读性:HTML 代码可读性提高,利于维护。
CSS
说说 inline
和 inline-block
的区别?
炽翎:
inline
:将标签设置为行内标签,不可设置宽高。inline-block
:将标签设置为行内块级标签,可设置宽高。
柒柚:
- 行内元素设置 width 无效,height 无效(可以设置 line-height),设置 margin 和 padding 的上下不会对其他元素产生影响。
flex
布局了解吧, flex: 1
是几个属性的缩写?默认值分别是?
炽翎:
flex
布局常用属性:flex-direction
设置 flex 主轴方向,column 纵向从上到下,row 横向从左到右;justify-content
设置主轴方向上元素排列方式,center 居中,space-around 空白环绕均分,space-between 空白间隔均分,flex-start 主轴方向,flex-end 主轴反方向;align-items
设置交叉轴上元素排列方式,center 居中,stretch 交叉轴方向且各元素高度为 flexbox 高度(默认值),flex-start 交叉轴方向,flex-end 交叉轴反方向,baseline 基准线下沿对齐。flex: 1
:是flex-grow
flex-shrink
flex-basis
三个属性是缩写,默认值分别是:0 1 auto
,分别对应的是 flex 元素的占位、缩放、容器宽高。
响应式布局有哪些实现方式?
炽翎:
- 媒体查询
- flex 布局
- grid 布局(兼容性较差)
- 百分比布局
什么是 BFC ?BFC 如何触发?
炽翎:
- BFC:(Block Formatting Context)块格式化上下文,BFC 中的元素如何排列不会影响到 BFC 之外的元素。
- 触发 BFC:
overflow
不为visible
,float
不为none
,display
为inline-block
tabel-cell
table-caption
其中之一,position
不为relative
或static
。 - 作用:解决父元素高度塌陷问题;解决外边距重叠问题;清除内部元素浮动;
CSS 动画了解吗?为什么用 CSS 做动画比 JS 做动画好?
炽翎:
transition
:侦听属性发生变化时触发,搭配其他样式属性能实现大多数过渡动画。animation
:自动触发,搭配@keyframes
能实现大多数较复杂循环动画。- CSS 做动画:触发 GPU 加速,调用 GPU 能力,帧率高(60)。动态设置较困难,适合效果固定的动画。
- JS 做动画:占用 JavaScript 引擎,使用 CPU 计算,帧率低(30-50),易卡顿。动态设置简单,适合效果复杂且动态效果要求高的动画。
JavaScript
new
操作符做了什么?能使用代码模拟一下吗?
炽翎:
function Person(name) {
this.name = name;
}
function myNew() {
var person = Object.create({});
person.__proto__ = Person.prototype;
var obj = Person.call(person, 'Jack');
if (typeof obj === 'object') {
return obj;
} else {
return person;
}
}
var me = myNew(); // Person { name: 'Jack' }
Number() 的存储空间是多大,如果后台发送了一个超过最大字节的数字怎么办?
yufeng:
- Number 类型的实质是一个 64 位的浮点数。(8 字节)
- JavaScript 的 Number 类型使用 53 位表示小数位,10 位表示指数位,1 位表示符号位。因此指数部分最大值为 2^10=1024。因此对于 Number 的范围,表示范围是 2~2 也就是 5e~1.7976931348623157e+308。
- 如果是整数的范围的话,JavaScript 能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点)。
- 如果超过的话,可以后端发送字符串类型
this
指向能分清楚嘛? call
apply
bind
有什么区别?
炽翎:
- 只要没有使用显示绑定(
call
apply
bind
),那么谁调用this
就指向谁。 call
:第一个参数为更改的this
指向的对象,第二个及之后的参数为函数执行的参数,函数会被立即执行。apply
:第一个参数的作用与call
相同,第二个参数为函数执行的参数数组,函数会被立即执行。bind
:会创建一个新的函数,执行时this
指向第一个参数设置的对象,第二个及之后的参数为新函数执行的参数,函数不会被立即执行。
什么是闭包?有什么用?
炽翎:
- 闭包就像函数背了一个小书包,这个书包里面装了函数出生之后就要用到的一些东西,用专业一点的语言来描述就是在一个父函数中返回子函数,子函数中如果引用到了父函数中变量,则这些变量会成为一个闭包,在子函数的生命周期内都可以获取到。
- 作用:可以在函数的外部引用到内部的值,做一些封装,将一些内部的状态私有化,类似 TypeScript 中的 private;另外就是能够将一些频繁使用的变量长期保存在内存当中(注意内存泄漏)。
手写一个 Promise 吧?
炽翎:
- !@#¥%……&*,老子不面了!(开玩笑的开玩笑的,面试官捞捞我)
function myPromise(fn) {
// 回调函数集合
this.callbacks = [];
// resolve 方法
// 往实例上挂载 data
// 依次执行回到函数
function resolve(value) {
setTimeout(() => {
this.data = value;
this.callbacks.forEach((callback) => callback(value));
});
}
// 将 resolve 方法交还
fn(resolve.bind(this));
}
myPromise.prototype.then = function (onResolve) {
return new myPromise((resolve) => {
this.callbacks.push(() => {
// 将结果返回给传入的回调
const res = onResolve(this.data);
// 链式调用 的返回值还是 Promise 对象时
if (res instanceof myPromise) {
res.then(resolve);
} else {
resolve(res);
}
});
});
};
原型链
uu:
prototype
:每个 **函数对象 **都有一个 prototype 属性,这个属性指向函数的原型对象。对象以其原型为模板,从原型继承方法和属性,这些属性和方法定义在对象的构造器函数的 prototype 属性上,而非对象实例本身。prototype 就像构造函数的工具箱一样,里面装着各种各样的工具,并且一直伴随着构造函数。__proto__
:每个对象都有的属性,不管是实例、构造函数还是原型对象。__proto__
属性指向构造函数的原型对象:
person.__proto__ === Person.prototype;
Object.__proto__ === Function.prototype; // Object本质是由Function构造的 Function同理
Funtion.__proto__ === Funtion.prototype; // 这是一个比较特殊的地方
原型链本质上是通过
__proto__
链接起来的,而不是prototype
。记住以下要点:
1. 实例的
__proto__
指向构造函数的原型对象。 2. 函数对象的__proto__
指向Function.prototype
。 3. 原型对象的__proto__
指向Object.prototype
。 (不知道自己在写啥,不懂怎么提炼之前写的东西 orz)
计算机网络
HTTP header 有哪些字段?
炽翎:
- user-agent:用来让服务器识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。
- referer:请求来源。
- origin:跨域请求时存在,请求来源。
- cache-control:强缓存控制,字段见 MDN 。
- expires:强缓存控制,值为时间戳,标记缓存过期时间。
- cookie:状态保持,详细见浏览器部分。
- content-type:请求/响应体文件里 MIME 类型。
- content-length:请求/响应体长度。
HTTP 有哪些请求方法?
炽翎:
- GET:请求参数以键值对的形式拼接在 URL 中,参数只支持 URL 编码格式,通常用于获取资源。
- POST:请求参数以文本形式存放在请求体中,支持多种类型(MIME),编码格式较多,通常用于创建/修改资源。
- PUT:类似 POST,通常用于创建资源。
- DELETE:类似 POST,通常用于删除资源。
- OPTION:预检请求,用于检查服务器性能,通常出现在非简单的跨域请求中,可以通过设置 Access-Control-Max-Age 设置预检请求的有效时间,避免每次跨域请求前都发送预检。(非简单请求:GET HEAD POST 以外,content-type 为
text/plain
multipart/form-data
appliction/x-form-urlencoded
以外) - HEAD:只返回响应头。
HTTP 2.0 与 HTTP 1.1 有什么区别?
炽翎:
- 多路复用:允许多个请求同时通过一个 HTTP 2 连接收发。由于 TCP 存在慢启动过程,如果为每一份报文都创建一个 TCP 连接,将多次经历慢启动阶段,这对于 HTTP 这种具有突发性和即时性的请求来讲非常消耗时间,因此,复用同一个 TCP 连接能极大提高传输效率,避免慢启动对传输速率带来的影响。HTTP 1.1 中 RFC 2616 规定同一域名下只允许同时存在 2 个 TCP 连接,即只允许存在 2 个 HTTP 连接,超过数量的连接会被阻塞(Chrome 为 6 个)。
- 二进制帧:在 HTTP 2.0 中,报文将被分为二进制帧进行传输,可以在一个连接上完成传输(多路复用)。
- 首部压缩:在服务端与客户端之间分别保存一个 hash table,用于记载在传输过程中出现过的报文首部字段。当相同的报文首部再次出现时,报文传输过程中只需要带上对应首部字段的 ID 即可。
- 服务端推送:HTTP 1.1 中请求只能由客户端发起,且遵循请求-应答模式,一问一答,客户端要获取到实时信息只能依靠轮询的形式,非常不优雅且性能差。HTTP 2.0 中客户端发起一次请求后,服务端可以进行多次响应,能够进行一些资源的预加载,实现服务端的推送功能。
keep-alive 了解吗?说说它的作用?
炽翎:
- HTTP 请求发出,TCP 连接创建,收到响应后客户端不释放连接,复用同一个 TCP 连接通道进行通信,避免了建立连接和释放连接带来的不必要的开销。
如何防止传输的数据被修改?也就是说如何保证数据发送和接受的时候数据是没有变化的?
yufeng:
- 采用 https 协议传输,网络安全相关,详情:blog.csdn.net/Endno/artic…。
浏览器
localStorage 和 sessionStorage 的区别?
炽翎:
- localStorage:键值对,大小约 5M,永不删除,除非手动,所有同源窗口共享。
- sessionStorage:键值对,大小约 5M,会话关闭立即删除,按照会话窗口独立,即使同源也不共享。
cookie 知道吗?有哪些字段?
炽翎:
- 由于 HTTP 是无状态的,为了在服务端与客户端之间做状态保持,cookie 应运而生。cookie 本质是一份在服务端与客户端之间传输的文本片段,其中保存了一些客户端的状态信息,方便服务端读取并进行其他操作。
- Name:键名。
- Value:键值。
- Domain:能够携带该键值对的域名,开头为
.
则表示其子域名也可携带。 - Path:能够携带该键值对的文件路径。
- Expires/Max-Age:cookie 过期时间的时间戳,超时则自动删除。
- Size:cookie 大小。
- HttpOnly:禁止 JavaScript 对 cookie 进行读取和修改。
- Secure:仅允许在 HTTPS 中传输。
- SameSite:禁止某些请求中携带 cookie,值为
None
Lax
Strict
。值为None
时,任何请求都可以携带该 cookie;值为Lax
时,部分跨站请求无法发送该 cookie(仅a
link
GET
请求可以携带);值为Strict
时,所有跨站请求都无法发送该 cookie,仅同站请求允许。(Chrome 80 版本前默认值都为None
,80 版本后默认值变成了Lax
,WWDC 2020 上 Safari 的新版本也由None
改为了Lax
)
网络安全
XSS 了解吗?如何进行一次 XSS 攻击?
- XSS(跨站脚本攻击):在未经转译的
url
或是用户输入域中输入攻击脚本,实现执行脚本获取 cookie、打开危险攻击网页等功能进行攻击。 - 预防:所有的用户输入都是不可信的,
url
和用户输入的文本内容必须经过转译,防止其中的内容称为 html 中的一部分被浏览器解析。cookie 也可以通过设置 HttpOnly 禁止 JavaScript 读取和写入 cookie。
CSRF 了解吗?如何进行一次 CSRF 攻击?
- CSRF(跨站请求伪造):在未做 CSRF 防护的网站 A 中诱导用于点击第三方攻击网站 B,第三方攻击网站 B 中存在向网站 A 的服务器发送的请求,访问网站 B 就自动发送,请求会自动带上网站 A 的 cookie,能够冒用网站 A 的身份,这就形成了一次 CSRF 攻击。
- 预防:在 HTTP header 中加入 origin 和 referer 来验证请求来源,拦截第三方的请求;用户访问时由服务端生成一个随机 token,保存在服务端 session 中,同时将 token 发送至客户端,客户端的每个请求都需要带上这个 token,与服务端 session 中保存的 token 比对,若 token 未过期且比对通过,才认证为一次合法的请求;cookie SameSite 属性;
框架原理
Vue 实现双向绑定的原理是什么?
炽翎:
- 响应式原理:Object.defineProperty()重写对象的 setter/getter,遍历对象中的属性,递归重写。
var data = {
name: 'Jack Wang',
age: 21,
salary: 3500,
array: ['Jack', 'should', 'fighting']
};
// 模拟渲染
function render() {
console.log('render');
}
// 定义需要重写的数组方法
var method = ['push', 'pop', 'unshift', 'shift', 'reverse', 'sort', 'splice'];
// 获取数组原型
var arrayProto = Array.prototype;
// 创建新的原型对象
var proto = Object.create(arrayProto);
// 遍历需要重写的数组方法
method.forEach(function (method) {
// 修改新的原型对象
proto[method] = function () {
// 调用数组原型方法
var res = arrayProto[method].call(this, ...arguments);
render();
return res;
};
});
// 数据劫持/代理方法
function observe(obj) {
// 非对象类型直接返回
if (!obj || typeof obj !== 'object') {
return;
}
// 为数组则将新的原型添加到其原型链上
if (Array.isArray(obj)) {
obj.__proto__ = proto;
return;
}
// 遍历对象 key 将其传入
Object.keys(obj).forEach(function (key) {
defineReactive(obj, key, obj[key]);
});
}
// 设置对象属性
function defineReactive(obj, key, value) {
// 递归子属性
observe(value);
Object.defineProperty(obj, key, {
// 重写 getter
get: function () {
console.log('get');
return value;
},
// 重写 setter
set: function (newValue) {
console.log('set', newValue);
// 递归子属性
observe(newValue);
// 若新值不等于旧值 则发生渲染
if (newValue !== value) {
value = newValue;
render();
}
}
});
}
// 进行数据劫持/代理
observe(data);
var data = {
name: 'Jack Wang',
age: 21,
salary: 3500,
array: ['Jack', 'will', 'be', 'the', 'best']
};
// 模拟渲染
function render(params) {
console.log('render');
}
// 代理逻辑
var handler = {
get(target, key) {
console.log('get');
// 若获取到的值为对象 则递归
if (typeof target[key] === 'object' && target[key] !== null) {
return new Proxy(target[key], key);
}
return Reflect.get(target, key);
},
set(target, key, value) {
console.log('set', value);
// 若设置对象的 length 属性 则直接返回
if (key === 'length') {
return;
}
Reflect.set(target, key, value);
render();
}
};
// 数据代理
var proxy = new Proxy(data, handler);
- 双向绑定:自定义 v-model,设置子组件 model 属性,设置 v-model 侦听的属性值,同时绑定属性变化时执行的事件,实现自定义 v-model,即双向绑定。
Vue 中的 computed 和 watch 有什么区别?
炽翎:
- computed:计算属性,当一个值由 data 中的多个值计算出来得到时,我们可以使用 computed 对其进行计算并返回,这个计算结果是被缓存的。当这个值依赖的 data 值未发生变化时,将不会重新计算而是直接返回缓存的结果。从原理上来讲,computed 被创建后,会生成一个惰性的 watcher 对象,当其依赖的 data 值发生变化时,dirty 值会被标记为 true,当其被引用时,会根据 dirty 的值来判断是重新计算结果(true)还是直接取缓存(false)。
- watch:属性侦听,当被侦听的属性发生变化后,会被自动触发的一些操作。从原理上来讲,watch 被创建后会生成一个 watcher 对象,当被侦听的属性发生变化时会触发回调。
性能优化
炽翎:
- 这一项看我的 博客 吧,有点偷懒不想整理了。
特别鸣谢
- 文中的一些内容,都是我和我的小伙伴们一起讨论学习的答案,同时我的小伙伴们也参与到了编辑中,这里我要特别感谢一下他们。
- 我们是一群抱团取暖的应届生,因为前端和一个进大厂的梦想而遇见彼此,如果你也想加入到我们当中来,欢迎加我微信,大家一起学习交流,一起进步!(我的微信在公众号有~)
文末福利
除了基础知识要牢固之外,面试中还有非常多的面试技巧需要注意的,这些都放在了我的公众号:「Hello FE」 中。
公众号中有个「大厂系列」,里面有很多「大厂实习生的经验总结」,「关注还会赠送一份前端书籍大全(真的非常全)」,欢迎大家关注,二维码放楼下: