面试题2
https://juejin.im/post/5e757f376fb9a07cde64f920
HTML
1. 必考:你是如何理解 HTML 语义化的?
html语义化就是在合适的位置使用正确的标签。比如段落使用p标签、标题使用h1-h6标签、文章使用article标签
html语义化相对于div + css的优点
- 网页结构更清晰
- 有利于团队的开发和维护,便于阅读理解
- 有利于搜索引擎的检索抓取
- 有利于视觉障碍用户使用设备的读取,如屏幕阅读器
2. meta viewport 是做什么用的,怎么写?
专为移动设备下显示所设计的.只有检测到在移动设备上使用包含meta的文档时, meta标签才会起作用.
手机浏览器是把页面放在一个虚拟的窗口(viewport)中,用户可以通过平移和缩放来看网页的不同部分
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
width
:控制 viewport 的宽度,可以指定的具体值或者特殊的值,如device-width
为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。height
:和 width 相对应,指定高度initial-scale
:初始缩放比例,即页面首次加载时的缩放比例maximum-scale
:允许用户缩放到的最大比例minimum-scale
:允许用户缩放到的最小比例user-scalable
:用户是否可以手动缩放
3. 你用过哪些 HTML 5 标签?
布局标签: header footer main article section aside time
功能标签: canvas video aduio
4. H5 是什么?
狭义上的H5是一种编程语言,是HTML5的简略写法
广义上的H5则不同,浏览的网页、使用的微信乃至于手机中的软件,大部分都有H5的功劳
因此H5在国内互联网圈涵盖的范围极大,凡是使用了H5技术的网页微信页面等页面都可以被称为H5。H5技术也不仅仅局限于单纯的HTML5了,涵盖了HTML5、CSS3、JavaScript等一系列前端技术
CSS
1. 必考:两种盒模型分别说一下
-
盒子模型组成:分为内容(content)、填充(padding)、边框(border)和边界(margin)
-
IE盒模型:属性width,height包含content、border和padding
-
W3C标准盒模型:属性width,height只包含内容content,不包含border和padding
-
box-sizing(CSS新增的属性)用于选择盒模型
- content-box: 标准盒模型
- border-box: IE盒模型
如果在ie6,7,8中DOCTYPE缺失会将盒子模型解释为IE盒子模型。若在页面中声明了DOCTYPE类型,所有的浏览器都会把盒模型解释为W3C盒模型
2. 必考:如何垂直居中?
水平居中
-
设置一个宽度,然后添加
margin:0 auto
属性/*子盒子有宽度*/ .child { margin: 0 auto; }
-
利用
text-align:center
实现.parent { text-align: center; } .child { display: inline-block; }
-
绝对定位
position: absolute
+margin-left
/*子盒子有宽度*/ .parent { position: relative; } .child { position: absolute; top:0; left: 50%; margin-left: -100px; /*子盒子宽度的一半*/
-
绝对定位
position: absolute
+transform
.parent { position: relative; } .child { position: absolute; top:0; left: 50%; transform: translate(-50%); }
-
使用flex布局
.parent { display: flex; justify-content: center; }
垂直居中
-
单行文本垂直居中
.parent { height: 100px; border: 1px solid #ccc; /*设置border是为了方便查看效果*/ } .child { line-height: 100px; }
-
绝对定位
position: absolute
+transform
.parent { width: 100%; height: 100%; position: relative; } .child { width: 500px; border: 1px solid #ccc; /*设置border是为了方便查看效果*/ position: absolute; top: 50%; transform: translateY(-50%); }
-
绝对定位
position: absolute
+ 四边值相等.parent { position: relative; width: 100%; height: 100%; } .child { width: 500px; height: 30%; border: 1px solid #ccc; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
-
display:table
.parent {
width: 100%;
height: 100%;
display: table;
}
.child {
display: table-cell;
vertical-align: middle;
}
水平垂直居中
-
绝对定位
position: absolute
+transform
.container { position: relative; } .box { position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); /*已知高度宽度,margin-top margin-left设置为本身高度宽度的一般*/ }
-
绝对定位
position: absolute
+ 四边值相等.container { width: 500px; height: 500px; border: 1px solid #000; position: relative; } .box { position: absolute; width: 200px; height: 200px; background-color: rgb(247, 207, 207); margin: auto; top: 0; left: 0; right: 0; bottom: 0; }
-
使用flex布局
.container { display:flex; justify-content: center; align-items:center; }
-
使用grid布局
.container { width: 500px; height: 500px; border: 1px solid #000; display:grid; } .box { position: absolute; width: 200px; height: 200px; background-color: rgb(247, 207, 207); justify-self:center; align-self:center; }
-
table-cell
组合使用display:table-cell和vertical-align、text-align,使父元素内的所有行内元素水平垂直居中
/*利用 table 的单元格居中效果展示*/ .container { display: table-cell; text-align: center; vertical-align: middle; }
-
vertical-align
+inline-block
.container { width: 500px; height: 500px; border: 1px solid #000; text-align: center; } .container::after { content: ""; display: inline-block; vertical-align: middle; height: 100%; } .box { width: 200px; height: 200px; background-color: rgb(247, 207, 207); display: inline-block; vertical-align: middle; }
3. 必考:flex 怎么用,常用属性有哪些?
Flex是FlexibleBox的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性
任何一个容器都可以指定为Flex布局。行内元素也可以使用Flex布局。注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效
采用Flex布局的元素,称为Flex容器(flexcontainer),简称"容器"。它的所有子元素自动成为容器成员,称为Flex项目(flexitem),简称"项目"
容器默认存在两根轴:水平的主轴(mainaxis)和垂直的交叉轴(crossaxis),项目默认沿主轴排列
设置在容器上的属性
flex-direction
——决定主轴的方向(即项目的排列方向)flex-wrap
——定义一条轴线排不下时,如何换行flex-flow
——是flex-direction
和flex-wrap
的简写,默认值为row nowrap
justify-content
——定义项目在主轴上的对齐方式align-items
——定义项目在交叉轴上如何对齐align-content
——定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用
设置在项目上的属性
order
——定义项目的排列顺序。数值越小,排列越靠前,默认为0flex-grow
——定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大flex-shrink
——定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小flex-basis
——定义了在分配多余空间之前,项目占据的主轴空间。浏览器根据该属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小flex
——flex-grow
,flex-shrink
和flex-basis
的简写,默认值为0 1 auto
- 属性有两个快捷值:
auto
(1 1 auto
) 和 none (0 0 auto
)
- 属性有两个快捷值:
align-self
——允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性
4. 必考:BFC 是什么?
BFC的概念
块格式化上下文(Block FormattingContext,BFC)是一个独立的布局环境
如果一个元素符合触发 BFC 的条件,则 BFC 中的元素布局不受外部影响
创建BFC
(1)根元素
(2)浮动元素float=left|right或inherit(≠none)
(3)绝对定位元素position=absolute或fixed
(4)display=inline-block|flex|inline-flex|table-cell或table-caption
(5)overflow=hidden|auto或scroll(≠visible)
BFC 的特性
- BFC 是一个独立的容器,容器内子元素不会影响容器外的元素。反之亦如此
- 盒子从顶端开始垂直地一个接一个地排列,盒子之间垂直的间距是由 margin 决定的
- 在同一个 BFC 中,两个相邻的块级盒子的垂直外边距会发生重叠
- BFC 区域不会和 float box 发生重叠
- BFC 能够识别并包含浮动元素,当计算其区域的高度时,浮动元素也参与计算
BFC 的作用
-
包含浮动元素(清除浮动)
-
浮动元素会脱离文档流(绝对定位元素也会脱离文档流),导致无法计算准确的高度,这种问题称为高度塌陷
-
解决高度塌陷问题的前提是能够识别并包含浮动元素,也就是清除浮动
解决方法:在容器(container)中创建 BFC
-
-
避免外边距折叠
外边距折叠(Margin collapsing)只会发生在属于同一BFC的块级元素之间。如果它们属于不同的 BFC,它们之间的外边距则不会折叠
5. CSS 选择器优先级
- 按权重和来源
!important
——infinity
- 按特指度排序
1 0 0 0
——行内样式0 1 0 0
——#id
0 0 1 0
——.class /:伪类 / 属性选择器
0 0 0 1
——标签 / 伪元素
0 0 0 0
——*
- 继承样式优先级低于通配符选择器样式
- 按前后位置排序
- 如果两个优先级相同,则最后出现的优先级高,!important也适用
(1)样式应用时,css会先查看规则的权重(!important),加了权重的优先级最高,当权重相同的时候,会比较规则的特殊性
(2)特殊性值越大的声明优先级越高
(3)相同特殊性值的声明,根据样式引入的顺序,后声明的规则优先级高(距离元素出现最近的)
6. 清除浮动说一下
浮动的概念
- 浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止
- 由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样
浮动的影响
浮动的清除
-
clear属性的空标签
<div class="parent"> <div class="child">xxx</div> <div class="child">xxx</div> <div class="clearfix"></div> </div>
.child { height: 100px; width: 100px; float: left; } .clearfix { clear: both; }
-
触发BFC——父元素添加
overflow:hidden
.parent { overflow: hidden; }
-
:after伪元素
.clearfix::after { content: ""; display: block; clear: both; visibility: hidden; height: 0; }
-
before和after双伪元素清除浮动
.clearfix::before, .clearfix::after { content: ""; display: table; } .clearfix::after { clear: both; }
-
父元素设置高度
.parent { height: 20px; }
7.vue中APP的适配问题,使用单位
媒体查询 @media query
- 媒体查询的语法
@media not|only mediatype and (expressions) {
CSS 代码...;
}
-
mediaType
: all | print | screen 等 -
expressions
:- min-device-width——设备宽度,缩放不响应
- min-width——视口宽度,缩放响应
-
栅格系统尺寸—elmentUI
extra small small mediun large extra large <768px ≥768px ≥992px ≥1200px ≥1920px 手机 平板 桌面显示器 桌面显示器 桌面显示器 -
使用媒体查询
平板背景为红色
@media screen (min-width: 768px) and (max-width: 882px) { body: {background-color: red} }
rem + 媒体查询
rem(font size of the root element)是指相对于根元素的字体大小的单位,默认16px
屏幕划分成多等份
-
动态设置html标签
font-size
大小- 假设设计稿是750px,将整个屏幕划分成15等份,
1rem = 50px
- 假设设计稿是750px,将整个屏幕划分成15等份,
-
页面元素的rem值=页面元素值(px)/(屏幕宽度/划分的份数)
屏幕宽度/划分的份数
为是html中的font-size
的大小
-
代码实现
@media screen and (min-width:320px) { html { font-size: 50px } } @media screen and (min-width:640px) { html { font-size: 100px } }
按照缩放比例动态改变
-
通过修改不同设备根元素的
font-size
适配html { font-size: 16px } @media screen and (min-width:768px) { html { font-size: 18px !important; } } @media screen and (min-width: 992px) and (max-width: 1200px){ html { font-size: 20px !important; } }
-
setRem
函数动态修改根元素font-size
function setRem() { const baseSize = 16 //获取屏幕宽度 const htmlWidth = document.documentElement.clientWidth || document.body.clientWidth //计算缩放比例 屏幕宽度/设计稿宽度 let scale = htmlWidth / 1536 //设置根元素的字体大小 document.querySelector("html").style.fontSize = baseSize * scale + "px" } setRem() window.addEventLinstence("resize", () => { setRem() })
-
设置元素的宽高为 rem
-
scss
@function px2rem($px){ // rem基准值 $rem : 41.4px; @return ($px/$rem) + rem; } .box{ background-color: red; width: px2rem(100px); height: px2rem(100px); }
-
插件
- px to rem ——vscode
- postcss-pxtorem——webpack
-
原生 JS
1. 必考:ES 6 语法知道哪些,分别怎么用?
-
let和const——用于声明块级作用域的变量且不具备变量提升
let
用于声明变量const
用于声明常量,对象类型栈中的内存地址不可改变,可以改变堆中的值
-
解构赋值
-
数组的结构赋值——数组是按位置匹配
let [a, b, c] = [1, 2, 3];
-
对象的结构赋值——对象是按属性名匹配
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
-
-
模板字符串
-
使得字符串的拼接更加的简洁,支持变量、HTML文档与换行
let a = "aaa" let b = `bbb ${a} bbb`
-
-
箭头函数
-
用于简化函数表达式和改善函数this指向的问题
var sum = (num1, num2) => num1 + num2;
-
-
扩展运算符(...)
-
用于函数(剩余参数)——用于获取函数的多余参数,不需要使用
arguments
对象function add(...values) { let sum = 0; for (var val of values) { //数组 sum += val; } return sum; }
-
用于数组或字符串——将内容依次取出
var str = "asdfghjkl" console.log(...str) //a s d f g h j k l var arr = [1,2,3,4,5,6] console.log(...arr) //1 2 3 4 5 6
-
用于对象——克隆或者属性拷贝
var obj1 = { foo: 'bar', x: 42 }; var obj2 = { foo: 'baz', y: 13 }; var clonedObj = { ...obj1 }; // 克隆后的对象: { foo: "bar", x: 42 } var mergedObj = { ...obj1, ...obj2 }; // 合并后的对象: { foo: "baz", x: 42, y: 13 }
-
-
类(class)
-
让面向对象编程变得更加简单和易于理解
class Person { constructor(name, age) { this.name = name this.age = age } sayHi() { alert(`my name is ${this.name}`) } } class Worker extends Person { constructor(name,age,job) { super(name,age) this.job = job } sayJob() { alert(`my job is ${this.job}`) } }
-
-
模块化(Module)
- 每一个模块都有自己单独的作用域
- 为模块创造了命名空间,防止函数的命名冲突
- 模块的功能主要由 export 和 import 组成
- 通过 export 来规定模块对外暴露的接口
- 通过import来引用其它模块提供的接口
- 每一个模块都有自己单独的作用域
-
Promise
-
异步编程的一种解决方案,比传统的解决方案callback更加的优雅
-
Promise
是一个容器,保存着在某个未来结束的异步操作的结果 -
回调函数
setTimeout(function(){ console.log('Hello'); // 1秒后输出"Hello" setTimeout(function(){ console.log('Hi'); // 2秒后输出"Hi" }, 1000); }, 1000);
-
Promise
var wait = new Promise((resolve, reject) => { setTimeout(resolve, 2000); }) wait.then(() => console.log('Hello')).then(() => console.log('hi'))
-
-
async/await
- async
- 用于申明一个 function 是异步的
async
函数的返回值是 Promise 对象
- await
- 用于等待一个异步方法执行完成
await
只能出现在 async 函数中
const axios = require("axios"); async function getZhihuTopSearch(id) { const url = "https://www.zhihu.com/api/v4/search/top_search"; //将异步操作的值赋给变量 const response = await axios(url); console.log(response); } getZhihuTopSearch(5);
- async
2. 必考 Promise、Promise.all、Promise.race 分别怎么用?
Promise是异步编程的一种解决方案,可以解决回调地狱的问题。
Promise
是一个容器,保存着在某个未来结束的异步操作的结果Promise
具有三种状态,并且一旦状态更改,不再变化。任何时候都可以获得结果pedding
——等待状态resolved
——已处理rejected
——已拒绝
Promise.all——同时等待多个Promise异步操作完成以后再做某件事
Promise.race ——率先改变状态的Promise实例作为返回值返回
3. 必考:手写函数防抖和函数节流
//防抖函数
function debounce(fn, wait, immediate=true) {
let timer, result
return function(...args) {
if(immediate) {
if(!timer) {
result = fn.apply(this, args)
}
timer = setTimeout(() => {
timer = null
}, wait)
} else {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
return result
}
}
//节流函数
function throttle(fn, wait = 1000) {
let timer;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;//到达指定时间将定时器清除,让其能够再次进入执行fn
}, wait);
}
};
}
4. 必考:手写AJAX
//1.创建Ajax实例
let xhr = new XMLHttpRequest(); //IE下为ActiveObject对象
//2.设置请求配置
xhr.open("GET", "data/a.text", true)
//3.事件监听:通过监听readyStateChange事件,获知AJAX状态改变
xhr.onreadyStateChange = function() {
//请求完成 获取服务器返回的响应头响应主体
if(xhr.readyState == 4 && xhr.status == 200 ) {
console.log(xhr.responseText)
}
}
//4.发送Ajax请求
xhr.send()
5. 必考: this 的指向问题
普通函数的this
- 函数调用模式
- 如果一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象
- 方法调用模式
- 如果一个函数作为一个对象的方法来调用时,this 指向这个对象
- 构造器调用模式
- 如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象
- apply 、call 和 bind 调用模式
- 显式地指定调用函数的 this 指向
函数调用方式 | 函数内部this的指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 创建的实例对象 |
对象方法调用 | 该方法所属的对象 |
事件绑定方法 | 绑定事件的的对象 |
定时器函数 | window |
立即执行函数 | window |
匿名函数 | window |
箭头函数的this
箭头函数没有自己的this值,箭头函数中所使用的this都是来自函数作用域链,它的取值遵循普通普通变量一样的规则,在函数作用域链中一层一层往上找
箭头函数的this是继承父执行上下文里面的this
apply实现bind函数
6. 必考:闭包/立即执行函数是什么?
立即执行函数
-
立即执行函数的定义
-
声明一个匿名函数,马上调用这个匿名函数
(function(){alert("立即调用匿名函数")})()
-
-
立即执行函数的作用
- 创建一个独立的作用域
- 作用域里面的变量,外面访问不到(即避免「变量污染」)
var liList = ul.getElementsByTagName('li') for(var i=0; i<6; i++){ liList[i].onclick = function(){ alert(i) } } //用户在for循环完后点击,而i贯穿作用域不是每个li都有独立的i变量。点击时全为6
var liList = ul.getElementsByTagName('li') for(var i=0; i<6; i++){ !function(ii){ liList[ii].onclick = function(){ alert(ii) // 0、1、2、3、4、5 } }(i) }
闭包
7. 必考:跨域的几种实现方式
JSONP
JSONP的原理
——利用script
标签获取资源不受到同源策略的限制。可以动态创建 script
标签,再请求一个带参网址实现跨域通信
//**前端代码**
var script = document.createElement("script");
script.type = "text/javascript";
// 传参并指定回调执行函数为jsonpCallback
script.src = "http://localhost:3000?callback=jsonpCallback";
document.head.appendChild(script);
//回调函数
function jsonpCallback(data) {
console.log(data);
}
// **后端代码**
const express = require('express')
const app = express()
app.get('/', (req,res) => {
let callback = req.query.callback
res.send(`${callback}(${JSON.stringify({
success:0,
data:{
name:"xxx"
}
})})`)
})
app.listen(3000, () => { console.log('开启3000端口') })
jsonp缺点
- 需要服务器端支持
- 只能实现get一种请求
- 回调函数需要为全局函数
- 不安全
jQuery的封装
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "jsonpCallback", // 自定义回调函数名
data: {}
});
跨域资源共享 CORS
跨域资源共享(CORS) 是一种机制
使用额外的 HTTP 头来告诉浏览器让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。
当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求
- 客户端(发送ajax和fetch请求)
- 发生跨域问题时,服务器跨域接收到请求,但是返回内容被浏览器屏蔽
- 服务端设置相关的头信息
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
// * 不允许携带cooike | 具体地址 只支持单源
res.header('Access-Control-Allow-Headers',
'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods',
'PUT, POST, GET, DELETE, OPTIONS');
//预检请求
if (req.method == 'OPTIONS') {
res.send(200); //在正常的请求之前,会发送一个验证,是否可以请求。
}
else {
next();
}
});
CORS 缺点
- 允许源只能支持一个或全部
- 允许全部源时,不支持携带cookie
http proxy (Nodejs中间件)
利用node + webpack + webpack-dev-server
代理接口跨域。
在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些https服务报错时用
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}],
noInfo: true
}
}
nginx反向代理
跨域原理
同源策略是浏览器的安全策略,不是HTTP协议的一部分
服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题
实现思路
通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
postMessage跨域
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
用法
postMessage(data,origin)
方法接受两个参数
data
: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin
: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
1.)a.html:(http://www.domain1.com/a.html)
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
};
// 接受domain2返回数据
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>
8. 常考:async/await 怎么用,如何捕获异常?
async function
声明用于定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果
简单来说,如果在函数前使用async
关键字,那么这个函数返回一个promise
。如果你返回的不是一个promise
,JavaScript也会自动把这个值"包装"成Promise的resolve值
9. 常考:如何实现深拷贝?
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用
-
浅拷贝的实现
- 浅拷贝可以使用
Object.assign
和扩展运算符...
来实现
- 浅拷贝可以使用
-
深拷贝的实现方法
-
JSON.parse(JSON.stringify(obj))
-
递归——递归的复制所有层级属性
function deepCopy(object) { //判断参数是否为object if (!object || typeof object !== "object") return; let newObject = Array.isArray(object) ? [] : {}; for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]; } } return newObject; }
-
jQuery的extend方法
$.extend( [deep ], target, object1 [, objectN ] )
-
10. 常考:如何用正则实现 trim()?
String.protoType.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "");
}
11. 常考:不用 class 如何实现继承?用 class 又如何实现?
ES5
//0.没有专门的类声明方法
function Person(name, age) { //1.即是构造函数又是类
this.name = name
this.age = age
}
Person.protoType.showName = function() { //2.方法独立于类之外
alert(this.name)
}
//3.没有专门的继承方法
function Worker(name, age, job) {
Person.call(this, name, age) //4.从父类继承属性要靠骗
this.job = job
}
Worker.prototype = new Person() //5.没有专门继承父类方法的方式
Worker.prototype.constructor = Worker
ES6
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
showName() {
alert(this.name)
}
showAge() {
alert(this.age)
}
}
class Wroker extends Person {
constructor(name, age, job) {
super(name, age)
this.job = job
}
//父类的方法自动继承
showJob() {
alert(this.job)
}
}
let foo = new Worker("foo", "18", "bar")
12. 常考:如何实现数组去重?
-
set与解构赋值去重
Set函数可以接受一个数组(或类数组对象)作为参数来初始化
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } return [...Set(arr)] //return Array.from(new Set(arr)) }
-
indexOf方法去重1
数组的indexOf()方法可返回某个指定的元素在数组中首次出现的位置
定义一个空数组,调用indexOf方法对原来的数组进行遍历判断,如果元素不在res中,则将其push进res中
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } let res = [] arr.forEach((item) => { if(res.indexOf(item) === -1) { res.push(item) } }) return res }
-
indexOf方法去重2
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } return arr.filter((item, index) => { return arr.indexOf(item) === index }) }
-
相邻元素去重
调用了数组的排序方法sort(),然后根据排序后的结果进行遍历及相邻元素比对,如果相等则跳过改元素,直到遍历结束
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } arr = arr.sort() let res = [] for (let i = 0; i < arr.length; i++) { if (arr[i] !== arr[i-1]) { res.push(arr[i]) } } return res }
-
利用对象属性去重
创建空对象,遍历数组,将数组中的值设为对象的属性,并给该属性赋初始值1,每出现一次,对应的属性值增加1,这样,属性值对应的就是该元素出现的次数了
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } let res = [], obj = {} for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { res.push(arr[i]) obj[arr[i]] = 1 } else { obj[arr[i]]++ } } return res }
-
双循环去重
定义一个包含原始数组第一个元素的数组, 然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组;
function unique(arr) { if(!Array.isArray(arr)) { return new Error("type error") } let res = [arr[0]] for (let i = 1; i < arr.length; i++) { let flag = true for (let j = 0; j < res.length; j++) { if (arr[i] === res[j]) { flag = false; break } } if (flag) { res.push(arr[i]) } } return res }
13. 放弃:== 相关题目(反着答)
只用===
14. 送命题:手写一个 Promise
15.创建对象的几种实现方式
-
工厂模式
-
描述:使用函数抽象创建对象的细节
-
缺点:创建的对象无法和某个类产生联系
function person(name, age) { var obj = new Object() obj.name = name obj.age = age obj.sayName = function() { alert(this.name) } return obj } var person1 = person("xiaoyu", 18)
-
-
构造函数模式
-
描述:构造函数需要使用new, new关键字将对象的原型指向构造函数的prototype属性;并且将this指向新创建的对象
-
缺点:每个创建的对象都重新开辟内存空间用于保存相同的方法
function Person(name, age) { this.name = name this.age = age this.sayName = function() { alert(this.name) } } var person1 = new Person("xiaoyu", 18) person1.constructor = Person
-
-
原型模式
-
描述:使用原型对象让所有的对象实例共享系统的属性和方法
-
缺点:无法为对象设置初始值
function Person() { } Person.protoType = { name: "xiaoyu", age: 18, sayName: function() { alert(this.name) } } var person1 = new Person()
-
-
构造函数 + 原型
- 描述:构造函数模式 与 原型模式相结合
- 缺点:两种模式相结合 代码封装性不好
function Person(name, age){ this.name = name; this.age = age; } Person.prototype = { constructor: Person, sayName: function(){ alert(this.name); } } var person1 = new Person("xiaoyu", 18)
-
动态原型模式
function Person(name, age){ this.name = name; this.age = age; if(typeof this.sayName !== "function" ){ Person.prototype.sayName = function(){ alert(this.name); } } } var person1 = new Person("xiaoyu", 18)
-
寄生构造函数模式
function Person(name, age) { var obj = new Object() obj.name = name obj.age = age obj.sayName = function() { alert(this.name) } return obj } var person1 = new Person("xiaoyu", 18)
16.继承的几种实现方式
继承是面向对象语言中最重要的一个概念。由于在 JavaScript 中函数没有签名,无法实现接口继承,只支持实现继承
-
原型链
- 缺点:属性被实例共享;不能向父类传递参数
function Parent() {} function Child() {} Child.prototype = new Parent()
-
借用构造函数(经典继承)
- 优点:避免属性共享;可以向父类传参
- 缺点:方法构造函数中定义,创建实例都会创建一次
function SuperType(name){ this.name = name this.colors = ["red", "blue", "green"]; } function SubType(name){ //继承了 SuperType SuperType.call(this, name); } var instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); console.log(instance2.colors); //"red,blue,green"
-
组合继承
- 描述:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
- 缺点:调用两次父类构造函数
function SuperType(name){ this.name = name this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ //继承属性 SuperType.call(this,name); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ console.log(this.age); } var instance1 = new SubType("james",9); instance1.colors.push("black"); console.log(instance1.colors); //"red,blue,green,black" instance1.sayName(); // "james" instance1.sayAge(); // 9 var instance2 = new SubType("kobe",10); console.log(instance2.colors); //"red,blue,green" instance2.sayName(); // "kobe" instance2.sayAge(); // 10
-
原型式继承(Object.create())
function object(o){ function F(){}; F.prototype = o; return new F(); }
DOM
1. 必考:事件委托
事件委托本质
- 利用浏览器事件冒泡的机制——父级元素可以通过事件对象获取到触发事件的目标节点,因此可以由父级元素统一监听和处理多个子元素的事件
事件委托的优点
- 不必为每一个子元素都绑定一个监听事件
- 减少内存的消耗
- 实现事件的动态绑定——比如新增子节点
2. 曾考:用 mouse 事件写一个可拖曳的 div
var box = document.querySelector("#box")
var position = null;
var dragging = false;
box.addEventListener("mousedown", function (e) {
dragging = true;
position = [e.clientX, e.clientY]; //获取坐标
});
document.addEventListener("mousemove", function (e) {
if (dragging === false) return;
//获取现在的坐标
const x = e.clientX;
const y = e.clientY;
//位移: 当前坐标 - 位置信息
const deltaX = x - position[0];
const deltaY = y - position[1];
//移动
const left = parseInt(box.style.left) || 0;
const top = parseInt(box.style.top) || 0;
box.style.left = left + deltaX + "px";
box.style.top = top + deltaY + "px";
//保存现在坐标作为位置信息
position = [x, y];
});
document.addEventListener("mouseup", function (e) {
dragging = false;
});
HTTP
1. 必考:HTTP 状态码知道哪些?分别什么意思?
1XX Informational (信息性状态码)
表示临时响应并需要请求者继续执行操作的状态码
-
100 Continue 继续
-
请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
-
101 Switching Protocols 切换协议
- 请求者已要求服务器切换协议,服务器已确认并准备切换
2XX Success(成功状态码)
表示成功处理了请求的状态码
-
200 OK 成功
- 客户端发送的请求在服务器端正常处理
GET 方法——对应请求资源的实体会作为响应返回
HEAD方法——在响应中只返回首部,不会返回实体的主体部分 -
201 Created 已创建
- 请求成功并且服务器创建了新的资源
-
202 Accepted 已接受
- 服务器已接受请求,但尚未处理或稍后处理
-
203 Non-Authoritative Information 非授权信息
- 服务器已经成功处理了请求,但返回的信息可能来自另一来源
-
204 No Content 无内容
- 服务器成功处理了请求,但没有返回任何内容
-
205 Reset Content 重置内容
- 服务器成功处理了请求,但没有返回任何内容
- 与204类似,表明客户端应重置数据源的视图或数据结构
-
206 Partial Content 部分内容
- 服务器成功处理了部分GET请求。常用于大型二进制文件的断点续传
3XX Redirection(重定向状态码)
需要进行附加操作以完成请求
-
300 Multiple Choices 多重选择
- 针对请求,服务器可执行多种操作。服务器可根据请求者(user agent)选择一项操作,或提供操作列表供请求者选择
-
301 Moved Permanently 永久移动
- 请求的网页已永久移动到新位置。服务器返回此响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置
-
302 Found 临时移动
- 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
-
303 See Other 查看其它位置
- 请求者应当对不同的位置使用单独的GET请求来检索响应时,服务器返回此代码
请求已经被处理,但服务器不是直接返回一个响应文档,而是返回一个响应文档的URI
-
304 Not Modified 未修改
- 自动上次请求后,请求的网页未修改过。服务器返回此响应,不会返回网页的内容
-
305 Use Proxy 使用代理
- 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理
-
307 Temporary Redirect 临时重定向
- 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有的位置来进行以后的请求
服务器尚未执行操作,客户端需要向Location报头里的那个URI重新提交整个请求
4XX Client Error(客户端错误状态码)
表示请求可能出错,妨碍了服务器的处理
-
400 Bad Request 错误请求
- 服务器不理解请求的语法
-
401 Unauthorized 未授权
- 请求要求身份验证。对于需要登录的网页,服务器可能返回此响应
-
403 Forbidden 禁止
- 服务器拒绝请求
-
404 Not Found 未找到
- 服务器找不到请求的网页
-
405 Method Not Allowd 方法禁用
- 禁用请求中指定的方法
客户端试图使用一个本资源不支持的HTTP方法
-
406 Not Acceptable 不接受
- 无法使用请求的内容特性响应请求的网页
-
407 Proxy Authentication Required需要代理身份验证
- 此状态码与401(未授权)类似,但指定请求者应当授权使用代理
-
408 Reqeust Timeout 请求超时
- 服务器等候请求时发生超时
-
409 Conflict 冲突
- 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息
5XX Server Error(服务器错误状态码)
表示服务器在尝试处理请求时发生内部错误
- 500 Internal Server Error 服务器内部错误
- 服务器遇到错误,无法完成请求
- 503 Service Unavailable 服务器不可用
- 服务器目前无法使用(由于超载或者停机维护)。通常只是暂时状态
2. 大公司必考:HTTP 缓存有哪几种?
HTTP 缓存分为 2 种,一种是强缓存,另一种是协商缓存
主要作用是可以加快资源获取速度,提升用户体验,减少网络传输,缓解服务端的压力
3. 必考:GET 和 POST 的区别
GET和POST本质上两者没有任何区别。都是HTTP协议中的请求方法底层实现都是基于TCP/IP协议。
所谓区别,只是浏览器厂家根据约定,做出的限制
- 用法方面
- GET一般用于请求数据,POST用于提交数据
- 参数方面
- GET参数通过URL传递,POST放在Request body中
- GET请求在URL中传送的参数是有长度限制的(2k),而POST没有
- GET请求参数只能是ASCII码而中文需要URL编码,而POST支持多种编码方式
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息
- 浏览器行为方面
- GET产生的URL地址可以被Bookmark,而POST不可以
- GET请求会被浏览器主动cache,而POST不会,除非手动设置
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
- GET在浏览器回退时是无害的,而POST会再次提交请求
- 数据包方面
- GET产生一个TCP数据包;POST产生两个TCP数据包
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
- 对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
- GET产生一个TCP数据包;POST产生两个TCP数据包
4. Cookie V.S. LocalStorage V.S. SessionStorage V.S. Session
https://juejin.im/post/5a191c47f265da43111fe859
https://www.bilibili.com/video/BV1ut411j7R7?from=search&seid=6592752127117987191
cookie | localStorage | sessionStorage | |
---|---|---|---|
共同点 | 键值对形式存储 同域名可用 |
同上 | 同上 |
存储位置 | 客户端 | 同上 | 同上 |
生命周期 | 可设置失效时间,默认关闭浏览器后失效 | 手动清除,否则将会永久保存 | 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除 |
数据大小 | 4KB | 5MB | 同上 |
http请求 | 随请求头提交 | 不参与和服务器的通信 | 不参与和服务器的通信 |
跨页 | 可跨页 不可跨域 |
同上 | 不可跨页 不可跨域 |
易用性 | 自行封装接口 | 易用接口 | 同上 |
Session
5.HTTP1和HTTP2的区别
http1.0特性
- 无状态:服务器不跟踪不记录请求过的状态
- 借助cookie/session机制来做身份认证和状态记录
- 无连接:浏览器每次请求都需要建立tcp连接
- 性能缺陷
- 无法复用连接:每次请求都需进行一次tcp连接(3次握手4次挥手),网络利用率低
- 队头阻塞:http1.0规定在前一个请求响应到达之后下一个请求才能发送,如果前一个阻塞,后面的请求也给阻塞的
- 性能缺陷
http1.1特性
-
长连接
- 新增Connection字段,可以设置keep-alive值保持连接
- http1.1默认保持长连接,数据传输完成保持tcp连接不断开
-
管道化
-
基于长连接,可以一次发送多个请求,响应仍按请求顺序返回
//非管道化 请求1 > 响应1 --> 请求2 > 响应2 --> 请求3 > 响应3 //管道化 请求1 --> 请求2 --> 请求3 > 响应1 --> 响应2 --> 响应3
-
-
缓存处理
- 当浏览器请求资源时,先看是否有缓存的资源,如果有缓存,直接取,不会再发请求,如果没有缓存,则发送请求
- 通过设置字段cache-control来控制
-
断点传输
http2.0特性
- 二进制分帧
- 将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码
- 多路复用
- 头部压缩
- 服务器推送
- 服务器可以额外的向客户端推送资源,而无需客户端明确的请求
6. 从输入URL到页面加载发生了什么?
-
地址栏输入URL并按下回车
-
DNS解析:将输入的URL找到对应的IP地址
- DNS缓存:浏览器
->
操作系统- 浏览器会在缓存中查找URL是否存在,并比较缓存是否过期
- win操作系统保存在
C:\Windows\System32\drivers\etc
- 分级查询
- 本地DNS服务器
->
根域名服务器->
COM顶级域名服务器
- 本地DNS服务器
- DNS缓存:浏览器
-
建立TCP连接(三次握手)
- 第一次握手:客户端发送
SYN=1
字段和客户端序列号seq=n
,并进入SYN_SENT状态,等待服务器确认; - 第二次握手: 服务器收到客户端的
SYN
字段,返回SYN=1
表示同意建立连接ack=n+1
用于确定收到客户端信息和服务端本身的序号seq=x
,此时服务器进入SYN_RECV状态; - 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包
ack=x+1
,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手
客户端 -> 服务端:SYN=1(请求进行连接) seq=n(序列号) 服务端 -> 客户端:SYN=1(同意建立连接) ack=n+1(确认收到信息) seq=x(服务端序列号) 客户端 -> 服务端:SYN=0(开始发送信息) ack=x+1(确认收到信息) seq=n+1
- 第一次握手:客户端发送
-
客户端发送HTTP请求
- tcp将
http请求报文
切割为报文段
,并在各个报文上打上标记序号以及端口号,将每个报文段可靠地传给网络层
- 协议在
网络层
通过ip地址找到mac地址(ARP协议
,解析地址,根据通信方的ip地址反查出对应的MAC地址),在各个路由中间进行路由中转
传送到数据链路层
- 服务器端在数据链路层收到数据,按数据链路层→网络层→传输层→应用层顺序逐层发送数据,期间,之前加在数据报上的报头信息被层层解析丢弃
- tcp将
-
服务端接收HTTP请求,并返回HTML响应报文
-
客户端解析响应数据,布局与渲染页面
-
断开连接(四次挥手)
7.http和https有什么区别?
HTTP协议本身明文传输,导致在传输过程中很容易被截取和篡改数据。因此HTTP协议不适合传输如支付密码等敏感信息。
HTTPS在HTTP的基础上加入SSL协议依赖证书验证服务器的身份,实现浏览器和服务端的加密通信
区别
- http是超文本传输协议,数据通过明文传输;https协议基于ssl加密传输的更加安全
- http速度更快,https传输需要验证证书
- 端口不同,http端口是80,https端口是443
- http连接是无状态的
8.三次握手和四次挥手
三次握手
- 第一次握手:客户端发送
SYN=1
字段和客户端序列号seq=n
,并进入SYN_SENT状态,等待服务器确认; - 第二次握手: 服务器收到客户端的
SYN
字段,返回SYN=1
表示同意建立连接ack=n+1
用于确定收到客户端信息和服务端本身的序号seq=x
,此时服务器进入SYN_RECV状态; - 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包
ack=x+1
,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手
客户端 -> 服务端:SYN=1(请求进行连接) seq=n(序列号)
服务端 -> 客户端:SYN=1(同意建立连接) ack=n+1(确认收到信息) seq=x(服务端序列号)
客户端 -> 服务端:SYN=0(开始发送信息) ack=x+1(确认收到信息) seq=n+1
四次挥手
- 第一次挥手:客户端发送
Fin=1
表示将要关闭连接,客户端进入FIN_WAIT_1
状态 - 第二次挥手:服务端发送确认应答报文
ack=n+1
和服务端序列号seq=x
,告知对方知道要关闭连接,但要等待数据处理完成。服务端进行close_wait
状态,客户端收到报文进入FIN_WAIT_2
状态 - 第三次挥手:当服务端处理完数据,发送
Fin=1
报文告知客户端已经处理完数据可以关闭连接,服务端进入LAST_ACK
状态 - 第四次挥手:客户端收到
FIn
报文,发送ACK
报文进入TIME_WAIT
状态
客户端 -> 服务端:Fin=1(关闭连接) ack=x+1(确定身份) seq=n(序列号)
服务端 -> 客户端:ack=n+1(确认收到信息) seq=x(服务端序列号)
服务端 -> 客户端:Fin=1(关闭连接) ack=n seq=x(服务端序列号)
客户端 -> 服务端: ack=x+1(确认收到信息) seq=n
框架 Vue
1. 必考:watch 和 computed 和 methods 区别是什么?
methods
-
作用
- 在Vue中定义函数方法,可以对DOM发生的事件作出反应
-
使用
- 使用
v-on
指令绑定事件处理函数
<button @click="handleSubmit">Submit</button>
methods: { handleSubmit() {} }
- 使用
computed(计算属性)
-
作用
- 在Vue中替代具有复杂逻辑的模板表达式
-
使用
- get方法返回计算后的值, 计算属性被赋值时调用set方法
<p>name: {{ fullName }}</p>
// ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ...
-
特点
- 支持缓存
- methods可以实现computed相同功能,但是不支持缓存
- 仅当依赖数据发送改变时才会重新计算;
- 页面重新渲染时,依赖数据未改变不会重新计算
- 不支持异步
- 内部的异步操作无效,无法监听数据的变化
- 支持缓存
-
使用场景
- 一个数据受多个数据影响
- 简化tempalte里面{{}}计算和处理props或$emit的传值
watch(侦听属性)
- 作用
- 在Vue中数据变化时处理执行异步或开销较大的操作时
- 特点
- 不支持缓存
- 页面重新渲染时值不变化也会执行
- 直接监测一个值的变化情况,不会像computed检测其中的依赖
- 支持异步操作
- 参数
- watch里面的函数接收两个值。分别为变化前和变化后的值
- 不支持缓存
- 使用场景
- 数据变化响应时,执行异步操作,或高性能消耗的操作
- 一个数据影响多个
- 监听props,$emit或本组件的值执行异步操作
2.v-for渲染列表中 key的作用
主要是为了高效的更新虚拟DOM, 添加key作为唯一标识能够让Diff算法正确识别节点并且插入到正确位置
渲染项目列表时,key
属性允许 Vue 跟踪每个 Vnode。key 值必须是唯一的
-
vue中默认使用“就地更新”的策略,不同的
key
属性的元素将不会被复用 -
v-for中使用
key
与虚拟dom和diff算法有关,每个节点带有唯一标识,Diff算法正确识别此节点
3. 必考:Vue 有哪些生命周期钩子函数?分别有什么用?
4. 必考:Vue 如何实现组件间通信?
5. 必考:Vue 数据响应式怎么做到的?
6. 必考:Vue.set 是做什么用的?
Vue 不能检测到对象属性的添加或删除,解决方法是手动调用 Vue.set 或者 this.$set
7. Vuex 你怎么用的?
8. VueRouter 你怎么用的?
9. 路由守卫是什么?
10.keep-alive的用处
框架 React
- 必考:受控组件 V.S. 非受控组件
- 必考:React 有哪些生命周期函数?分别有什么用?(Ajax 请求放在哪个阶段?)
- 必考:React 如何实现组件间通信?
- 必考:shouldComponentUpdate 有什么用?
- 必考:虚拟 DOM 是什么?
- 必考:什么是高阶组件?
- React diff 的原理是什么?
- 必考 Redux 是什么?
- connect 的原理是什么?
TypeScript
- never 类型是什么?
- TypeScript 比起 JavaScript 有什么优点?
Webpack
必考:有哪些常见 loader 和 plugin,你用过哪些?
英语题:loader 和 plugin 的区别是什么?
必考:如何按需加载代码?
必考:如何提高构建速度?
转义出的文件过大怎么办?
安全
- 必考:什么是 XSS?如何预防?
- 必考:什么是 CSRF?如何预防?
开放题目
- 必考:你遇到最难的问题是怎样的?
- 你在团队的突出贡献是什么?
- 最近在关注什么新技术
- 有没有看什么源码,看了后有什么记忆深刻的地方,有什么收获
***钻题目
- 代码
- 代码
(a ==1 && a== 2 && a==3)
可能为 true 吗?
超纲题
-
JS 垃圾回收机制
-
Eventloop 说一下
个性化题目
- PWA
- echarts.js / d3.js
- three.js
- flutter
- SSR