前端面试中遇到得问题
1.盒子模型有几部分组成?
主要组成:内容(content)、内边距(padding)、边框(border)、外边距(margin);
盒子模型分为:标准盒子模型(W3C盒子模型)、怪异盒子模型(IE盒子模型)
标准盒子模型:content只包括自身的宽度和高度(box-sizing:content-box)。
怪异盒子模型:content除了包含自身的width和height还包括border和padding(box-sizing:border-box)。
2.js创建,添加,查找节点
var div = document.getElementsByTagName("div"); //创建元素 var p = document.createElement("p"); var a = document.createElement("a"); var input = document.createElement("input"); //为创建元素添加属性和内容 var node = document.createTextNode("这是创建的标签"); a.href = "http://www.baidu.com"; a.innerHTML = "这是一个创建的a标签"; input.value = "这是一个input"; input.setAttribute("type","text"); //添加创建的元素 p.appendChild(node); div.appendChild(p); //查找元素 //1.根据gelElement来找 document.getElementById(); document.getElementsByClassName(); //根据class名字找,找出来的是类数组 document.getElementsByName(); //根据name属性名找,找出来的是类数组 document.getElementsByTagName(); //根据标签名字找,找出来的是类数组 //2.根据node节点来找 div.parentNode; div.childNodes; div.firstChild; div.lastChild; div.nextSibling; div.previousSibling;
3.常见的行内元素和块级元素各列举5个
行内元素:span、a、b、i、u、em、label、input、textarea
块级元素:div、p、h1-h6、ui、ol、li、dt、dd
4.js的数据类型有几种?
JavaScript有六大数据类型,其中包括五大基本数据类型(number、string、Boolean、null、undefined),一种引用数据类型(object),包含function、array、date
5.判断一个对象是不是数组?
- typeof检测对象、数组、null都会返回object,并不能很好的区分出来;
- a instanceof Array
- a.constructor == Array
- Array.isArray(a)
- Object.prototype.toString.call(a) ===‘[object Array]’
6.元素不定宽高居中
/* 1.利用transform */ .parent{ position: relative; } .son{ position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); } /* 2.仿照table居中 */ .parent{ display: table-cell; text-align: center; vertical-align: middle; } .son{ display: inline-block; } /* 3.利用弹性布局 */ .parent{ display: flex; justify-content: center; align-items: center; }
7.数组去重问题
一维数组去重 https://www.cnblogs.com/leonwang/p/4845576.html
1).新建一个数组,利用indexof判断是否已存在,每次向里面push值
function removeDuplicatedItem(ar) { var ret = []; for (var i = 0, j = ar.length; i < j; i++) { if (ret.indexOf(ar[i]) === -1) { ret.push(ar[i]); } } return ret; }
2).利用object,判断数组值是否已保存在object中
function removeDuplicatedItem2(ar) { var tmp = {}, ret = []; for (var i = 0, j = ar.length; i < j; i++) { if (!tmp[ar[i]]) { tmp[ar[i]] = 1; ret.push(ar[i]); } } return ret; }
3).数组先排序, 然后比较俩数组一头一尾进行去重
function removeDuplicatedItem4(ar) { var ret = [], end; ar.sort(); end = ar[0]; ret.push(ar[0]); for (var i = 1; i < ar.length; i++) { if (ar[i] != end) { ret.push(ar[i]); end = ar[i]; } } return ret; }
4).ES6的方法
Array.from(new Set([1,2,3,4,4,4,5,6,7,7,7,8,9,9,9,9,0,0,0,12])); //或者使用扩展运算符 var arr=[1,2,3,4,4,4,5,6,7,7,7,8,9,9,9,9,0,0,0,12]; var arr2=[...new Set(arr)];
二维数组去重
//如果存在"10",10会判断为一样 var arr = [["aa","bb","cc"],["aa","bb","cc"],["b","b","v"]]; var hash = {}; var result = []; for(var i = 0, len = arr.length; i < len; i++){ if(!hash[arr[i]]){ result.push(arr[i]); hash[arr[i]] = true; } } console.log(result)
8.判断一个字符串中出现最多的字符及出现的次数 (直接放人家的链接了,懒得写了😊:https://www.cnblogs.com/john69-/p/5555025.html)
var str = 'qwertyuilo.,mnbvcsarrrrrrrrtyuiop;l,mhgfdqrtyuio;.cvxsrtyiuo'; var json = {}; //遍历str拆解其中的每一个字符将其某个字符的值及出现的个数拿出来作为json的kv for (var i = 0; i < str.length; i++) { //判断json中是否有当前str的值 if (!json[str.charAt(i)]) { //如果不存在 就将当前值添加到json中去 json[str.charAt(i)] = 1; } else { //else的话就让数组中已有的当前值的index值++; json[str.charAt(i)]++; } } //存储出现次数最多的值和次数 var number = ''; var num=0; //遍历json 使用打擂算法统计需要的值 for (var i in json) { //如果当前项大于下一项 if (json[i]>num) { //就让当前值更改为出现最多次数的值 num = json[i]; number = i; } } //最终打印出现最多的值以及出现的次数 alert('出现最多的值是'+number+'出现次数为'+num);
9.vue的双向数据绑定原理 具体原理请参阅:http://www.cnblogs.com/canfoo/p/6891868.html
VUE实现双向数据绑定的原理就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的
10.vue父子组件传值问题
<-- 父组件 --> <carTypeDialog :open="carType_open" :pData="p_data" @close="toggleCarTypeDialog"></carTypeDialog> <script> //引入 import carTypeDialog from './selectors/car-type-dialog' export default { components:{ carTypeDialog //注册 }, methods:{ toggleCarTypeDialog(){ //自定义方法 } } } </script>
export default { //使用props接受父组件传过来的值 props:{ open:'', pData: '' }, methods:{ close() { this.$emit('close'); //调用父组件@close中的方法,可以有第二个参数,进行传值 }, sonClick(e) { console.log(e) } } }
父组件调用子组件的方法:this.$refs.mychild.sonClick("子组件的方法");
11.路由嵌套 使用children
routes: [ { path: '/login', component: Login }, { path: '/', component: Index, children: [ { path: '/welcomeIn', name: 'welcomeIn', component: WelcomeIn }, { path: '/waybillManage', name: 'waybillManage', component: WaybillManage }, { path: '/404', component: NotFound } ] }, { path: '*', redirect: { path: '/404' } } ]
12.生命周期函数
13.箭头函数和普通函数的区别 https://www.cnblogs.com/biubiuxixiya/p/8610594.html
- 箭头函数相当于匿名函数,简化了函数定义。当箭头函数只有一行表达式时,可以省略return。当含有多条语句时,不能省略return
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数没有arguments,只有rest参数
- 箭头函数不绑定this,会捕获上下文的this,作为自己的this值
- 箭头函数通过call()和apply()调用一个函数时,只传入一个参数,对this没有影响
- 箭头函数没有原型属性
14.浏览器渲染页面过程(当用户在地址栏输入url点击回车后)
https://mp.weixin.qq.com/s/X7DhDcb4F7ojpPGj79JrMQ
- 对URL地址进行域名解析:将域名解析为IP地址
- 建立TCP连接(三次握手)
- 发送HTTP请求
- 服务器处理请求,并返回HTTP报文
- 浏览器解析渲染页面
- 断开连接(TCP四次握手)
TCP三次握手:
三次握手的目的是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
-
- 客户端发送一个带SYN=1 ,Seq=X的数据包到服务器端口(第一次握手由浏览器发起,告诉服务器,我要发送请求了)
- 服务端发回一个带SYN=1 ,ACK =X+1 ,Seq=Y的响应包以示传达确认信息(第二次握手由服务器发起,告诉浏览器,我准备接收请求了)
- 客户端再发送一个带ACK =Y+1 ,Seq=Z的数据包,表示握手结束(第三次握手由浏览器发起,告诉浏览器我发上就发了,你准备接收吧)
TCP四次握手断开连接
-
- 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FINWAIT1 状态。(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
- 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FINWAIT2 状态。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
- 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
- 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)
15.重排和重绘
浏览器的渲染机制如下:
重绘:不重新布局,只是元素的外观发生改变。重绘不一定重排。一般是元素外观的改变会触发重绘。例如:visibility,outline,background等
重排:页面会重新布局。重排一定会发生重绘。
会触发重排的情况:
-
- 页面初始化
- 添加或删除可见的DOM元素
- 改变元素的位置
- 改变元素尺寸(宽、高、内外边距、边框)
- 改变元素内容(一个文本被换成另一个不同尺寸的图片)
- 改变窗口尺寸
避免重排的方法:
-
- 如果多次用js改变样式,可以使用class名进行一次操作
- 可以使用定位来使元素脱离文档流,不会影响到其他元素
- 尽量使用dispaly:none;对隐藏元素操作,不会影响其他元素的重排。不使用visibility,也尽量不要使用z-index
16.transform和定位的区别 http://www.w3school.com.cn/cssref/pr_transform.asp
transform属性允许我们对元素进行平移,缩放,旋转和倾斜。一般以当前的元素位置的左上角,左边,上面为起始点进行变换。
transform支持的方法:
-
- translate(平移)
- scale(缩放)
- rotate(旋转)
- skew(倾斜)
定位分为:
- 静态定位(static):默认值,没有定位。
- 固定定位(fixed):相当于浏览器窗口进行定位。
- 相对定位(relative):相对于自身进行定位。
- 绝对定位(absolute):脱离文档流,相对于第一个非静态定位的父元素进行定位。
17. 2个空数组是否相等
当然是不相等的
那么为什么不相等呢?
我们都知道JavaScript除了五大基本数据类型(number、string、Boolean、undefined、null)之外,还有一个引用数据类型(object)。object里面又包含function、array、object。那么引用数据类型之间的比较就跟基本数据之间的比较就不太一样了。
引用数据类型之间的比较是比较的引用地址。即使两个对象有相同的属性和相同的值,它们之间也是不相等的。只有当两个对象的引用地址相等时,它们之间才是相等的。
顺带把其他的几个类型也试了一下
18.vue监听路由的变化
https://www.cnblogs.com/crazycode2/p/8727410.html
watch:{ //监听路由变化 $route( to , from ){ console.log( to , from ) // to , from 分别表示从哪跳转到哪,都是一个对象 // to.path ( 表示的是要跳转到的路由的地址 eg: /home ); } }
19.路由跳转传参
通过router-link进行跳转
通过编程式导航就行跳转
<router-link :to="{ path: 'yourPath', params: { name: 'name', dataObj: data }, query: { name: 'name', dataObj: data } }"> </router-link> 1. path -> 是要跳转的路由路径,也可以是路由文件里面配置的 name 值,两者都可以进行路由导航 2. params -> 是要传送的参数,参数可以直接key:value形式传递 3. query -> 是通过 url 来传递参数的同样是key:value形式传递
//组件 a <template> <button @click="sendParams">传递</button> </template> <script> export default { name: '', data () { return { msg: 'test message' } }, methods: { sendParams () { this.$router.push({ path: 'yourPath', name: '要跳转的路径的 name,在 router 文件夹下的 index.js 文件内找', params: { name: 'name', dataObj: this.msg } /*query: { name: 'name', dataObj: this.msg }*/ }) } }, computed: { }, mounted () { } } </script> <style scoped></style> ---------------------------------------- // 组件b <template> <h3>msg</h3> </template> <script> export default { name: '', data () { return { msg: '' } }, methods: { getParams () { // 取到路由带过来的参数 let routerParams = this.$route.params.dataobj // 将数据放在当前组件的数据内 this.msg = routerParams } }, watch: { // 监测路由变化,只要变化了就调用获取路由参数方法将数据存储本组件即可 '$route': 'getParams' } } </script>
20.路由导航
https://www.cnblogs.com/hyl1991/archive/2019/03/27/10609193.html
路由守卫:路由拦截,在跳转之前,之后,跳转的瞬间干的事情
全局守卫:
就是全局的,整个项目所有路由,跳转所用到的守卫(拦截),设置了全局守卫之后,只要路由(浏览器地址栏)发生变化就会触发的事件
全局守卫分为二部分 前置守卫(跳转之前) 后置钩子(跳转之后)
前置守卫:
router.beforeEach
这个方法有三个参数
(to, from, next)
to:即将进入的地址,比如说 点击按钮 从 A 跳转到 B ,那么to就是 B 的路由对象,
from:要离开的地址,比如说 点击按钮 从 A 跳转到 B ,那么to就是 A 的路由对象,
next:就是在跳转的时候要执行的事件,比如说 点击按钮 从 A 跳转到 B ,然后我在next执行了一个方法 next({ path: ‘/C’ }) 这样就会跳转到C 页面,而不是 B 页面了,这就是路由拦截了,如果这么写的话 不管你愿来是想从 那个页面 跳转 那个页面 他都会给你跳转到 C 页面
next():进入管道中的下一个钩子,如果全部的钩子执行完了,则导航的状态就是 confirmed(确认的)
next(false):这代表中断掉当前的导航,即 to 代表的路由对象不会进入,被中断,此时该表 URL 地址会被重置到 from 路由对应的地址
next(‘/’) 和 next({path: ‘/’}):在中断掉当前导航的同时,跳转到一个不同的地址
next(error):如果传入参数是一个 Error 实例,那么导航被终止的同时会将错误传递给 router.onError() 注册过的回调
next 方法必须要调用,否则钩子函数无法 resolved
在这里就可以判断,如果满足一定的条件 就让他 next({ path: ‘/C’ }) 就是满足一定的条件 才让他跳转到C 不满足的时候 就正常跳转
这里next 还有一个作用 next(false) 这样会中断路由的跳转 比如说 点击按钮 从 A 跳转到 B 然后我执行了 next(false) 那么浏览器就不会进行跳转 从新回到A页面 这样就阻止了路由的跳转 在这里就可以判断,如果满足一定的条件 就让他 next(false) 这样就不进行跳转了
不同于前置守卫,后置钩子并没有 next 函数,也不会改变导航本身
路由独享守卫
顾名思义:就是这个守卫,只是单独的这个组件独享的,局部的,不是全局的,只有这个路由在进行跳转的时候,才会触发的,其他的组件,路由进行跳转的时候不执行这个方法
独享守卫有三个方法:
beforeRouteEnter 在渲染该组件的对应路由被 confirm 前调用 就是页面跳转前要执行的方法 要干的事
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用 就是当页面 在A 跳转到 B 的一瞬间 要干的事
beforeRouteLeave 导航离开该组件的对应路由时调用 就是在跳转完成之后 要干的事
这三个方法 都有三个参数 (to, from, next) 跟全局守卫的 三个参数用法一样
其中 beforeRouteEnter 守卫 不能 访问 this 其他两个守卫可以访问到this
21.vuex状态管理