前端八股文——笔试题
记录一下面试题及答案~啥都有把,大杂烩~一、flex布局手写题目代码结构如下,请实现以下效果(忽略样式只写布局).box{//重点1//重点2}.item{}//重点3}//重点4}主要考察flex熟练程度,align-self表示自身在交叉轴(主轴为横着的,交叉轴就为竖着的)的位置,有三个值:flex-start、center、flex-end。参考:阮一峰flex布局发出npm install
目录
二、移动端点击事件为什么有延迟?时间多久?如何解决这个问题?
3.设置touch-action:manipulation。
六、介绍npm模块安装机制,为什么输入npm就能自动安装对应模块?
7.$attrs / $listeners(父子、跨级传值)
前言
记录一下面试题及答案~啥都有把,大杂烩~
一、flex布局手写题目
代码结构如下,请实现以下效果(忽略样式只写布局)
<div class="box">
<span class="item"></span>
<span class="item"></span>
<span class="item"></span>
</div>
解答:
.box{
width: 100px;
height:100px;
background:lightgray;
display: flex; //重点1
justify-content: space-between; //重点2
}
.item{
width: 20px;
height: 20px;
background:deeppink;
border-radius: 50%;
}
//重点3
.item:nth-child(2){
align-self: center;
}
//重点4
.item:nth-child(3){
align-self: flex-end;
}
主要考察flex熟练程度,align-self表示自身在交叉轴(主轴为横着的,交叉轴就为竖着的)的位置,有三个值:flex-start、center、flex-end。
二、移动端点击事件为什么有延迟?时间多久?如何解决这个问题?
移动端的设计之初为了判断用户是否还有其他操作(主要为了区分双击和单击,双击缩放),延迟时长为3000ms。
解决方案(四种):
1.meta标签里面content属性,设置禁止缩放。
//方式一:设置用户不可缩放
<meta name="viewport" content="user-scalable=no">
//方式二:初始化比例为1
<meta name="viewport" content="initial-scale=1,maximum-scale=1">
2.设置默认宽度为浏览器宽度。
<meta name="viewport" content="width=device-width">
3.设置touch-action:manipulation。
设置css样式,touch-action有多个可选值,none表示禁止一切事件,会引起左右滑动不行;
manipulation表示禁止双击操作,这可以完美解决300ms延迟得问题。
注意:除ie之外得浏览器可能无这个属性。
html{
touch-action:manipulation
}
4.fastClick
在window.onload事件得时候引用attach方法。
原理是在检测到touch事件得时候,通过dom模拟一个click事件阻止浏览器默认300ms
window.addEventListener( "load", function() { FastClick.attach( document.body );}, false );
三、简述sessionStorage、localStorage、cookies区别?
数据都存储在客户端
1.存储量方面。cookies存储量为5k,seesionStorage和localStorage存储了为3M
2.有效期方面。cookies设置了时长,过期后数据清除。sessionStorage关闭标签后失效。localStorage不主动清除,永久存在客户端
3.作用域方面。cookies、localStorage同源页面可以共享数据。seesionStorage不可共享。
4.请求方面。cookies会将数据放在http请求里面,sessionStorage和localStorage不会。
5.seesionStorage、localStorage支持事件通知机制,可以将更新得数据通知给监听折,且他们api接口使用更方便。
四、浏览器会缓存哪些内容?如何解决浏览器缓存问题?
1.浏览器会缓存哪些内容?如何解决浏览器缓存问题?
浏览器会缓存图片视频及数据资源。
浏览器缓存针对相同url,解决方法是在url后面加上时间戳
2.浏览器缓存策略
浏览器缓存图片及数据至内存中,缓存分两种强缓存、协商缓存。
2.1 强缓存
浏览器第一次加载得时候会将图片、数据缓存,当再次访问相同网站时直接获取缓存中的资源,不需要重新发起请求。
主要是在加载资源的时候验证时间是否过期,验证时间的两种方式:设置header中得expries(HTTP/1.0)、cache-control(HTTP/1.1)。
expries
客户端第一次发起请求时,服务端返回一个expries,再次发送请求时,客户端携带这个exprise发起,对该expeise进行验证,时间未过期则使用缓存。否则再次发送请求
缺点:但是这个方式浏览器和服务端和时间可能会不一致,造成偏差,客户端也可以单方面修改exprise造成失效。为了解决这个问题提出了cache-contorol
cache-control
有多个至,常用的有以下几个:
no-cache:表示使用协商缓存
max-age:表示缓存多少秒
public:公有缓存,可被代理服务器共享,可被多个用户共享
private:私有缓存,不可被代理服务器共享,不可被用户共享。
注意:cache-control中的max-age优先级高于expries
2.2协商缓存
每次都要重新发起请求,判断是否使用本地缓存。
协商缓存没有缓存时间,而且生成一个标识,服务端用这个表示去验证是否更新,如果不需要更新即表示可以用本地缓存就会返回304,否则就会返回200并且返回新的资源和新的标识。
资源标识有两种:last-Modefied、Etag
last-Modified
第一次发起请求的时候,服务端会返回一个last-Modified,再次请求时,将last-Modified赋值给if-modified-since参数发起,服务端对比返回304使用缓存,或者返回200和新的资源及新的last-modified
缺点:只能精确到秒,如果一秒内发生了变化,是检测不到的。如果改变后又恢复,此时还是会发送请求。有些服务端不能及时最新的修改信息。所以为了解决这三个问题,出现了Etag
Etag
为了更加的精确,我们采用哈希值作为标识,以解决last-modefied的缺点。
第一次请求,服务端返回一个Etag,第二次发起请求的时候,我们将etag赋值给if-none-match去发起请求,如果返回304则表示资源没有更新,使用缓存,如果不相同则返回200和新的资源及新的etag
缺点:这个方法也有缺点,就是每个文件生成哈希值,对于大文件来说,开销很大。不同服务器间的哈希值不一样,所以对于服务器群处理请求来说缓存命中率会降低。
注意:Etag优先级高于last-Modified
2.3用户操作方面
用户回车、前进后退、跳转、打开新窗口强缓存和协商缓存都有效。
刷新时候,强缓存会无效,协商缓存有效。
强制刷新时,强缓存和协商缓存都无效。
2.4如何解决缓存问题
服务端禁用缓存:
Cache-Control: max-age=0, must-revalidate
Cache-Control: no-cache
Cache-Control: no-store
浏览器禁用缓存:
在url后面加个时间戳,如:https://192.168.33:443/xxx
五、浏览器从url输入到页面完全展示都经历了什么?
1.浏览器解析url
2.解析DNS域名
3.浏览器发起请求
4.服务端处理请求
5.浏览器根据返回资源渲染页面
六、介绍npm模块安装机制,为什么输入npm就能自动安装对应模块?
1.npm安装机制
- 发出npm install 安装命令
- 检查node_modules是否有该模块
- 有。不再安装
- 无。首先,npm向register查询模块压缩地址。其次,将压缩包下载并存放在根目录下的.npm(缓存)文件中。最后,将模块 解压至node_moudles文件夹。
2.为什么输入npm就能自动安装对应模块?
当我们输入npm install 命令的时候,会经历7个阶段,分别是:执行工程自身preinstall、确定首层依赖模块、获取模块、模块扁平化(dedupe)、执行模块生命周期、执行工程生命周期、更新package-lock.json。
2.1 执行工程自身preinstall
当npm工程定义定义了preinstall的时候就会执行
2.2 确定首层依赖模块
这里先说一下,npm会生成两个文件:package.json和package-lock.json。
package.json包含项目基本信息、以及第三方依赖等
package-lock.json包含具体版本信息
这一部分可以看这篇:详解package.json和package-lock.json
首层依赖模块就是package.json里面的devDependencies(开发依赖)和dependencies(生产依赖)中指定的模块。
我们获取的流程如下:
工程(根节点)> 首层依赖模块(子节点)> 更深层次的节点
2.3 获取模块
获取模块分三步:获取模块信息、获取模块实体、查找该模块依赖
第一步:查看package.json中的版本信息,如果和package-lock.json中的一致就使用package-lock.json的文件(版本不一致就取兼容版本)。如果package-lock.json中没有就从仓库中获取对应的版本。
第二步:上一步会获取到压缩包地址,npm此时查看本地是否有此缓存,有就用缓存,没有就去仓库下载。
第三步:此时再查找是否有该模块,如果有依赖就执行1,没有就停止。
2.4模块扁平化(dedupe)
以上三步生成了完整的依赖树,但我们很可能存在多个不同版本的依赖,我们为了优化他,需要执行dedupe的过程。
他会遍历完所有节点,将依赖放在node_modules,当有重复依赖的时候,如果有兼容版本就是使用兼容版本。如果不能兼容,则后面一个版本留在依赖树中。
2.5安装模块
到这里会在更新一次node_modules,并执行模块生命周期(preinstall、install、postInstall)
2.6工程自身生命周期
模块更新完了,工程也需要更新。
如果npm定义了钩子,此时就会执行(install、postinstall、prepublish、prepare)
2.7.生成版本描述文件
一切完成,生成或更新package-lock.json文件
七、写一个正则精确判断邮箱?
略
八、为什么会有AJAX跨域?如何解决?
跨域的原因就是浏览器的同源策略,协议端口域名不同我们称之为跨域。
跨域的解决方式有以下几种:
1.jsonp
利用标签属性不受同源策略约束解决跨域,缺点是只能发送get请求,容易收到xss攻击,不安全。
2.cors
属于后端解决跨域的方法,服务端设置Access-Control-Allow-Origin,可再响应请求中看到
3.vue代理devserver
vue中可以设置devserver中的地址
4.nginx反向代理
在nginx.conf配置监听域名、端口以及实际要访问的地址
还可以使用node解决
5.websokect
建立双向通行协议,就可以访问啦
九、如何取消AJAX跨域?
我们知道跨域是浏览器机制限制,如何取消肯定是从浏览器上入手。
我们右击浏览器图标,重新设置目标参数就可以啦
chrome 浏览器
–args --disable-web-security --user-data-dir=D:\chrome
safari 浏览器
open -a ‘/Applications/Safari.app’ --args --disable-web-security --user-data-dir
十、vue中哪些情况会导致数据更新页面不更新?
1.data未定义变量
vue初始化的时候会对data中定义的数据setter/getter,此时才会监听到数据变化。
解决方案:data中定义变量
2.通过索引修改数组的项
如:this.array[2] = "新的值"
由于js的限制,不能直接修改数组数据。性能代价和用户体验不成正比。
解决方案:
使用vue官方推荐$set进行设置,this.$set(array,index,value)。
使用array.prototype.splice设置,array.prototype.splice(index,1,value)
3.修改数据长度
如:this.array.length=10
由于js的限制,不能直接修改数组数据。性能代价和用户体验不成正比。
解决方案:使用splice改变,array.splice(newLength)
4.对象的添加或移除
如: object.name = "添加的值"
delete object.id
由于vue2是object.defineprototype(),检测不到对象属性的添加或移除。
解决方案:
1.添加。使用$set(obj,name,value);使用object.assign(obj,obj1,obj2)添加多个
2.删除。this.$delete(obj,name)
4.异步更新之前操作dom
vue是异步更新,当数据变化时,vue开启队列并在同一事件循环中发生数据变更。如果同一watcher被多次触发,则会放在同一队列中(此时已去重)。
解决方案:使用this.$nextTick(),表示dom更新之后立即被调用。
5.动态路由共用router-view
当我们配置了动态路由,如:/index/:id,同时多个组件公用一个页面,数据只会渲染第一次路由匹配的参数数据。
解决方案:
1.使用watch监听$router变量。
2.为每个组件赋一个key(不建议,不同路由直接也有key)
6.keep-alive
keep-alive可以记录两个路由之间的状态,数据会缓存下来,切换回来时,也会先获取缓存内容,适用于不需要及时更新的数据页面。
解决方案:
1.keep-alive中created失效,取而代之的是activited中重新获取数据
2.window.location.reload()重新请求
3.动态绑定include的值,需要时缓存,不需要时清除缓存
4.使用组件导航守卫beforeRouterEnter的next( 不能访问this,只能用next来访问啦)里面获 取最新数据并赋值刷新
7.深层嵌套
层次太多导致的问题。
解决方案:使用this.$forceUpdate()强制更新,不太建议用。
十一、组件传值方式?
1.父传子 prop
<comp-a :prop="msg" />
2.子传父 $emit
<button @click="submitForm" />
submitForm(){
this.$emit("change",msg)
}
3.$parent /$children(父子之间传值)
父组件:this.$children[0].msg="msg"
子组件:computed:{
value(){
this.$parent.msg
}
}
4. $ref /$refs(父子传值)
给子组件设置ref属性 ,用$refs获取子组件实例(包括变量、方法等)
<comp-a ref="child"/>
获取:this.$refs.child
5.vuex / evenBus(兄弟、跨级传值 )
const evenbus= new Vue()
触发:evenbus.$emit("change",msg)
接收:evenbus.$on("change",value=>{
console.log(value,"接收到得值")
})
6.provide/inject(父子、跨级传值)
可以获取嵌套层级很深得数据
父:provide:{"for":value}
子:inject:["for"] ,通过this.for获取变量
孙子:inject:["for"] ,通过this.for获取变量
...获取得方式都一样,使用inject注入后通过this.for调用
7.$attrs / $listeners(父子、跨级传值)
在组件上设置属性,通过调用$attar可获取这个属性对象
父:
<comp-a width="200" height="500" :name="name">
data(){
retrun{
name:"小明"
}
}
子:
<comp-b v-bind="$attrs">
设置inheritAttars:false 挂仔除了props中的所有属性
获取:this.$attrs
结果为:{width:"200" , height:"500" ,name:"小明"}
孙子:
<div > $attrs</div>
inheritAttars:false
props:{
name:string
}
获取:this.$attrs
结果为:{width:"200" , height:"500"}
十二、数字精度问题?
导致数字精度问题原因有很多,由于计算机二进制所以对于浮点数加减来说,结果并不是一个整数;也有可能是整数长度超过js长度Math.pow(2,52),数值长度超过16位,此时也会出现精度问题;还有可能是使用了tofixed()喝浏览器不兼容导致得。
不管是那种情况,我们都需要解决这个问题,解决方法有一下几种:
1.将小数位变为整数再除倍数
let num = 0.1+0.2;
console.log(num)
结果:可以看到这种情况,就是因为二进制得缘故
解决:
let num =( 0.1*10 +0.2*10)/10;
console.log(num);
结果:
2.使用插件Decimal或bignumber
十三、es6熟悉哪些特性?
略
十四、扩展运算符复制一个数组,是深拷贝还是浅拷贝?
分两种情况,数组中如果只是基本数据,则可以理解位深拷贝,若有对象或数组则是浅拷贝。看个栗子:
第一种情况:
let obj=['1','2']
let cc=[ ...obj]
cc[0] = '你好'
console.log(obj,cc)
结果:
可以看到cc变化了,但obj没有变化,此时为深拷贝。
看第二种情况:
let obj=['1',['2','3']]
let cc=[ ...obj]
cc[1] = '你好'
console.log(obj,cc)
结果:
此时因为数组得值也是数组,所以是深拷贝。
第三种情况:
let obj=['1',{a:'1'}]
let cc=[ ...obj]
cc[1].a = '2'
console.log(obj,cc)
结果:
此时,数组里面得值是对象,此时为浅拷贝
综上所诉,扩展运算符复制对象或数组得时候如果值是基本数据类型,就是深拷贝;如果是数组或象引用类型,就是浅拷贝。
这里说一句我们经常也用object.assign(),这也是一个浅拷贝。
1.如何实现深拷贝?缺陷?
目前有三种方式:
1.对象序列化
使用json.parse()和json.stringify()
缺点:会忽略null undefined symbol得数据 ,不能深拷贝对象中得方法
2.函数库lodash
3.递归遍历
遍历判断该项是否为数组或对象,是则递归,不是则置入。
十五、const定义的数据可以更改值吗?
const定义得基本数据是不可以更改得,但是如果是数组或对象,我们可以更改项得值。如下:
const bb=['1',"2"]
bb[2]="你好"
console.log(bb)
十六、css哪些熟悉可以继承
文字属性:font=family、font-size、font-weigh
文本属性:text-indent、text-align、line-height、direction(文本方向)
其他属性:color、visibility、cursor(光标)
十七、重排和重绘?
重排:当页面结构(dom)发生了变化,需要重新渲染页面结构
重绘:当页面样式发生变化,需要重新渲染样式
十八、代码题目
第一题:输出题
var a=[];
for(var i=0;i<10;i++){
console.log(i,"1")
a[i]=function () {
console.log(i)
}
}
a[6]()
输出:10
解答:for循环为数组a的每一个数值都添加一个函数,i是var定义的全局变量,所以i是共享的,调用a[6]()的时候,全局函数i以及变成了10,所以输出结果是10。
第二题:输出题
var b =100;
function foo() {
console.log(b)
}
function bar(fn) {
var b =200;
fn()
}
bar(foo)
输出:100
解答:调用bar(foo),函数bar里面调用foo函数,此时foo()作用域里面没有变量b,向上级作用域查找,查找到b=100 ,所以输出100.
第三题:手写字符串倒序
//将"a,b,c,d,e,f" 变成 "f,e,d,c,b,a"
var str = "a,b,c,d,e,f";
var newStr=str.split(',').reverse().join(',');
console.log(newStr)
第四题:手写数组去重
// 去重数组arr
var arr =[1,2,2,3,3,3,4,8,6]
//1
console.log([...new Set(arr)])
var newArr =[]
// 2 includes
for(var i=0;i<arr.length;i++){
if( ! newArr.includes(arr[i]) ){
newArr.push(arr[i])
}
}
// 3 indexof
arr.filter((i,index)=>{
if(newArr.indexOf(i)==-1){
newArr.push(i)
}
})
// 4 reduce
arr.reduce((result,item)=>{
newArr.includes(item)?newArr:newArr.push(item)
return
},0)
console.log(newArr)
第五题:手写递归
// 0 1 1 2 3 5 8 13 ...N 求N
var per ='0 1 1 2 3 5 8 13 '
function di(n) {
var f =[]
var result;
for (var i=0;i<n;++i) {
console.log(i)
f.push((i<2)?i:f[i-1]+f[i-2])
}
result =f.slice(-1).toString()
console.log(result,"结果")
// console.log(f.slice(-1),f.pop(),"sss")
}
di(9)
总结
以上就是今天要讲的内容,本文仅仅简单记录了一下面试,简单写了一下答案,希望对你有帮助吧。
更多推荐
所有评论(0)