前端面试

前端常见面试题二十

2019-07-31  本文已影响68人  jw_fc89

目录:
1、什么是事件代理且描述事件代理的原理及为什么要用事件代理?
2、移动端1px问题,为什么会有?如何解决?
3、解释jsonp的原理
4、在工作中你是如何优化自己的代码的?
5、axios是什么?如何使用?描述其实现登录的流程
6、用JS去掉数组里面重复的数据,并且打印出来

1、什么是事件代理且描述事件代理的原理及为什么要用事件代理?

利用事件冒泡的原理,把事件交由他的父元素或祖先元素代为执行。
事件冒泡:就是事件从最深的节点开始,然后逐步向上传播事件

优点:
1、可以大量节省内存占用,减少事件注册。比如ul上代理所有li的click事件就很不错;
2、可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为适合;

缺点:
事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件;

2、移动端1px问题,为什么会有?如何解决?

问题:

第一种想法:UI设计师设计的时候,画的1px(真实像素)实际上是0.5px(css)的线或者边框。但是他不这么认为,他认为他画的就是1px的线,因为他画的稿的尺寸本身就是屏幕尺寸的2倍。假设手机视网膜屏的宽度是320x480宽,但实际尺寸是640x960宽,设计师设计图的时候一定是按照640x960设计的。但是前端工程师写代码的时候,所有css都是按照320x480写的,写1px(css),浏览器自动变成2px(真实像素)。
第二种想法:像素有设备物理像素和逻辑像素,其实这两个px的含义其实是不一样的,UI设计师要求的1px是指设备的物理像素1px,而CSS里记录的像素是逻辑像素,它们之间存在一个比例关系,通常可以用 javascript 中的 window.devicePixelRatio 来获取,也可以用媒体查询的 -webkit-min-device-pixel-ratio 来获取。当然,比例多少与设备相关。

解决方案:

1、rem + viewport
2、伪类+transform实现
原理:是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
链接地址1:https://blog.csdn.net/a419419/article/details/80217328
链接地址2:https://www.jb51.net/html5/627114.html
链接地址3:https://www.jianshu.com/p/31f8907637a6

3、解释jsonp的原理

很简单的一句:动态创建script标签,利用src进行传值
参考一位大神的讲解:
为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

总结:
JSONP就是通过加载远程js文件,远程的js文件中写有调用当前域中已经写好的js方法的语句,并且这个语句中包含需要传入的数据,这样这个数据就传入到了当前域。用一句话概括就是:通过js文件携带数据实现了数据的跨域访问。
参考链接:https://blog.csdn.net/tianjindong0804/article/details/84971922

4、在工作中你是如何优化自己的代码的?

1、代码优化(css、html、js优化)
2、减少HTTP请求(雪碧图,文件合并…)
3、减少DOM节点
4、无阻塞(内联CSS,JS置后…)
5、缓存


image

1、CDN

CDN【Content Delivery Network】,即内容分发网络。属于http缓存技术中的一种。

2、原理:

其基本思路是尽可能避开网络上可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。CDN管理分布在多个地理位置上的服务器,其系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重定向到一个能提供最好用户体验的服务节点上。
总的来说,内容服务基于缓存服务器,也称作代理缓存【网关缓存】,它位于网络的边缘,据用户仅“一跳”之隔。代理缓存提供数据中心服务器的一个镜像,当用户对某网址访问的请求并非第一次,那么代理缓存在很大概率上缓存了域名,就不需要大费周章通过DNS【域名服务系统】来获取对应域名。
这里引用一个很喜欢的小比喻:古代交通不便,当有蛮夷入侵【用户请求】时朝廷从京城【数据中心服务器】调兵到边防,等军队到达的时候敌人早已一溜烟儿跑了。于是君主改变策略,将一批军队调配到边防驻扎,这些边防部队就能有效抵御入侵,正如代理缓存能快速响应用户请求(o)/~

二、减少http请求【减少请求数,降低请求量】

1、脚本合并
原理
通常一个大型网站需要依赖多个JS文件。可以把多个文件合并成一个,这样只需要引用一个
方法
① Grunt
② JSCompress

2、CSS雪碧图
优点
① 减少http请求,提高页面加载速度。只需加载一张图片,且由于只需要一个对应的色表,这张图片的大小很可能比拼合前的总尺寸小。
② 减少鼠标滑过的bug:IE6不会主动预加载:hover中的背景图片,因此使用多张图片时会出现闪白的现象。
缺点
① 最大的问题是内存的使用:除非非常小心的组织,否则会留下大量无用的空白。
② 影响页面缩放功能:缩放时要避免雪碧中相邻图片露出来。

3、文件压缩
原理
包括CSS、JavaScript、图片的压缩。
JavaScript:

最小化:删除注释和空格等不必要的字符。安全、直白,文件减少21%。
混淆:删除注释和空格,将函数名和变量名替换成短的字符串,难于反向工程。复杂,容易产生问题,文件减少25%。
方法
① JSMin
② YUI Compressor在线
③ 在线JS/CSS/HTML压缩
④ JavaScript压缩/解压缩
⑤ grunt
⑥ 批量图片压缩
⑦ 压缩HTTP响应内容:Gzip

4、延迟加载图片【Lazy Load Images】
首先只加载第一屏的图片,当用户滚动访问后面的内容时在加载相应图片。
方法:将图片的src属性值存放在一个非src的自定义属性中,判断图片进入可视区域后将路径赋值给src属性。

5、CSS3图标
iconfont公开图标库
Font Awesome

6、避免重定向
301:永久重定向,抓取新内容的同时也将旧的网址替换为重定向之后的网址;
302:暂时重定向,抓取新的内容而保留旧的网址
SEO:302好于301
重定向会增加http请求数,但必要的重定向有利于提高用户体验
方法:
定义链接URL时使用最完整的、最直接的地址。如:

使用www.baidu.com而非baidu.com
使用www.google.com.hk而非www.google.com
使用http://write.blog.csdn.net/而非http://write.blog.csdn.net
在使用Response.Redirect的时候,设置第二个参数为false.
为true时,相当于在Redirect后调用Exit Sub/Function,不再执行Redirect后的语句;
为false时,依然执行Redirect后的语句。

三、代码优化

1、减少对DOM的操作
对DOM的操作代价是昂贵的,这在网页中通常是一个性能瓶颈。
减少对DOM元素的查询与修改:
① 查询:需多次访问的可以保存在变量中。
② 修改:使用innerHTML代替DOM操作。

2、减少重排与重绘
原理
页面生成时会进行至少一次渲染,用户访问过程中还可能不停地重新渲染,情况如下:

修改DOM
修改样式表
用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)
重新渲染,就需要重新生成布局和重新绘制。前者叫做“重排【reflow】”,表现为页面布局出现变动。后者叫做“重绘【repaint】”。“重绘”不一定需要“重排”,如改变元素颜色,因为此时页面布局并未发生改变;而“重排”一定会导致“重绘”,布局的变化会同时触发“重排”和“重绘”。“重排”和“重绘”均会使性能下降,因此要尽量避免。
一般规则
样式表越简单,重排和重绘就越快;
重排和重绘的DOM元素层级越高,成本越高;
table元素的重排和重绘成本
2、减少重排与重绘
原理
页面生成时会进行至少一次渲染,用户访问过程中还可能不停地重新渲染,情况如下:
修改DOM
修改样式表
用户事件(比如鼠标悬停、页面滚动、输入框键入文字、改变窗口大小等等)
重新渲染,就需要重新生成布局和重新绘制。前者叫做“重排【reflow】”,表现为页面布局出现变动。后者叫做“重绘【repaint】”。“重绘”不一定需要“重排”,如改变元素颜色,因为此时页面布局并未发生改变;而“重排”一定会导致“重绘”,布局的变化会同时触发“重排”和“重绘”。“重排”和“重绘”均会使性能下降,因此要尽量避免。
一般规则
样式表越简单,重排和重绘就越快;
重排和重绘的DOM元素层级越高,成本越高;
table元素的重排和重绘成本高于div元素。
优化方法

通过class改变样式,避免逐条改变;
对display:none的元素操作不会引发重排和重绘,因此需要多次操作的元素可改变其display属性再进行操作,完成操作后再将其显示,这样只需要两次重排和重绘;另外,visibility:hidden的元素操作只重绘;
脱离文档流的元素重排开销较小【如:position为absolute或fixed,float元素】,因为对文档流中元素无影响;
此方面来自阮一峰博客

3、避免CSS表达式
CSS表达式会进行大量重复计算,甚至当鼠标在页面移动时也在不停执行,这大大影响性能。尽量使用一次性表达式,避免动态计算。

四、缓存Ajax
Ajax缓存和http缓存效果相同。
Ajax页面缓存是ajax处理数据时对一些重复相同数据进行一个缓存操作,这种设计使客户端对一些静态页面内容的请求,比如图片,css文件,js脚本等,变得更加快捷,提高了页面的响应速度,也节省了网络通信资源。

以下http响应头是可以用来做Ajax缓存的:

Expires:应该被应用在你知道内容何时被修改的情况下。 例如,如果是股票价格您可能会设置一个在10秒后过期的数值。对于照片,你可以设置一个更长时间的Expires头,因为你指望它永远不改变。Expires头允许浏览器在一段时间内可以重复使用缓存内容,并避免任何不需要的同服务器的交互过程。
Last-Modified: 设置这个标记会通知浏览器可以使用If-Modified-Since头来产生一个条件GET请求以便检查其本地缓存。如果数据不需要更新,服务器将使用HTTP 304状态码来响应此请求。
Cache-Control: 如果允许,这应该被设置为’public’,使其他用户可以在中间代理和缓存服务器上存储和共享数据,在Firefox上,这还将启用针对HTTPS的缓存但有时候如果通过Ajax对一些后台数据进行更改的时候,虽然数据在后台已经发生改变,但是页面缓存中并没有改变,对于相同的URL,Ajax提交过去以后,浏览器还只是简单的从缓存中拿数据。

解决Ajax缓存问题:

URL参数添加随机数或时间戳。【会在客户端产生大量缓存文件】
修改文件最后更改时间:setRequestHeader(“If-Modified-Since”,”0”);。这致使服务器上文件的最后修改时间与其不同,因此不会访问缓存文件,而是重新访问服务器。【只在客户端产生一个缓存文件】
禁用缓存【和文章的使用Ajax缓存有一丢丢矛盾啊】

客户端设置: 在ajax发送请求前加上xmlHttpRequest.setRequestHeader(“Cache-Control”,”no-cache”);
服务器端设置:在服务端加上header(“Cache-Control: no-cache, must-revalidate”);

五、配置Etags
ETag,全称为:Entity Tag,意思是实体标签,从名字上看,是对于某种实体的一个标识。它属于HTTP协议的一部分,也就是所有的Web服务器都应该(也确实能)支持这个特性。
它的作用是用一个特殊的字符串来标识某个资源的“版本”。

工作流程

客户端(浏览器)发出请求的时候,服务器会在响应中指定Etag;
客户端If-None-Match头回传ETag给服务器,服务器检查Etag与If-None-Match是否匹配;
若匹配则表示该资源并没有修改过,浏览器会返回304状态码,而且不需要在响应正文中包含所求资源;
客户端可以使用自己缓存的版本。

附链接:https://www.jb51.net/yunying/430232.html

5、axios是什么?如何使用?描述其实现登录的流程

axios是一个第三方的网络请求库,
axios是一个基于Promise封装的ajax类库,它可以在任何支持ajax和Promise的环境下使用。
附代码1:https://blog.csdn.net/weixin_44868863/article/details/90511452
附代码2:https://www.cnblogs.com/bgwhite/p/9547314.html

登录流程:

第一步 路由拦截

首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由,否则就进入登录页面。

const routes = [
    {
        path: '/',
        name: '/',
        component: Index
    },
    {
        path: '/repository',
        name: 'repository',
        meta: {
            requireAuth: true,  // 添加该字段,表示进入这个路由是需要登录的
        },
        component: Repository
    },
    {
        path: '/login',
        name: 'login',
        component: Login
    }
];

定义完路由后,我们主要是利用vue-router提供的钩子函数beforeEach()对路由进行判断。

router.beforeEach((to, from, next) => {
    if (to.meta.requireAuth) {  // 判断该路由是否需要登录权限
        if (store.state.token) {  // 通过vuex state获取当前的token是否存在
            next();
        }
        else {
            next({
                path: '/login',
                query: {redirect: to.fullPath}  // 将跳转的路由path作为参数,登录成功后跳转到该路由
            })
        }
    }
    else {
        next();
    }
})

每个钩子方法接收三个参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
确保要调用 next 方法,否则钩子就不会被 resolved。
其中,to.meta中是我们自定义的数据,其中就包括我们刚刚定义的requireAuth字段。通过这个字段来判断该路由是否需要登录权限。需要的话,同时当前应用不存在token,则跳转到登录页面,进行登录。登录成功后跳转到目标路由。

第二步:拦截器

要想统一处理所有http请求和响应,就得用上 axios 的拦截器。通过配置http response inteceptor,当后端接口返回401 Unauthorized(未授权),让用户重新登录。

// http request 拦截器
axios.interceptors.request.use(
    config => {
        if (store.state.token) {  // 判断是否存在token,如果存在的话,则每个http header都加上token
            config.headers.Authorization = `token ${store.state.token}`;
        }
        return config;
    },
    err => {
        return Promise.reject(err);
    });

// http response 拦截器
axios.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        if (error.response) {
            switch (error.response.status) {
                case 401:
                    // 返回 401 清除token信息并跳转到登录页面
                    store.commit(types.LOGOUT);
                    router.replace({
                        path: 'login',
                        query: {redirect: router.currentRoute.fullPath}
                    })
            }
        }
        return Promise.reject(error.response.data)   // 返回接口返回的错误信息
    });

通过上面这两步,就可以在前端实现登录拦截了。登出功能也就很简单,只需要把当前token清除,再跳转到首页即可。
转载地址:https://blog.csdn.net/wongru1/article/details/79474266

6、用JS去掉数组里面重复的数据,并且打印出来

  1. ES6 Set
// No Function
[...new Set(arr)]   // [...new Set(需要去重的数组)]
//Function
function distinct(arr) {
  return Array.from(new Set(arr));
}     

2 .使用splice配合两重for循环去重

function distinct(arr) {
  for(let i = 0; i < arr.length; i++) {
      for(let j = i + 1; j < arr.length; j++) {
          if(arr[i] === arr[j]) {
              arr.splice(j, 1);
              j--;
          }
      }
  }
  return arr;
}
  1. 使用for循环配合indexOf去重
 function distinct(arr) {
     let newArr = [];
     for(let i = 0; i < arr.length; i++) {
         if(newArr.indexOf(arr[i]) === -1) {
             newArr.push(arr[i]);
         }
     }
     return newArr;
}
上一篇下一篇

猜你喜欢

热点阅读