记录一下最近问的面试题

186 阅读12分钟

1.盒子模型

分为普通盒子模型和怪异盒子模型,区别在于定义盒子的宽高的不同
普通盒子模型:box-sizing:content-box
标签的实际宽度 = 设置的宽度+border+padding
怪异盒子模型:box-sizing:border-box
标签的实际宽度 = 设置的宽度,如果设置了border和padding则需要减掉

2.设置元素水平垂直居中

第一种定位:
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%)
第二种定位:
position:absolute;
margin:auto;
left:0;
top:0;
bottom:0;
right:0;
第三种flex布局:
display:flex;
justfy-content:center;
align-items:center;

3.说一些es6语法

let和const,let表示变量,const表示常量,两者都是块级作用
模板字符串:``表示 let template = `<div><span>hello world</span></div>`
箭头函数:注意没有this指向,继承当前上下文的this关键字
对象初始化的简写,键值对一致时可以只写一个
解构语法 const {name ,age } = {name:'zs',age:16}
...语法,扩展运算符:color=[a,b],fullcolor = [...color,c,d] => [a,b,c,d]
import和export语法,看node版本是否支持,不支持的话去presets设置项设置es2015,
帮你转换成支持的语法,npm i bable-preset-es2015

4.简要说说promise以及里面的一些方法

在promise内部进行错误处理
var promise=new Promise(function(resolve,reject){
   try {
      throw new Error('test');
   }catch(e){
      reject(e)
   }
})
处理多个请求可以用promise.all方法,将几个请求一起发出,然后将返回的数据由数组
返回,然后去拿自己想要的数据即可
async和await也可以用于异步请求,这么写更加简洁
 const makeRequest = async () => {
    try {
        // this parse may fail
        const data = JSON.parse(await getJSON())
        console.log(data)
    } catch (err) {
        console.log(err)
    }
}

5.重绘和重排,防抖和节流

例如一些字体颜色背景夜色发生更改会导致重绘,如一些字体大小或者元素大小发生改变
会导致重排,重绘不一定会引起重排但是重排一定会引起重绘
客户在短时间内连续触发同一事件即可使用防抖,即在某个时间期限内指定该函数只能执
行一次,而节流则是在如果短时间内大量触发同一事件,那么在函数执行一次之后,在指
定时间内不再执行,直到过了指定的时间段,即事件以一个时间段为间隔的去执行。

6.输入url之后的流程

用户输入网址,浏览器发起DNS查询请求
建立TPC连接
浏览器向web服务器发送一个http请求
发送相应数据给客户端
浏览器解析http response,渲染页面

7.请简要说说mvc和mvvm的区别

m为model,用于数据处理,v为view,用于视图处理,但是两者不会互相干扰,即数据处理不会去写视图更新相关的操作,而视图更新
中也不会写数据处理相关的操作,此时的mvc中提供了一个c即控制器,c可以直接引用m和v,但是m和v不能直接引用c,c的责任为及
时把m的最新数据赋值给v完成视图的更新。但是随着发展,应用的逻辑越来越复杂,而m和v的定义早就定义死,数据解析这一块全都由
c来完成,但是会导致c越来越臃肿,设计c的当初其实并没有数据解析这一功能,所以我们额外创建了一个专门用来数据解析的一个新
的类,即vm,viewModel。在mvvm里面不是没有c,而是c的存在感被vm模块给大大降低了。之前需要由c处理的东西现在基本都是交给了
vm模块来完成,所以导致c的存在感降低,并不是没有c模块。
vue就是一个mvvm框架,其双向数据绑定原理是利用了object.defineProperty()方法来重新定义了对象获取属性值和设置属性值的的操
作来实现的,也就是其中的get和set方法

8.前端攻击有哪些,原理是什么,有什么解决办法

①.xss跨站脚本攻击
原理:页面渲染的数据中包含可运行的脚本
攻击的基本类型:反射型(url参数直接注入)和存储型(存储到DB后读取时注入)
注入点:HTML节点内的内容(text) html的DOM元素的属性 js代码  富文本
②.CSRF 跨站请求伪造
原理:在第三方网站向本网站发起请求
(1)用户在a站前端页面发起登录(身份认证)请求
(2)a站后端确认身份,登录成功,cookie中存在用户的身份认证信息
(3)b站前端页面向a站后端发起请求,带着a站的cookie信息(身份认证信息),请求成功
综上,可以清楚的知道,只要用户访问了b站的前端页面,b站就可以在用户完全不知道的情况下,带着a站的用户登录信息向a发起请求
③.点击劫持
原理:第三方网站通过iframe内嵌某一个网站,并且将iframe设置为透明不可见,将其覆盖在其他经过伪装的dom上,违章的可点击的
dom与实际的内嵌网站的可点击dom位置相同,当用户点击伪装的dom时,实际上点击的是iframe中内嵌的网页的dom从而触发请求操作
特点:用户自己做了点击事件,但是用户毫不知情

如何防御
①.xss攻击防御
(1)浏览器自带防御机制,主要应对反射型攻击,http响应头中自动添加x-xss-protection,值为0关闭,1打开,默认打开
(2)对特定字符做转义:内容注入替换尖括号,属性注入替换单引号或者双引号
(3)csp内容安全策略:用于指定哪些内容可执行
②.CSRF攻击防御
CSRF有几个特点,b站发送的请求携带a站的cookie,b站发送的请求不经过a站前端,http请求头中的referer为b站
(1)禁止第三方网站携带本网站的cookie信息,设置same-site属性,strict值为所有第三方请求都不能携带本网站的cookie,lax值
为链接可以但是form表单提交和ajax请求不行
(2)本网站前端页面添加验证信息:使用验证码或者添加token验证
验证码:当发起请求时,前端需要输入本网站页面的验证信息,后端对验证码进行验证,验证码正确才会进行相关操作(存储数据等)
token验证:a站前端将token存于当前页面和cookie中,当请求a站后端的时候,参数中带上这个token字段,a站后端将参数中的token和
cookie中的token做对比,相同则验证通过,不同则请求不合法
不管是token还是验证码原理都是一样的,在a站前端页面加入验证,当第三方网站请求a站后端时,及时能携带a站的cookie,但是因为
没有经过a站的前端页面而拿不到验证消息,也会导致请求失败
(3)referer验证:禁止来自第三方的请求
③.点击劫持攻击防御
(1)js禁止内嵌:当王爷没有被使用iframe内嵌时,top和window是相等的,当王爷被内嵌时,top和window是不相等的,可以在本网
站的页面中添加如下判断
        <script>
        if (top.location != window.location) {
            //如果不相等,说明使用了iframe,可进行相关的操作
        }
        </script>
但是这种方式并不是万能的,因为iframe标签中的属性sandbox属性是可以禁用内嵌网页的脚本的:
<iframe sandbox='allow-forms' src='...'></iframe>
(2)设置http响应头x-frame-options:有三个值deny(禁止内嵌)sameorigin(只允许同域名页面内嵌)allow-from(指定内嵌的地址)
能在所有的web服务器端预设好x-frame-options字段值是最理想的状态
(3)一些辅助手段,比如添加验证码,提高用户的防范意识

9.vue中的两种路由模式,并简单说明一下

vue的路由模式有两种,一种hash模式,另外一种为history模式,hash模式可以利用onhashchange事件,可以在windows对象上监听这
个事件,因为hash发生变化的url都会被浏览器记录下来,从而发现浏览器的前进后退都可以用,但是你只能改变#号后面的url片段,在
vue项目中我们用hash模式,使用一个完整的url的hash来模拟一个完整的url,于是当url发生更改的时候,页面不会重新加载,而当页
面使用history模式的时候,在开发阶段可能一切正常,但是项目上线之后,页面发生跳转会给你返回404,因为后端没有配置对应的
url的请求资源,返回的都是同一个index.html页面。

10.深拷贝与浅拷贝

浅拷贝与深拷贝的区别简单来说B是拷贝于A,B改变了A是否跟着改变,如果改变了则是浅拷贝,如果不变则是深拷贝,实现方法如下
浅拷贝的实现方法:
(1)for..in只循环第一层,然后直接赋值
(2)object.assign()方法
            var obj = {
            a: 1,
            b: 2
            }
            var obj1 = Object.assign(obj);
            obj1.a = 3;
            console.log(obj.a) // 3
(3)直接=号赋值
深拷贝的实现方法:
(1)采用递归去拷贝所有层级的属性
    function deepClone(obj){
        let objClone = Array.isArray(obj)?[]:{};
        if(obj && typeof obj==="object"){
            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    //判断ojb子元素是否为对象,如果是,递归复制
                    if(obj[key]&&typeof obj[key] ==="object"){
                        objClone[key] = deepClone(obj[key]);
                    }else{
                        //如果不是,简单复制
                        objClone[key] = obj[key];
                    }
                }
            }
        }
        return objClone;
    }    
        let a=[1,2,3,4],
            b=deepClone(a);
        a[0]=2;
        console.log(a,b);
(2)通过JSON对象来实现深拷贝
    function deepClone2(obj) {
      var _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
      return objClone;
    }
缺点是无法实现对对象中方法的深拷贝,会显示为undefined
(3)通过jquery的extend()方法实现深拷贝
    var array = [1,2,3,4];
    var newArray = $.extend(true,[],array); // true为深拷贝,false为浅拷贝
(4)lodash函数库实现深拷贝     let result = _.cloneDeep(test)使用该方法即可
(5)手动实现深拷贝
    let obj1 = {
       a: 1,
       b: 2
    }
    let obj2 = {
       a: obj1.a,
       b: obj1.b
    }
    obj2.a = 3;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
(6)如果对象的value是基本类型的话,也可以用Object.assign来实现深拷贝,但是要把它赋值给一个空对象
    var obj = {
    a: 1,
    b: 2
}
var obj1 = Object.assign({}, obj); // obj赋值给一个空{}
obj1.a = 3;
console.log(obj.a);// 1
(7)用slice实现对数组的深拷贝
    // 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
    // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
    var arr1 = ["1","2","3"]; 
    var arr2 = arr1.slice(0);
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 );
    console.log("数组的新值:" + arr2 );
(8)用concat实现对数组的深拷贝
    // 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
    var arr1 = ["1","2","3"];
    var arr2 = arr1.concat();
    arr2[1] = "9";
    console.log("数组的原始值:" + arr1 );
    console.log("数组的新值:" + arr2 );
    // 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
    var arr1 = [{a:1},{b:2},{c:3}];
    var arr2 = arr1.concat();
    arr2[0].a = "9";
    console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9
    console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9
(9)直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果
function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
(10)使用扩展运算符实现深拷贝
// 当value是基本数据类型,比如String,Number,Boolean时,是可以使用拓展运算符进行深拷贝的
// 当value是引用类型的值,比如Object,Array,引用类型进行深拷贝也只是拷贝了引用地址,所以属于浅拷贝
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }

11.vue中的/deep/的作用

vue中的css样式不起作用,用!important也不起作用,此时可以用/deep/,加了之后有作用,此时这个deep是深度作用域。
在vue组建中给style标签添加属性scoped属性可以避免组件内样式对外界造成污染,scoped使得组件内的样式编程局域样式,只作用于
当前组件,如果我们在当前组件内使用了scoped,但是我想在当前组件内改变组件的样式的时候会发现不好使,不好使的原因是因为父
组件内解析的是父组件的hash值,而子组件内标签上添加的是子组件的hash值,对应不上所以没有效果。添加的scoped属性会在标签上
添加一个data-v-hash属性,如果父子组件都有scoped那么子组件最外层既有父组件的hash值又有子组件的hash值。
如何解决不生效,此时需要用到deep,我们可以不加scoped属性,来覆盖子组件的样式
例如.father-div /deep/ .child-div{color:red;}

12.http和https的区别

http:超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,长基于tcp/ip协议传输数据,互联网上应用最为广泛的一
种网络协议,所有的www文件都必须遵守这个标准,设计http的初衷是为了提供一种发布和接收HTML页面的方法。
https:HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用
的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
TLS是传输层加密协议,前身是SSL协议,由网景公司1995年发布,有时候两者不区分。
SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。