jersey

jersey 查看完整档案

武汉编辑  |  填写毕业院校  |  填写所在公司/组织 leejersey.github.io/ 编辑
编辑

个人动态

jersey 赞了回答 · 1月21日

解决Chrome Network 里,Size 和 Content 分别是什么意思

Content 是这个资源的实际大小,Size 则是传输时的大小。之所以不同是因为多数情况下我们请求资源所在的服务器会对资源进行压缩处理以减少传输负荷(如 Gzip),而客户端则会对其做相应的解压处理。所以前者是 Size,后者即是 Content

有时候资源的体积其实特别小,Gzip 压缩之后反而会比原始尺寸还要大(加入头信息之类的,我也不懂),所以偶尔你也会看到 SizeContent 大的情况出现。(感谢 @依云 曾经的指教)

关注 3 回答 2

jersey 关注了问题 · 2019-07-15

es6如何优雅的判断两个数组包含的值完全相同,顺序无所谓

这是我现在的做法,请问还有更好一些的方式么

let flag = true
    const listA = [1, 2, 3]
    const listB = [2, 3, 4]
    if (listA.length !== listB.length) {
      flag = false
    } else {
      listA.forEach(item => {
        if (listB.indexOf(item) === -1) {
          flag = false
        }
      })
    }

关注 12 回答 9

jersey 赞了问题 · 2019-07-15

es6如何优雅的判断两个数组包含的值完全相同,顺序无所谓

这是我现在的做法,请问还有更好一些的方式么

let flag = true
    const listA = [1, 2, 3]
    const listB = [2, 3, 4]
    if (listA.length !== listB.length) {
      flag = false
    } else {
      listA.forEach(item => {
        if (listB.indexOf(item) === -1) {
          flag = false
        }
      })
    }

关注 12 回答 9

jersey 赞了文章 · 2018-10-09

百度阿里网易大疆等大小厂前端校招面筋

自我介绍下:某985硕士,程序媛,接触前端一年时间。从八月份开始校招面试笔试,前前后后大厂小厂也都面了挺多,不过大厂基本都被我挂完了,哭晕我,还是太菜啊。面过的公司:ThoughtWorks,大疆,阿里,网易,百度,电信it研发中心,深信服,华为,小米,搜狗,腾讯。拿了offer的公司目前是:大疆、电信、深信服、华为。下面总结了这段时间的面筋和挂筋~

腾讯

10月16日更新,面了腾讯一面,然后就挂了,结束的太快就像龙卷风。

1. 怎么从十万个节点中找到想要的节点,怎么快速在某个节点前插入一个节点?

这个题目我已经在segmentfault上提问了,目前还没有人给出回答,求大神前去解答~

题目链接: 怎么在有十万个节点的html文档中找到特定的某个节点

2. 负载均衡方式和容错机制

3. 描述一个文件从硬盘读取到进程内存中的过程

4. TCP三次握手和挥手的过程,除了这些书本上的还有哪些?

5. 画一下OSI七层模型

6. 写一个你最熟悉的算法

7. 有哪些伪类和伪元素

8. IE盒模型和标准盒模型,用哪个属性改变

9. 怎么清除浮动,原理是什么

10. em,rem,px的区别

大疆

大疆是我校招面的第一家公司,从六月份投简历,然后笔试面试到拿到录用意向书,前后用了近四个月,来之不易啊。

一面二面

因为时间太久,就直接放在一起了,问的都是基础吧,讲真,大疆前端面试不难,都是很基础的,就是时间长,等的捉急。一面是电话面,两个面试官轮流问;二面是视频面,是三个面试官一起微信视频,视频面还是蛮累的,上下左右都得顾上;终面是去的现场面,就跟一个面试官聊了十几分钟人生。

1. meta标签

meta标签:提供给页面的一些元信息(名称/值对), 比如针对搜索引擎和更新频度的描述和关键词。

  • name:名称/值对中的名称。常用的有author、description、keywords、generator、revised、others。 把 content 属性关联到一个名称。
  • http-equiv:没有name时,会采用这个属性的值。常用的有content-type、expires、refresh、set-cookie。把content属性关联到http头部。
  • content: 名称/值对中的值, 可以是任何有效的字符串。 始终要和 name 属性或 http-equiv 属性一起使用。
  • scheme: 用于指定要用来翻译属性值的方案。

2. css哪些属性可以继承

  • 字体相关:line-height, font-family, font-size, font-style, font-variant, font-weight, font
  • 文本相关: letter-spacing, text-align, text-indent, text-transform, word-spacing
  • 列表相关:list-style-image, list-style-position, list-style-type, list-style
  • 颜色:color

3. css3有哪些新属性

(1)边框:

  • border-radius:圆角边框,border-radius:25px;
  • box-shadow:边框阴影,box-shadow: 10px 10px 5px #888888;
  • border-image:边框图片,border-image:url(border.png) 30 30 round;

(2)背景:

  • background-size:规定背景图片的尺寸,background-size:63px 100px;
  • background-origin:规定背景图片的定位区域,背景图片可以放置于 content-box、padding-box 或 border-box 区域。background-origin:content-box;
  • CSS3 允许您为元素使用多个背景图像。background-image:url(bg_flower.gif),url(bg_flower_2.gif);

(3)文本效果:

  • text-shadow:向文本应用阴影,可以规定水平阴影、垂直阴影、模糊距离,以及阴影的颜色。text-shadow: 5px 5px 5px #FF0000;
  • word-wrap:允许文本进行换行。word-wrap:break-word;

(4)字体:CSS3 @font-face 规则可以自定义字体。

(5)2D 转换(transform

  • translate():元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数。 transform: translate(50px,100px);
  • rotate():元素顺时针旋转给定的角度。允许负值,元素将逆时针旋转。transform: rotate(30deg);
  • scale():元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数。transform: scale(2,4);
  • skew():元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数。transform: skew(30deg,20deg);
  • matrix(): 把所有 2D 转换方法组合在一起,需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。transform:matrix(0.866,0.5,-0.5,0.866,0,0);

(6)3D 转换

  • rotateX():元素围绕其 X 轴以给定的度数进行旋转。transform: rotateX(120deg);
  • rotateY():元素围绕其 Y 轴以给定的度数进行旋转。transform: rotateY(130deg);

(7)transition:过渡效果,使页面变化更平滑

  • transition-property :执行动画对应的属性,例如 color,background 等,可以使用 all 来指定所有的属性。
  • transition-duration:过渡动画的一个持续时间。
  • transition-timing-function:在延续时间段,动画变化的速率,常见的有:ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier 。
  • transition-delay:延迟多久后开始动画。

简写为:transition: [<transition-property> || <transition-duration> || <transition-timing-function> || <transition-delay>];

(8)animation:动画

使用CSS3 @keyframes 规则。

  • animation-name: 定义动画名称
  • animation-duration: 指定元素播放动画所持续的时间长
  • animation-timing-function:ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier(<number>, <number>, <number>, <number>): 指元素根据时间的推进来改变属性值的变换速率,说得简单点就是动画的播放方式。
  • animation-delay: 指定元素动画开始时间
  • animation-iteration-count:infinite | <number>:指定元素播放动画的循环次
  • animation-direction: normal | alternate: 指定元素动画播放的方向,其只有两个值,默认值为normal,如果设置为normal时,动画的每次循环都是向前播放;另一个值是alternate,他的作用是,动画播放在第偶数次向前播放,第奇数次向反方向播放。
  • animation-play-state:running | paused :控制元素动画的播放状态。

简写为: animation:[<animation-name> || <animation-duration> || <animation-timing-function> || <animation-delay> || <animation-iteration-count> || <animation-direction>]

这里只列出了一部分,详情可以去看w3school的CSS3 教程

4. 闭包是什么,什么时候闭包会消除?

因为作用域链,外部不能访问内部的变量和方法,这时我们就需要通过闭包,返回内部的方法和变量给外部,从而就形成了一个闭包。

JavaScript是一门具有自动垃圾回收机制的编程语言,主要有两种方式:

  • 标记清除(最常用)

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

  • 引用计数

引用计数(reference counting)的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

导致问题:会导致循环引用的变量和函数无法回收。

解决:将用完的函数或者变量置为null。

5. 怎么理解js是单线程的

主要说一下异步以及事件循环机制,还有事件队列中的宏任务、微任务。

  • macrotask:主代码块,setTimeout,setInterval、setImmediate等。
  • microtask:process.nextTick(相当于node.js版的setTimeout),Promise 。process.nextTick的优先级高于Promise。

更详细可以看这篇博客:这一次,彻底弄懂 JavaScript 执行机制,讲的非常清晰。

6. 有哪些排序算法,时间复杂度是多少?什么时候快排的效率最低?

排序算法最坏事件复杂度平均时间复杂度稳定度空间复杂度
冒泡排序O(n^2)O(n^2)稳定O(1)
插入排序O(n^2)O(n^2)稳定O(1)
选择排序O(n^2)O(n^2)稳定O(1)
快速排序O(n^2)O(n*log2n)不稳定O(log2n)~O(n)
二叉树排序O(n^2)O(n*log2n)不一定O(n)
堆排序O(n*log2n)O(n*log2n)不稳定O(1)

整个序列已经有序或完全倒序时,快排的效率最低。

7. 原生ajax的请求过程

创建全平台兼容的XMLHttpRequest对象:

function getXHR(){
  var xhr = null;
  if(window.XMLHttpRequest) {// 兼容 IE7+, Firefox, Chrome, Opera, Safari
    xhr = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");// 即MSXML3
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");// // 兼容 IE6, IE5,很老的api,虽然浏览器支持,功能可能不完善,故不建议使用
      } catch (e) {
        alert("您的浏览器暂不支持Ajax!");
      }
    }
  }
  return xhr;
}

Ajax请求数据的过程:

var xhr = getXHR();
xhr.open('GET', url/file,true);  //设置请求方式,url,以及是否异步
xhr.onreadystatechange = function() {   //设置回调监听函数
   if(xhr.readyState==4){
        if(xhr.status==200){
            var data=xhr.responseText;
             console.log(data);
   }
};
xhr.onerror = function() {
  console.log("Oh, error");
};
xhr.send();  //发送请求

8. http状态码,cookie字段,cookie一般存的是什么,session怎么存在的?

这部分可以参考我的博客:HTTP协议知识点总结

9. http请求方式有哪些?

  • HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
  • HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
  • 更多请看:HTTP请求方法

10. 怎么用原生js实现一个轮播图,以及滚动滑动

之前我使用轮播图都是用的antd的组件,所以我大致说了一下思路,用定时器去实现,以及如何实现平滑的滚动效果。详情请看: 原生js实现轮播图

11. 用过哪些开源的组件

说了antd和element-ui。

12. 怎么实现上传下载的功能

主要说了下form表单和input标签。

13. react生命周期,以及diff算法,diff算法是对树的深度优先遍历还是广度优先遍历?

14. 强缓存和协商缓存

参考:HTTP协议知识点总结

15. react-router的原理

react-router就是控制不同的url渲染不同的组件。react-router在history库的基础上,实现了URL与UI的同步。

原理:DOM渲染完成之后,给window添加onhashchange事件监听页面hash的变化,并且在state属性中添加了route属性,代表当前页面的路由。

具体步骤

  • 当点击链接,页面hash改变时,触发绑定在 window 上的 onhashchange 事件;
  • 在 onhashchange 事件中改变组件的 state中的 route 属性,react组件的state属性改变时,自动重新渲染页面;
  • 页面随着 state 中的route属性改变,自动根据不同的hash给Child变量赋值不同的组件,进行渲染。

参考:react-router的实现原理

16. 怎么用无人机捕获天空上的鸟

这个题目我也不造啊,毕竟我没用过无人机,有知道的大神可以在评论中回答一下~

终面

终面是去的现场,在深圳总部那边,基本就是闲聊了二十来分钟吧,面完还有hr小姐姐给我们介绍和参观了无人机,酷炫~

  1. 做的项目中,哪个做的最深入最久
  2. 为什么要做前端,喜欢做前端么
  3. 未来的职业规划
  4. 了解大疆么,大疆的文化是什么
  5. 除了实习,还做过哪些项目
  6. 如果生活富足,衣食无忧,你会选择干什么

阿里巴巴

阿里是提前批,找人内推了菜鸟网络,面了六轮,面的我怀疑人生了,中途四面本来已经挂了,后面三面面试官又捞起来给我加面了一轮,不过最后还是挂在了hr。

一面

1. css选择器,怎么选择相同的类

id、class、标签、伪类、通配符等。可以用getElementsByClassName()选择相同的类。

2. css3有哪些伪类,伪类选择器有哪些

这里要区分一下伪类和伪元素的概念。根本区别在于它们是否创造了新的元素(抽象)。

  • 伪类:用于向某些选择器添加特殊的效果。例如,a标签的:link, :visited, :hover, :active; 以及 :first-child, :last-child
  • 伪元素:是html中不存在的元素,用于将特殊的效果添加到某些选择器。例如:before,:after, :first-letter, :first-line。css3只新增了一个伪元素 ::selection(改变用户所选取部分的样式)。

参考: CSS3 选择器——伪类选择器

3. OSI七层网络模型

OSI七层模型作用对应协议对应设备
应用层它是计算机用户,以及各种应用程序和网络之间的接口HTTP, FTP, SMTP, POP3计算机设备
表示层信息的语法语义以及它们的关系,如加密解密、转换翻译、压缩解压缩IPX, LPP, XDP
会话层建立、维护、管理应用程序之间的会话SSL, TLS, DAP, LDAP
传输层服务点编址,分段与重组、连接控制、流量控制、差错控制TCP, UDP防火墙
网络层为网络设备提供逻辑地址,进行路由选择、分组转发IP ARP RARP ICMP IGMP路由器
数据链路层物理寻址,同时将原始比特流转变为逻辑传输路线PPTP, ARP, RARP交换机
物理层机械、电子、定时接口通道信道上的原始比特流传输IEEE 802.2, Ethernet v.2, Internetwork网卡

参考: 一张非常强大的OSI七层模型图解

4. MVC和MVVM的区别

  • Model用于封装和应用程序的业务逻辑相关的数据以及对数据的处理方法;
  • View作为视图层,主要负责数据的展示;
  • Controller定义用户界面对用户输入的响应方式,它连接模型和视图,用于控制应用程序的流程,处理用户的行为和数据上的改变。

MVC将响应机制封装在controller对象中,当用户和你的应用产生交互时,控制器中的事件触发器就开始工作了。

MVVM把View和Model的同步逻辑自动化了。以前Controller负责的View和Model同步不再手动地进行操作,而是交给框架所提供的数据绑定功能进行负责,只需要告诉它View显示的数据对应的是Model哪一部分即可。也就是双向数据绑定,就是View的变化能实时让Model发生变化,而Model的变化也能实时更新到View。

参考: 浅析前端开发中的 MVC/MVP/MVVM 模式

5. 用过哪些设计模式

(1)单例模式

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

实现方法:先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次。

(2)发布/订阅模式
定义:又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

场景:订阅感兴趣的专栏和公众号。

(3)策略模式
定义:将一个个算法(解决方案)封装在一个个策略类中。

优点:

  • 策略模式可以避免代码中的多重判断条件。
  • 策略模式很好的体现了开放-封闭原则,将一个个算法(解决方案)封装在一个个策略类中。便于切换,理解,扩展。
  • 策略中的各种算法可以重复利用在系统的各个地方,避免复制粘贴。
  • 策略模式在程序中或多或少的增加了策略类。但比堆砌在业务逻辑中要清晰明了。
  • 违反最少知识原则,必须要了解各种策略类,才能更好的在业务中应用。

应用场景:根据不同的员工绩效计算不同的奖金;表单验证中的多种校验规则。

(4)代理模式

定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

应用场景:图片懒加载(先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。)

(5)中介者模式

定义:通过一个中介者对象,其他所有相关对象都通过该中介者对象来通信,而不是互相引用,当其中的一个对象发生改变时,只要通知中介者对象就可以。可以解除对象与对象之间的紧耦合关系。

应用场景: 例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。

(6)装饰者模式

定义:在不改变对象自身的基础上,在程序运行期间给对象动态的添加方法。

应用场景: 有方法维持不变,在原有方法上再挂载其他方法来满足现有需求;函数的解耦,将函数拆分成多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但增强了复用性。

参考: JavaScript设计模式

6. Http状态码

7. https怎么加密

参考: HTTP协议知识点总结

8. es6相比es5有哪些优点

大概说一下:let、const,模板字符串,箭头函数,做异步处理的promise、generator、async await,es6模块等。

参考: 阮一峰 —— ECMAScript 6 入门

9. ajax请求过程

不多说,上面有。

10. 有哪些性能优化

参考:

11. 懒加载怎么实现

场景:一个页面中很多图片,但是首屏只出现几张,这时如果一次性把图片都加载出来会影响性能。这时可以使用懒加载,页面滚动到可视区在加载。优化首屏加载。

实现:img标签src属性为空,给一个data-xx属性,里面存放图片真实地址,当页面滚动直至此图片出现在可视区域时,用js取到该图片的data-xx的值赋给src。

优点:页面加载速度快,减轻服务器压力、节约流量,用户体验好。

12. 项目中写过什么组件,组件有哪些功能

主要介绍了下实习项目写过的组件,说了下如何实现的。

二面

1. react框架有哪些设计的好的地方

主要介绍了以下几个部分:

  • JSX语法
  • 组件化
  • react单项数据流
  • 虚拟DOM
  • react生命周期

2. react是怎么工作的,怎么提高性能

主要还是说了下react的生命周期,还有shouldComponentUpdate这个函数,以及diff算法。

3. redux有哪些需要改进,你觉得你用的不怎么舒服的地方?

我当时说的是redux的subscribe方法有点麻烦,每次更新数据都要手动的subscribe一下,所以觉得react-redux的api封装的更好,用起来比较简单。

参考:

4. 怎么设计一个类似于antd 的 react 组件库

这个问题把我给问懵了额,我是按照软件工程的生命周期流程来答的。

5. 你做的最自豪的一个项目

这个略过...言之有理即可

6. mysql 的左关联和右关联

左关联:保留左表中所有的元组,右表中没有的属性填充NULL。

右关联:保留右表中所有的元组,左表中没有的属性填充NULL。

7. 有没有折腾过后端

直接说了没有,之前学了点PHP,不过都快忘得差不多了额。

8. 学习方法和未来的学习路线

言之有理即可。

9. 浏览器页面渲染机制

  • 解析html建立dom树
  • 解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)
  • 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
  • 绘制render树(paint),绘制页面像素信息
  • 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

参考: 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

10. XSS和CSRF防范措施

(1)XSS:跨站脚本攻击

攻击方式:在URL或者页面输入框中插入JavaScript代码。

防范:

  • 设置httpOnly,禁止用document.cookie操作;
  • 输入检查:在用户输入的时候进行格式检查;
  • 对输出转义。

(2)CSRF:跨站点伪造请求

攻击方式:攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。主要是拿到了用户的登录态。

防范:

  • 检查 Referer 字段:这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer 字段应和请求的地址位于同一域名下。
  • 添加校验 Token:这种数据通常是表单中的一个数据项。服务器生成token并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 Token 的值为空或者错误,拒绝这个可疑请求。
  • 通过输入验证码来校验合法请求

三面

这一面基本问的是个人知识沉淀了,如实回答就可以了。

  1. 在项目中的难点,怎么解决的
  2. 你的优势是什么
  3. redux 源码学到了什么,怎么看源码的
  4. 了解哪些前端的前沿技术
  5. 平时看什么书,兴趣爱好是什么
  6. 异步有哪些方法
  7. 博客写了什么
  8. 除了实习经历,还做过哪些项目

四面

这一面是在杭州菜鸟现场面的,尴尬的是通知我的小姐姐一直强调是hr面,我天真的以为是hr面了,然鹅问了很多技术,当时候想的是阿里的hr都这么厉害了,都能直接问技术了。临走之前,特意问了面试官是hr面么,他说是技术,然后我......大概就知道自己凉了。

1. mysql的索引用的什么,介绍一下b树,b+树,红黑树这些

mysql的索引用的是B+树。

参考: 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

2. Mysql的基本写法

参考: 一千行 MySQL 学习笔记

3. 估算下杭州上空现在有多少架飞机

这个题目,也真的是为难我了额。在网上搜到了个答案,可以参考下:高盛的面试题

4. 两组数据,都存储五亿条url,内存有4G,如何找出相同的两条url

参考: 面试- 阿里-. 大数据题目- 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

5. 如何找到一个字符串中最长的两个字符串

解法:后缀数组。首先生成字符串的所有后缀数组,在进行排序,找出相邻两个最长的公共子串(从第一位开始相同的)

例如:abcdeabc

生成后缀数组:【abcdeabc,bcdeabc,cdeabc,deabc,eabc,abc,bc,c】

再排序:【abcdeabc,abc,bcdeabc,bc,cdeabc,c,deabc,eabc】

找出相邻的最长公共子串:【abc,bc,c】

因此,最长的串是abc。

6. 在白板上画出这个项目的整个架构

画了下项目的功能架构什么的。

7. XSS, CSRF,token 怎么来的,sql 注入知道么

sql注入:

攻击方式:服务器上的数据库运行非法的 SQL 语句,主要通过拼接字符串的形式来完成,改变sql语句本身的语义。通过sql语句实现无账号登陆,甚至篡改数据库。

防御

  • 使用参数化查询:使用预编译语句,预先编译的 SQL 语句,并且传入适当参数多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。 使用preparedStatement的参数化sql,通过先确定语义,再传入参数,就不会因为传入的参数改变sql的语义。(通过setInt,setString,setBoolean传入参数)
  • 单引号转换:将传入的参数中的单引号转换为连续两个单引号,PHP 中的 Magic quote 可以完成这个功能。
  • 检查变量数据类型和格式。
  • 使用正则表达式过滤传入的参数,对特殊符号过滤或者转义处理。

8. 怎么设计一个ant的组件

9. 你觉得你实习做的项目有什么改进的地方

10. 你做过印象最深刻的项目

11. 算法了解过吗

就知道一些基本的排序额...

12. Setstate 会发生什么

setState会引发一次组件的更新过程,从而引发页面的重新绘制。主要会涉及以下几个生命周期函数:

  • shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新)
  • componentWillUpdate(被调用时this.state没有更新)
  • render(被调用时this.state得到更新)
  • componentDidUpdate

13. 平时处理过什么兼容性

参考: web前端兼容性问题总结

14. 了解分布式和负载均衡么

然鹅我并不了解呃。

参考: 服务器负载均衡的基本功能和实现原理

五面

第四面确实是挂了,没面hr就让我走了,后面过了两天之后,三面面试官又把我捞起来了,说我计算机基础还有数据库基础不怎么好,然后说问我几个简单的,之后给了我机会面了hr,感谢三面面试官让我体验了阿里的整个面试流程,很满足了。

1. 进程和线程的区别

  • 根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
  • 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
  • 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)。
  • 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
  • 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

2. 冒泡排序和快速排序的区别

简述了下冒泡和快排的思想,以及冒泡和快排的时间复杂度。

3. OSI七层模型以及作用

上面有写噢,不知道的往上翻。

4. 你有哪些优势,或者打动他的

呃,最怕这种自夸的问题额,然后就是夸了一顿,手动捂脸。

5. 面向对象和非面向对象有什么区别

面向对象三大特性:封装,继承,多态。

面向对象的好处:

  • 将对象进行分类,分别封装它们的数据和可以调用的方法,方便了函数、变量、数据的管理,方便方法的调用(减少重复参数等),尤其是在编写大型程序时更有帮助。
  • 用面向对象的编程可以把变量当成对象进行操作,让编程思路更加清晰简洁,而且减少了很多冗余变量的出现

参考: 面向对象(一)|面向对象概念及优点

6. 设计模式有哪些,说下装饰者模式和代理模式

前面有总结,往前翻。

7. 重载和重写有什么区别

方法重写(overriding)

  • 也叫子类的方法覆盖父类的方法,要求返回值、方法名和参数都相同。
  • 子类抛出的异常不能超过父类相应方法抛出的异常。(子类异常不能超出父类异常)
  • 子类方法的的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)。

方法重载(overloading):

重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数。

参考: 方法重载和重写的区别

hr面

  1. 为什么选择前端开发
  2. 什么事情让你最有成就感
  3. 什么让你最有挫败感
  4. 为什么选择阿里
  5. 平时是怎么学习的
  6. 职业发展

百度

二面三面都有手写代码的环节,对于我这种动手能力弱的人来说还是挺吃力。当时提前批投递的是深圳百度,总共只招五个前端,没过也很正常。后面正式批笔试过了,但是要去北京面试,也就直接放弃了。

1. 为什么要用flex布局,align-items和justify-content的区别

传统布局基于盒模型,非常依赖 display属性 、position属性 、float属性。而flex布局更灵活,可以简便、完整、响应式地实现各种页面布局,比如水平垂直居中。

align-items:定义在垂直方向上的对齐方式;

justify-content:定义在水平方向上的对齐方式。

2. webpack是怎么打包的,babel又是什么?

把项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

babel将es6、es7、es8等语法转换成浏览器可识别的es5或es3语法。

3. saas和less不同于普通css的地方

  • 定义变量,可以把反复使用的css属性值定义成变量,然后通过变量名来引用它们,而无需重复书写这一属性值;
  • 嵌套写法,父子关系一目了然;
  • 使用运算符,可以进行样式的计算;
  • 内置一些颜色处理函数用来对颜色值进行处理,例如加亮、变暗、颜色梯度等;
  • 继承:为多个元素定义相同样式的时候,我们可以考虑使用继承的做法;
  • Mixins (混入):有点像是函数或者是宏,当某段 CSS经常需要在多个元素中使用时,可以为这些共用的 CSS 定义一个Mixin,然后只需要在需要引用这些 CSS 地方调用该 Mixin 即可。

4. es 6模块和其他模块不同的地方

对比了一下es6模块和CommonJS模块:

区别CommonJSes6
加载原理第一次加载模块就会执行整个模块,再次用到时,不会执行该模块,而是到缓存中取值。不会缓存运行结果,动态的去被加载的模块中取值,并且变量总是绑定其所在模块。
输出值的拷贝(模块中值的改变不会影响已经加载的值)值的引用(静态分析,动态引用,原来模块值改变会改变加载的值)
加载方式运行时加载(加载整个模块,即模块中的所有接口)编译时加载(只加载需要的接口)
this指向指向当前模块指向undefined
循环加载只输出已经执行的部分,还未执行的部分不会输出遇到模块加载命令import时不会去执行模块,而是生成一个动态的只读引用,等到真正用到时再去模块中取值。只要引用存在,代码就能执行。

5. 有没有用过es6的一些异步处理函数

Promise,generator,async await

6. redux怎么处理异步操作

可以引入Redux-thunk或者redux-promise这种中间件,可以延迟事件的派发。

其中的原理:是因为他们用了applymiddleware()包装了store的dispatch方法,拥有可以处理异步的能力。

7. 为什么reducer要是个纯函数,纯函数是什么?

纯函数:对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。

原因:Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(浅比较)。如果你在reducer内部直接修改旧的state对象的属性值,那么新的state和旧的state将都指向同一个对象。因此Redux认为没有任何改变,返回的state将为旧的state。两个state相同的话,页面就不会重新渲染了。

因为比较两个Javascript对象所有的属性是否相同的的唯一方法是对它们进行深比较。但是深比较在真实的应用当中代价昂贵,因为通常js的对象都很大,同时需要比较的次数很多。因此一个有效的解决方法是作出一个规定:无论何时发生变化时,开发者都要创建一个新的对象,然后将新对象传递出去。同时,当没有任何变化发生时,开发者发送回旧的对象。也就是说,新的对象代表新的state。

8. 高阶函数是什么,怎么去写一个高阶函数

高阶函数:参数值为函数或者返回值为函数。例如map,reduce,filter,sort方法就是高阶函数。

编写高阶函数,就是让函数的参数能够接收别的函数。

9. vue跟react的区别是什么

没有用过vue,所以就只说了vue具有双向绑定,react是单向数据流。

参考: Vue.js与React的全面对比

10. nodejs处理了什么问题

可以处理高并发的I/O,也能与websocket配合,开发长连接的实时交互应用程序。

11. 响应式布局,怎么做移动端适配

使用媒体查询可以实现响应式布局。

移动端适配方案:

(1)meta viewport:让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

width=device-width: 让当前viewport宽度等于设备的宽度

user-scalable=no: 禁止用户缩放

initial-scale=1.0: 设置页面的初始缩放值为不缩放

maximum-scale=1.0: 允许用户的最大缩放值为1.0

minimum-scale=1.0: 允许用户的最小缩放值为1.0

(2)媒体查询(响应式)

(3)动态 rem 方案

参考: 移动端是怎么做适配的?

二面

1. 怎么做一个实时的聊天系统

使用WebSocket和nodejs,《nodejs实战》这本书详细介绍了这个项目。

2. 当消息有延迟的时候,怎么保证消息的正确顺序

每个消息在被创建时,都将被赋予一个全局唯一的、单调递增的、连续的序列号(SerialNumber,SN)。可以通过一个全局计数器来实现这一点。通过比较两个消息的SN,确定其先后顺序。

3. 怎么做一个登陆窗口,input有哪些兼容性

使用form表单。

4. input按钮如何校验

使用正则表达式。

5. 如何实现水平垂直居中,relative、absolute、fixed

我说了三种方式:

(1)使用表格

.container{
  width: 600px;
  height: 600px;
  background: #eee;
  display: table-cell;
  text-align: center;
  vertical-align: middle;
}
.center{
  background: blue;
}

(2)css3的transform属性

.container{
  width: 100%;
  height: 400px;
  background: #eee;
  position: relative;
}
.center{
  background: blue;
  position:absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

(3)flex布局

.container{
  width: 100%;
  height: 400px;
  background: #eee;
  display: flex;
  justify-content: center;
  align-items: center;
}
.center{
  width: 100px;
  height: 100px;
  background: blue;
  text-align: center;
}
  • relative:相对于自己的定位;
  • absolute:相对于最近一级定位元素的定位;
  • fixed:相对于窗口的定位。

6. 写一个函数,1s之后依次输出1,2,3,4,5

直接使用了let和定时器。

for(let i = 1 ; i < 6; i++){
    setTimeout(() => {
        conosle.log(i)
    }, 1000)
}

7. 事件队列(宏任务、微任务)

参考::这一次,彻底弄懂 JavaScript 执行机制

8. 如何每隔三个数加一个逗号,还要考虑小数点的情况

这道题就是大疆的笔试题,当时候笔试题也是瞎写的,后面也没仔细看,没想到又成了一道面试题。

function transform(number){
    var num = number.toString() 
    var numArr = num.split('.')
    var [num, dotNum] = numArr


    var operateNum = num.split('').reverse()
    var result = [], len = operateNum.length
    for(var i = 0; i< len; i++){
         result.push(operateNum[i])
         if(((i+1) % 3 === 0) && (i !== len-1)){
              result.push(',')
        }
    }

    if(dotNum){
         result.reverse().push('.', ...dotNum)
         return result.join('')
    }else{
         return result.reverse().join('')
    }

}

9. webpack有配置过吗?原理知道吗

参考前面。

10. 父子组件如何通信,子组件怎么跟父组件通信?

父组件把state作为props传递给子组件进行通信。

父组件写好state和处理该state的函数,同时将函数名通过props属性值的形式传入子组件,子组件调用父组件的函数,同时引起state变化。

11. 简单说一下pwa

面试的这个部门就是做pwa的,所以说了下自己对pwa的理解。

三面

1. 手写indexOf

function indexOf(str, val){
    var strLen = str.length, valLen = val.length
    for(var i = 0; i < strLen; i++){
        var matchLen = i + valLen
        var matchStr = str.slice(i, matchLen)
        if(matchLen > strLen){
            return -1
        }
        if(matchStr === val){
            return i
        }
    }
    return -1
}

2. 实现 JS 的继承

function A () {
   this.name = 'a';    
}

A.prototype.getName = function () {
    return this.name;
}

function B () {
}

// B 如何继承 A

参考: JS实现继承的几种方式

3. 从url输入到页面显示会有哪些步骤

(1)DNS服务器解析域名,找到对应服务器的IP地址;

(2)和服务器建立TCP三次握手连接;

(3)发送HTTP请求,服务器会根据HTTP请求到数据服务器取出相应的资源,并返回给浏览器;

(4)浏览器处理响应

  • 加载:浏览器对一个html页面的加载顺序是从上而下的。

当加载到外部css文件、图片等资源,浏览器会再发起一次http请求,来获取外部资源。
当加载到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,等待js文件加载、解析完毕才可以恢复html文档的渲染线程。

  • 解析:解析DOM树和CSSDOM树。
  • 渲染:构建渲染树,将DOM树进行可视化表示,将页面呈现给用户。

4. method有哪些方法,分别是什么意思?post和put的区别

post:上传资源

put:修改资源

5. https有几次握手

6. http2比http1好的地方

主要是考察http2的几个特性。

参考:HTTP协议知识点总结

7. 页面刷新不出来,是有哪些问题

可以从第三题的每个步骤进行分析,大概是:

  • 域名不存在,或者ip地址错误
  • 网络问题,不能建立正常的tcp连接
  • 服务器找不到正确的资源

8. 上一次系统性的学习是什么时候,怎么学习的

学习react的时候,看文档、博客,照着网上写了点小项目。

9. 你觉得项目中最自豪的是什么

10. 上家公司有哪些不好的地方

网易

网易是在杭州网易大厦面的,一天面完三轮,然后录用排序,择优录取的吧。我投的是网易考拉,哭唧唧,后面被拒了之后还伤心的卸载了考拉。之后正式批投了杭研,过了笔试,要去武汉面,本来海康也是在武汉面的,考虑到还要住一晚上,有点怕怕,就没去了。

1.css3动画

2. flex布局

3. 实现call

Function.prototype.call2 = function (context) {
    var context = Object(context) || window
    context.fn = this
    var args = []
    for (var i = 1; i < arguments.length; i++) {
        args.push('arguments[' + i +']')
    }

    var res = eval('context.fn(' + args + ')')

    delete context.fn
    return res
}

4. 图片懒加载data-src

5. Promise异步

6. 水平垂直居中

7. 数组有哪些方法,哪些会改变原数组

改变原数组的方法:pop、push、reverse、shift、sort、splice、unshift,以及两个ES6新增的方法copyWithin 和 fill;

不改变原数组(复制):concat、join、slice、toString、toLocaleString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes;

循环遍历:forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。

8. 操作dom有哪些方法

创建:

  createDocumentFragment()    //创建一个DOM片段<br>
  createElement()   //创建一个具体的元素<br>
  createTextNode()   //创建一个文本节点<br>
  

添加:appendChild()

移出:removeChild()

替换:replaceChild()

插入:insertBefore()

复制:cloneNode(true)

查找:

  getElementsByTagName()    //通过标签名称<br>
  getElementsByClassName()    //通过标签名称<br>
  getElementsByName()    //通过元素的Name属性的值<br>
  getElementById()    //通过元素Id,唯一性

9. 左边定宽右边自适应

(1)左盒子左浮动,右盒子width=100%

(2)左盒子左浮动,右盒子margin-left=左盒子宽度

(3)左盒子左浮动,右盒子右浮动,设置calc(100vw-盒子宽度)

(4)父容器设置display=flex,右盒子flex:1

10. 事件代理

利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行。打个比方:一个button对象,本来自己需要监控自身的点击事件,但是自己不来监控这个点击事件,让自己的父节点来监控自己的点击事件。

11. 后端了解么

直接说了不了解,笑哭。

二面

1. 节流和防抖,手写一下代码

(1)防抖:

定义: 合并事件且不会去触发事件,当一定时间内没有触发这个事件时,才真正去触发事件。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。

场景: keydown事件上验证用户名,输入法的联想。

实现:

function debounce(fn, delay) {
    var timer

    return function () {
        var that = this
        var args = arguments

        clearTimeout(timer)
            timer = setTimeout(function () {
            fn.apply(that, args)
        }, delay)
    }
}

(2)节流:

定义: 持续触发事件时,合并一定时间内的事件,在间隔一定时间之后再真正触发事件。每间隔一段时间触发一次。

原理:对处理函数进行延时操作,若设定的延时到来之前,再次触发事件,则清除上一次的延时操作定时器,重新定时。

场景: resize改变布局时,onscroll滚动加载下面的图片时。

实现:

  • 方法一:使用时间戳。

当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。

缺陷:第一次事件会立即执行,停止触发后没办法再激活事件。

function throttle(fn, interval) {
    var previousTime = +new Date()

    return function () {
        var that = this
        var args = arguments
        var now = +new Date()
        if (now - previousTime >= interval) {
            previousTime = now
            fn.apply(that, args)
        }
   }
}
  • 方法二:使用定时器

当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。

缺陷:第一次事件会在n秒后执行,停止触发后依然会再执行一次事件。

function throttle(fn, interval) {
    var timer
    return function (){
        var that = this
        var args = arguments

   if(!timer){
        timer = setTimeout(function () {
            fn.apply(that, args)
            timer = null
         }, interval)
        }
    }
}
  • 方法三:优化

鼠标移入能立刻执行,停止触发的时候还能再执行一次。

var throttle = function(func,delay){
    var timer = null;
    var startTime = Date.now();

    return function(){
        var curTime = Date.now();
        var remaining = delay-(curTime-startTime);
        var context = this;
        var args = arguments;

        clearTimeout(timer);
        if(remaining<=0){
            func.apply(context,args);
            startTime = Date.now();
        }else{
            timer = setTimeout(func,remaining);
        }
    }
}

2. 知道哪些性能优化

3. react为什么比其他要快,虚拟dom知道吗

4. 写过什么组件

5. 平时怎么学习的

6. node,webpack了解么

7. 模块化,commonjs,es6模块

8. redux怎么实现的

hr面

  1. 项目上有哪些难点,项目中学到了什么
  2. 不喜欢跟什么样的人共事
  3. 平时怎么学习
  4. 为什么来杭州
  5. 职业发展

搜狗

搜狗是内推的,面试也很迷,第一面到第二面中间隔了二十几天,然后二面完了也毫无反馈。

一面

1. 说一下项目,整个网络过程,从前端到后台

2. Ajax 底层实现,readystate 有哪些

0-(未初始化)还没有调用send()方法

1-(载入)已调用send()方法,正在发送请求

2-(载入完成)send()方法执行完成,已经接收到全部响应内容

3-(交互)正在解析响应内容

4-(完成)响应内容解析完成,可以在客户端调用了

3. 状态码有哪些,100,307

4. OSI七层模型

5. TCP三次握手

6. SSL握手过程

7. jQuery 有哪些方法

8. display 有哪些属性,说一下flex的属性

9. Es6的async awiat ,generator

10. Map有哪些方法

Map的方法:set, get, has, delete, clear

遍历方法:

keys():返回键名的遍历器。

values():返回键值的遍历器。

entries():返回所有成员的遍历器。

forEach():遍历 Map 的所有成员。

参考: Set 和 Map 数据结构

11. 正则用过吗?exec, 匹配一个手机号

12. css3动画了解吗,怎么写一个loading动画

13. 怎么实现跨域,cors涉及哪些请求字段

14. 编程: 判断两个网络地址是否属于同一个子网掩码

用与运算符就可以了。当时是在牛客网的面试系统上写的,一直AC不出,也是很迷了额。

15. 怎么上传文件

二面

1. 怎么计算在一个页面上的停留时间

方案1:websocket,前端开个长连接,后台统计长连接时间。

方案2:ajax轮询,隔几秒发一个查询,后台记录第一与最后一个查询间隔时间。

方案3: 关闭窗口或者跳转的时候会触发window.onbeforeunload函数,可以在该函数中做处理(有兼容性问题);统计完数据记录到本地cookies中,一段时间后统一发送。

2. 给你一亿个数,是连续的,怎么找出两个不存在的数

用bitmap就能搞定了,存在为1,不存在为0。

3. 一个搜索框的输入联想,会遇到什么问题?如果第一个请求延迟,第二个请求先到,请问怎么处理?

键盘输入太快,每次输入都去联想,需要多次发送请求,会导致用户体验太差,可以使用防抖优化。

在前端做判断,判断此时的值是否与返回的值相同,不同就丢弃,相同就显示在页面。

4. Http的缓存

5. 二维码怎么工作的,扫描pc端的二维码,怎么让pc端登录?

  • pc端随机生成一个含有唯一uid的二维码,并与服务器建立一个长连接;
  • 手机扫描二维码,解析出二维码中的uid,并把这个uid和手机端的用户密码进行绑定,上传给服务器;
  • 服务器获得客户端信息之后,pc端的长连接轮询操作会获得该消息,显示该账号的信息;
  • pc端会再开一个长连接与手机端保持通信,等待手机端确认登陆后,获得服务器授权的token,就可以在pc端登陆进行正常通信了。

6. Promise 做什么的,有哪几种状态

异步处理的,有三个状态:resolve,pending,reject。

7. 项目有哪些难点,怎么处理的

8. 遇到过哪些性能优化

电信IT研发中心

当时听说电信对学历要求很高,本科基本都是211起的,想着自己本科太渣,就直接放弃了网上的笔试。之后电信来了学校宣讲会,跟朋友吃完饭看到了,就去说凑凑热闹,刚好有笔试也就做了。做完之后笔试居然考了最高分,比第二名高出二十分,手动捂脸额。一面完分数也挺高的,有95分,运气爆棚。重点是今年电信开的薪资实在太高了,目前还在纠结选哪个。

1. Xhtml和html的区别

  • XHTML 元素必须被正确地嵌套。
  • XHTML 元素必须被关闭。
  • 标签名必须用小写字母。
  • XHTML 文档必须拥有根元素。

2. 遇到过哪些兼容性问题

3. 浏览器内核有哪些,移动端用的是哪个

  • Trident内核:IE,MaxThon,TT,The Word,360,搜狗浏览器等。[又称为MSHTML]
  • Gecko内核:Netscape6及以上版本,FF,MozillaSuite/SeaMonkey等;
  • Presto内核:Opera7及以上。[Opera内核原为:Presto,现为:Blink]
  • Webkit内核:Safari,Chrome等。[Chrome的:Blink(Webkit的分支)]

对于Android手机而言,使用率最高的就是Webkit内核。

4. 怎么实现标签页的通信

5. Cookie、session,localstorage,sessionstorage

6. React 和jquery 之间的区别,哪个好用

7. 怎么实现继承

8. Es6,es7有哪些特性

9. 怎么跨域

10. Commonjs用的js哪个特性?

因为js之前只能在浏览器运行,为了能让js能在服务器上运行,所以设计了commonjs规范,而且js之前没有模块化的概念。

11. 选择器优先级

12. 伪类知道吗,有哪些

13. 块级元素有哪些,怎么转成行内元素

14. 一个完整的http请求,页面渲染过程,js和css文件怎么渲染

二面

一面问的都很常规的,不知道为啥给了这么高的分。二面的时候三个面试官,总共就问了三个问题,然后就说面试结束了,不超过五分钟。

1. TCP怎么工作的

三次握手

2. OSI七层模型,路由器工作在哪一层?

网络层

3. 平时用什么语言,用过哪些框架

深信服

深信服给的薪资居然比电信还低,而且加班还严重,就直接拒了。

一面

1. 跨域,同源策略,webpack里面有个跨域的方式知道么

2. 怎么把es6转成es5,babel怎么工作的

  • 解析:将代码字符串解析成抽象语法树
  • 变换:对抽象语法树进行变换操作
  • 再建:根据变换后的抽象语法树再生成代码字符串

3. 反向代理知道么,Nginx

4. 继承有哪些方式

5. 怎么实现一个sleep ,手写一个promise

6. 能写一个二叉树么,怎么去遍历

7. 深拷贝怎么写

(1)var new_arr = JSON.parse( JSON.stringify(arr) );

(2)for in 加递归

function isObj(obj) {
//判断是否为对象或者函数,但不是null
    return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}

function deepCopy(obj) {
    let newObj = Array.isArray(obj) ? [] : {}
    for(let key in obj) {
        newObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
    }
    return newObj
}

(3)$.extend()

(4)函数库lodash,提供_.cloneDeep()

8. 在公司除了完成上级交待的任务,还做了什么

9. 怎么实现垂直中间布局

10. Call和apply,哪个性能开销大

在思否上提问了,已有大神回答。

参考: call和apply的哪个性能更好

11. 正则写一个手机号,全局匹配是什么

12. 删除一个数组中的某个数

splice方法

13. 模块化介绍一下,什么是编译时优化

14. 有哪些网络安全名词,怎么防范

15. 平时怎么学习

二面

二面小哥哥问了几个问题之后,就一直跟我介绍深信服内部的一些管理、技术氛围、晋升机制什么的,全程都是笑脸额。

1. git push -u 是什么意思

绑定默认提交的远程版本库,加了参数-u后,以后即可直接用git push 代替git push origin master

2. git rebase解释下

有test和dev两个分支,分别有两个commit,此时执行下列命令:

git checkout test
git rebase dev

以dev为基准将test的提交进行回放,挨个的应用到dev上去,然后test的那些提交就会废弃。 等价于git merge dev。

git merge 和git rebase区别:

  • merge不会修改提交历史,rebase会修改提交历史

  • rebase只应用于本地没有提交的代码,如果应用到已经提交到远程的分支不要应用,不然会非常的麻烦,git merge 可以应用于远程分支。

3. linux命令,怎么打开一个文件

cat abc.txt

4. 你的上级给你review 代码时会提什么建议

5. 怎么看待加班和工作效率

6. get和post分别进行几次数据交互

get请求过程:(2次交互)

  • 浏览器请求tcp连接(第一次握手)   
  • 服务器答应进行tcp连接(第二次握手)   
  • 浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)   
  • 服务器返回200 ok响应。

post请求过程:(3次交互)

  • 浏览器请求tcp连接(第一次握手)   
  • 服务器答应进行tcp连接(第二次握手)   
  • 浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)   
  • 服务器返回100 continue响应   
  • 浏览器开始发送数据   
  • 服务器返回200 ok响应

7. 怎么打断点,如何确定一个结果来自于哪个函数

ThoughtWorks

TW是内推的,做了内推作业后,就面了技术和文化面。技术面是在作业的基础上加两个功能,只写出来一个,后面一个没时间写了,然后就只讲了下思路。

文化面面了快一个小时,听说TW不加班,对女程序员还很友好,挺中意的公司,不过最后还是挂了额。

华为

华为的面试就不多说了,基本不问前端的,进去是随机分岗的。华为的面试阵仗是我见过的最大的,听说要招一万人,在万达那里面的,全是人啊,阔怕。现在正泡在offer池里,估计国庆后发正式offer吧。

二面碰到的是个女面试官,太恐怖了,一直在怼我,最怕碰到女面试官了,惨。

小米

小米是内推的,电话面了一面,国庆后要我去武汉现场面,那会学校刚好有事应该也不会去了。

1. redux主要做什么的,用过redux的一些中间件吗,简单说一下

2. react生命周期说一下,diff算法说一下

3. setstate时会合并修改,是在哪个函数里修改的?宏事件和微事件

setstate是异步更新的,通过一个队列机制实现state的更新,当执行setState时,会将需要更新的state合并后放入状态队列,而不会立即更新,队列可以高效的批量更新state。

4. let、const、var的区别;如果const定义的是个对象,能够修改对象的属性吗?

const实际上保证的并不是变量的值不得改动,而是变量指向的那个指针不得改动,可以给对象添加属性。如果真的想将对象冻结,应该使用Object.freeze方法。

5. Object.freeze和Object.seal的区别

Object.preventExtension:禁止对象添加新属性并保留已有属性;

Object.seal:在一个现有对象上调用 Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false;

Object.freeze:在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false。

6. 说一下防抖,应用场景是什么

7. 快速排序算法说下,基点怎么选?如果一个数组是已经排序好的怎么选基点?

  • 数组元素随机,取固定基准;
  • 数组元素已排序或逆序,取随机基准;
  • 更好的方法:三数取中,选取数组开头,中间和结尾的元素,通过比较,选择中间的值作为快排的基准。

8. 算法的稳定性,冒泡、快排

9. lodash,underscore的库了解么?有哪些方法

10. 整个项目的架构,包括前端、后台、运营

11. sort的底层实现机制,看过源码么?

数组长度<=22时采用插入排序,大于22用快排。

12. 怎么调试bug?打过断点么?如果前端代码被压缩,如何去找到相应元素?

chromre控制台下,在 Scripts 面板下面有个 Pretty print 按钮(这种符号 {}),点击会将压缩 js 文件格式化缩进规整的文件,这时候在设定断点可读性就大大提高了。

写在最后

感谢这一年的学习。另外,公司已签,感谢各位的关注。

其他资料

这些都是我的学习笔记,也可以参考:

查看原文

赞 197 收藏 163 评论 25

jersey 赞了文章 · 2018-10-07

【干货】js 数组详细操作方法及解析合集

前言

在开发中,数组的使用场景非常多,平日中也涉及到很多数组的api/相关操作,一直也没有对这块内容进行一块整理总结,很多时候就算用过几次这个api,在开发中也很容易忘记,还是要谷歌一下。所以就希望对这块内容有一个比较系统性的总结,在这背景下,就有了本篇文章,如果喜欢的话可以点波赞/关注,支持一下,希望大家看完本文可以有所收获。

个人博客了解一下:obkoro1.com

本文篇幅较长,建议点赞保存再看,也便于日后翻阅。


创建一个数组:

    // 字面量方式:
    // 这个方法也是我们最常用的,在初始化数组的时候 相当方便
    var a = [3, 11, 8];  // [3,11,8];
    // 构造器:
    // 实际上 new Array === Array,加不加new 一点影响都没有。
    var a = Array(); // [] 
    var a = Array(3); // [undefined,undefined,undefined]
    var a = Array(3,11,8); // [ 3,11,8 ]

ES6 Array.of() 返回由所有参数值组成的数组

定义:返回由所有参数值组成的数组,如果没有参数,就返回一个空数组。

目的:Array.of() 出现的目的是为了解决上述构造器因参数个数不同,导致的行为有差异的问题。

    let a = Array.of(3, 11, 8); // [3,11,8]
    let a = Array.of(3); // [3]

ES6 Arrary.from() 将两类对象转为真正的数组

定义:用于将两类对象转为真正的数组(不改变原对象,返回新的数组)。

参数:

第一个参数(必需):要转化为真正数组的对象。

第二个参数(可选): 类似数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组。

第三个参数(可选): 用来绑定this。

    // 1. 对象拥有length属性
    let obj = {0: 'a', 1: 'b', 2:'c', length: 3};
    let arr = Array.from(obj); // ['a','b','c'];
    // 2. 部署了 Iterator接口的数据结构 比如:字符串、Set、NodeList对象
    let arr = Array.from('hello'); // ['h','e','l','l','o']
    let arr = Array.from(new Set(['a','b'])); // ['a','b']

方法:

数组原型提供了非常多的方法,这里分为三类来讲,一类会改变原数组的值,一类是不会改变原数组,以及数组的遍历方法。

改变原数组的方法(9个):

    let a = [1,2,3];
    ES5:
     a.splice()/ a.sort() / a.pop()/ a.shift()/  a.push()/ a.unshift()/ a.reverse()
    ES6:
    a.copyWithin() / a.fill

对于这些能够改变原数组的方法,要注意避免在循环遍历中改变原数组的选项,比如: 改变数组的长度,导致遍历的长度出现问题。

splice() 添加/删除数组元素

定义: splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目

语法: array.splice(index,howmany,item1,.....,itemX)

参数:

  1. index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
  2. howmany:必需。要删除的项目数量。如果设置为 0,则不会删除项目。
  3. item1, ..., itemX: 可选。向数组添加的新项目。

返回值: 如果有元素被删除,返回包含被删除项目的新数组。

eg1:删除元素

    let a = [1, 2, 3, 4, 5, 6, 7];
    let item = a.splice(0, 3); // [1,2,3]
    console.log(a); // [4,5,6,7]
    // 从数组下标0开始,删除3个元素
    let item = a.splice(-1, 3); // [7]
    // 从最后一个元素开始删除3个元素,因为最后一个元素,所以只删除了7

eg2: 删除并添加

     let a = [1, 2, 3, 4, 5, 6, 7];
    let item = a.splice(0,3,'添加'); // [1,2,3]
    console.log(a); // ['添加',4,5,6,7]
    // 从数组下标0开始,删除3个元素,并添加元素'添加'
     let b = [1, 2, 3, 4, 5, 6, 7];
    let item = b.splice(-2,3,'添加1','添加2'); // [6,7]
    console.log(b); // [1,2,3,4,5,'添加1','添加2']
    // 从数组最后第二个元素开始,删除3个元素,并添加两个元素'添加1'、'添加2'

eg3: 不删除只添加:

    let a = [1, 2, 3, 4, 5, 6, 7];
    let item = a.splice(0,0,'添加1','添加2'); // [] 没有删除元素,返回空数组
    console.log(a); // ['添加1','添加2',1,2,3,4,5,6,7]
    let b = [1, 2, 3, 4, 5, 6, 7];
    let item = b.splice(-1,0,'添加1','添加2'); // [] 没有删除元素,返回空数组
    console.log(b); // [1,2,3,4,5,6,'添加1','添加2',7] 在最后一个元素的前面添加两个元素

从上述三个栗子可以得出:

  1. 数组如果元素不够,会删除到最后一个元素为止
  2. 操作的元素,包括开始的那个元素
  3. 可以添加很多个元素
  4. 添加是在开始的元素前面添加的

sort() 数组排序

定义: sort()方法对数组元素进行排序,并返回这个数组。

参数可选: 规定排序顺序的比较函数。

默认情况下sort()方法没有传比较函数的话,默认按字母升序,如果不是元素不是字符串的话,会调用toString()方法将元素转化为字符串的Unicode(万国码)位点,然后再比较字符。

    // 字符串排列 看起来很正常
    var a = ["Banana", "Orange", "Apple", "Mango"];
    a.sort(); // ["Apple","Banana","Mango","Orange"]
    // 数字排序的时候 因为转换成Unicode字符串之后,有些数字会比较大会排在后面 这显然不是我们想要的
    var    a = [10, 1, 3, 20,25,8];
    console.log(a.sort()) // [1,10,20,25,3,8];

比较函数的两个参数:

sort的比较函数有两个默认参数,要在函数中接收这两个参数,这两个参数是数组中两个要比较的元素,通常我们用 a 和 b 接收两个将要比较的元素:

  • 若比较函数返回值<0,那么a将排到b的前面;
  • 若比较函数返回值=0,那么a 和 b 相对位置不变;
  • 若比较函数返回值>0,那么b 排在a 将的前面;

对于sort()方法更深层级的内部实现以及处理机制可以看一下这篇文章深入了解javascript的sort方法

sort排序常见用法

  1. 数组元素为数字的升序、降序:

    var array = [10, 1, 3, 4,20,4,25,8];
    // 升序 a-b < 0 a将排到b的前面,按照a的大小来排序的
    // 比如被减数a是10,减数是20 10-20 < 0 被减数a(10)在减数b(20)前面
    array.sort(function(a,b){
    return a-b;
    });
    console.log(array); // [1,3,4,4,8,10,20,25];
    // 降序 被减数和减数调换了 20-10>0 被减数b(20)在减数a(10)的前面
    array.sort(function(a,b){
    return b-a;
    });
    console.log(array); // [25,20,10,8,4,4,3,1];

  2. 数组多条件排序

    var array = [{id:10,age:2},{id:5,age:4},{id:6,age:10},{id:9,age:6},{id:2,age:8},{id:10,age:9}];

    array.sort(function(a,b){
        if(a.id === b.id){// 如果id的值相等,按照age的值降序
            return b.age - a.age
        }else{ // 如果id的值不相等,按照id的值升序
            return a.id - b.id
        }
    })

    // [{"id":2,"age":8},{"id":5,"age":4},{"id":6,"age":10},{"id":9,"age":6},{"id":10,"age":9},{"id":10,"age":2}]

  3. 自定义比较函数,天空才是你的极限

类似的:运用好返回值,我们可以写出任意符合自己需求的比较函数

    var array = [{name:'Koro1'},{name:'Koro1'},{name:'OB'},{name:'Koro1'},{name:'OB'},{name:'OB'}];
    array.sort(function(a,b){
        if(a.name === 'Koro1'){// 如果name是'Koro1' 返回-1 ,-1<0 a排在b的前面
            return -1
        }else{ // 如果不是的话,a排在b的后面
          return 1
        }
    })
    // [{"name":"Koro1"},{"name":"Koro1"},{"name":"Koro1"},{"name":"OB"},{"name":"OB"},{"name":"OB"}] 

pop() 删除一个数组中的最后的一个元素

定义: pop() 方法删除一个数组中的最后的一个元素,并且返回这个元素。

参数: 无。

    let  a =  [1,2,3];
    let item = a.pop();  // 3
    console.log(a); // [1,2]

shift() 删除数组的第一个元素

定义: shift()方法删除数组的第一个元素,并返回这个元素。

参数: 无。

    let  a =  [1,2,3];
    let item = a.shift();  // 1
    console.log(a); // [2,3]

push() 向数组的末尾添加元素

定义:push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

参数: item1, item2, ..., itemX ,要添加到数组末尾的元素

    let  a =  [1,2,3];
    let item = a.push('末尾');  // 4
    console.log(a); // [1,2,3,'末尾']

unshift()

定义:unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

参数: item1, item2, ..., itemX ,要添加到数组开头的元素

    let  a =  [1,2,3];
    let item = a.unshift('开头');  // 4
    console.log(a); // ['开头',1,2,3]

reverse() 颠倒数组中元素的顺序

定义: reverse() 方法用于颠倒数组中元素的顺序。

参数: 无

    let  a =  [1,2,3];
    a.reverse();  
    console.log(a); // [3,2,1]

ES6: copyWithin() 指定位置的成员复制到其他位置

定义: 在当前数组内部,将指定位置的成员复制到其他位置,并返回这个数组。

语法:

    array.copyWithin(target, start = 0, end = this.length)

参数:

三个参数都是数值,如果不是,会自动转为数值.

  1. target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  2. start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
  3. end(可选):到该位置前停止读取数据,默认等于数组长度。使用负数可从数组结尾处规定位置。

浏览器兼容(MDN): chrome 45,Edge 12,Firefox32,Opera 32,Safari 9, IE 不支持

eg:

        // -2相当于3号位,-1相当于4号位
        [1, 2, 3, 4, 5].copyWithin(0, -2, -1)
        // [4, 2, 3, 4, 5]
        var a=['OB1','Koro1','OB2','Koro2','OB3','Koro3','OB4','Koro4','OB5','Koro5']
        // 2位置开始被替换,3位置开始读取要替换的 5位置前面停止替换
        a.copyWithin(2,3,5)
        // ["OB1","Koro1","Koro2","OB3","OB3","Koro3","OB4","Koro4","OB5","Koro5"] 

从上述栗子:

  1. 第一个参数是开始被替换的元素位置
  2. 要替换数据的位置范围:从第二个参数是开始读取的元素,在第三个参数前面一个元素停止读取
  3. 数组的长度不会改变
  4. 读了几个元素就从开始被替换的地方替换几个元素

ES6: fill() 填充数组

定义: 使用给定值,填充一个数组。

参数:

第一个元素(必须): 要填充数组的值

第二个元素(可选): 填充的开始位置,默认值为0

第三个元素(可选):填充的结束位置,默认是为this.length

MDN浏览器兼容

    ['a', 'b', 'c'].fill(7)
    // [7, 7, 7]
    ['a', 'b', 'c'].fill(7, 1, 2)
    // ['a', 7, 'c']

不改变原数组的方法(8个):

    ES5:
    slice、join、toLocateString、toStrigin、cancat、indexOf、lastIndexOf、
    ES7:
    includes

slice() 浅拷贝数组的元素

定义: 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象,且原数组不会被修改。

注意:字符串也有一个slice() 方法是用来提取字符串的,不要弄混了。

语法:

    array.slice(begin, end);

参数:

begin(可选): 索引数值,接受负值,从该索引处开始提取原数组中的元素,默认值为0。

end(可选):索引数值(不包括),接受负值,在该索引处前结束提取原数组元素,默认值为数组末尾(包括最后一个元素)。

    let a= ['hello','world'];
    let b=a.slice(0,1); // ['hello']
    a[0]='改变原数组';
    console.log(a,b); // ['改变原数组','world'] ['hello']
    b[0]='改变拷贝的数组';
     console.log(a,b); // ['改变原数组','world'] ['改变拷贝的数组']

如上:新数组是浅拷贝的,元素是简单数据类型,改变之后不会互相干扰

如果是复杂数据类型(对象,数组)的话,改变其中一个,另外一个也会改变

    let a= [{name:'OBKoro1'}];
    let b=a.slice();
    console.log(b,a); // [{"name":"OBKoro1"}]  [{"name":"OBKoro1"}]
    // a[0].name='改变原数组';
    // console.log(b,a); // [{"name":"改变原数组"}] [{"name":"改变原数组"}]
    // b[0].name='改变拷贝数组',b[0].koro='改变拷贝数组';
    //  [{"name":"改变拷贝数组","koro":"改变拷贝数组"}] [{"name":"改变拷贝数组","koro":"改变拷贝数组"}]

原因在定义上面说过了的:slice()是浅拷贝,对于复杂的数据类型浅拷贝,拷贝的只是指向原数组的指针,所以无论改变原数组,还是浅拷贝的数组,都是改变原数组的数据。

join() 数组转字符串

定义: join() 方法用于把数组中的所有元素通过指定的分隔符进行分隔放入一个字符串,返回生成的字符串。

语法:

    array.join(str)

参数:

str(可选): 指定要使用的分隔符,默认使用逗号作为分隔符。

    let a= ['hello','world'];
    let str=a.join(); // 'hello,world'
    let str2=a.join('+'); // 'hello+world'

使用join方法或者下文说到的toString方法时,当数组中的元素也是数组或者是对象时会出现什么情况?

    let a= [['OBKoro1','23'],'test'];
    let str1=a.join(); // OBKoro1,23,test
    let b= [{name:'OBKoro1',age:'23'},'test'];
    let str2 = b.join(); // [object Object],test
    // 对象转字符串推荐JSON.stringify(obj);

所以,join()/toString()方法在数组元素是数组的时候,会将里面的数组也调用join()/toString(),如果是对象的话,对象会被转为[object Object]字符串。

toLocaleString() 数组转字符串

定义: 返回一个表示数组元素的字符串。该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。

语法:

    array.toLocaleString()

参数:无。

    let a=[{name:'OBKoro1'},23,'abcd',new Date()];
    let str=a.toLocaleString(); // [object Object],23,abcd,2018/5/28 下午1:52:20 

如上述栗子:调用数组的toLocaleString方法,数组中的每个元素都会调用自身的toLocaleString方法,对象调用对象的toLocaleString,Date调用Date的toLocaleString

toString() 数组转字符串 不推荐

定义: toString() 方法可把数组转换为由逗号链接起来的字符串。

语法:

    array.toString()

参数: 无。

该方法的效果和join方法一样,都是用于数组转字符串的,但是与join方法相比没有优势,也不能自定义字符串的分隔符,因此不推荐使用。

值得注意的是:当数组和字符串操作的时候,js 会调用这个方法将数组自动转换成字符串

   let b= [ 'toString','演示'].toString(); // toString,演示
   let a= ['调用toString','连接在我后面']+'啦啦啦'; // 调用toString,连接在我后面啦啦啦

cancat

定义: 方法用于合并两个或多个数组,返回一个新数组。

语法:

    var newArr =oldArray.concat(arrayX,arrayX,......,arrayX)

参数:

arrayX(必须):该参数可以是具体的值,也可以是数组对象。可以是任意多个。

eg1:

    let a = [1, 2, 3];
    let b = [4, 5, 6];
    //连接两个数组
    let newVal=a.concat(b); // [1,2,3,4,5,6]
    // 连接三个数组
    let c = [7, 8, 9]
    let newVal2 = a.concat(b, c); // [1,2,3,4,5,6,7,8,9]
    // 添加元素
    let newVal3 = a.concat('添加元素',b, c,'再加一个'); 
    // [1,2,3,"添加元素",4,5,6,7,8,9,"再加一个"]
   // 合并嵌套数组  会浅拷贝嵌套数组
   let d = [1,2 ];
   let f = [3,[4]];
   let newVal4 = d.concat(f); // [1,2,3,[4]]

ES6扩展运算符...合并数组

因为ES6的语法更简洁易懂,所以现在合并数组我大部分采用...来处理,...运算符可以实现cancat的每个栗子,且更简洁和具有高度自定义数组元素位置的效果。

    let a = [2, 3, 4, 5]
    let b = [ 4,...a, 4, 4]
    console.log(a,b); //  [2, 3, 4, 5] [4,2,3,4,5,4,4]

更多关于扩展符的详细内容移步阮一峰大神的ECMAScript 6 入门

indexOf() 查找数组是否存在某个元素,返回下标

定义: 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

语法:

    array.indexOf(searchElement,fromIndex)

参数:

searchElement(必须):被查找的元素

fromIndex(可选):开始查找的位置(不能大于等于数组的长度,返回-1),接受负值,默认值为0。

严格相等的搜索:

数组的indexOf搜索跟字符串的indexOf不一样,数组的indexOf使用严格相等===搜索元素,即数组元素要完全匹配才能搜索成功。

注意:indexOf()不能识别NaN

eg:

    let a=['啦啦',2,4,24,NaN]
    console.log(a.indexOf('啦'));  // -1 
    console.log(a.indexOf('NaN'));  // -1 
    console.log(a.indexOf('啦啦')); // 0

使用场景:

  1. 数组去重
  2. 根据获取的数组下标执行操作,改变数组中的值等。
  3. 判断是否存在,执行操作。

lastIndexOf() 查找指定元素在数组中的最后一个位置

定义: 方法返回指定元素,在数组中的最后一个的索引,如果不存在则返回 -1。(从数组后面往前查找)

语法:

    arr.lastIndexOf(searchElement,fromIndex)

参数:

searchElement(必须): 被查找的元素

fromIndex(可选): 逆向查找开始位置,默认值数组的长度-1,即查找整个数组。

关于fromIndex有三个规则:

  1. 正值。如果该值大于或等于数组的长度,则整个数组会被查找。
  2. 负值。将其视为从数组末尾向前的偏移。(比如-2,从数组最后第二个元素开始往前查找)
  3. 负值。其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

    let a=['OB',4,'Koro1',1,2,'Koro1',3,4,5,'Koro1']; // 数组长度为10
    // let b=a.lastIndexOf('Koro1',4); // 从下标4开始往前找 返回下标2
    // let b=a.lastIndexOf('Koro1',100); // 大于或数组的长度 查找整个数组 返回9
    // let b=a.lastIndexOf('Koro1',-11); // -1 数组不会被查找
    let b=a.lastIndexOf('Koro1',-9); // 从第二个元素4往前查找,没有找到 返回-1

ES7 includes() 查找数组是否包含某个元素 返回布尔

定义: 返回一个布尔值,表示某个数组是否包含给定的值

语法:

    array.includes(searchElement,fromIndex=0)

参数:

searchElement(必须):被查找的元素

fromIndex(可选):默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。

includes方法是为了弥补indexOf方法的缺陷而出现的:

  1. indexOf方法不能识别NaN
  2. indexOf方法检查是否包含某个值不够语义化,需要判断是否不等于-1,表达不够直观

eg:

    let a=['OB','Koro1',1,NaN];
    // let b=a.includes(NaN); // true 识别NaN
    // let b=a.includes('Koro1',100); // false 超过数组长度 不搜索
    // let b=a.includes('Koro1',-3);  // true 从倒数第三个元素开始搜索 
    // let b=a.includes('Koro1',-100);  // true 负值绝对值超过数组长度,搜索整个数组

兼容性(MDN): chrome47, Firefox 43,Edge 14,Opera 34, Safari 9,IE 未实现。


遍历方法(12个):

js中遍历数组并不会改变原始数组的方法总共有12个:

    ES5:
    forEach、every 、some、 filter、map、reduce、reduceRight、
    ES6:
    find、findIndex、keys、values、entries

关于遍历:

  • 关于遍历的效率,可以看一下这篇详解JS遍历
  • 尽量不要在遍历的时候,修改后面要遍历的值
  • 尽量不要在遍历的时候修改数组的长度(删除/添加)

forEach

定义: 按升序为数组中含有效值的每一项执行一次回调函数。

语法:

    array.forEach(function(currentValue, index, arr), thisValue)

参数:

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

关于forEach()你要知道

  • 无法中途退出循环,只能用return退出本次回调,进行下一次回调。
  • 它总是返回 undefined值,即使你return了一个值。

下面类似语法同样适用这些规则

    1. 对于空数组是不会执行回调函数的
    2. 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数
    3. 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
    4. 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。

eg:

    let a = [1, 2, ,3]; // 最后第二个元素是空的,不会遍历(undefined、null会遍历)
    let obj = { name: 'OBKoro1' };
    let result = a.forEach(function (value, index, array) { 
      a[3] = '改变元素';
      a.push('添加到尾端,不会被遍历')
      console.log(value, 'forEach传递的第一个参数'); // 分别打印 1 ,2 ,改变元素
      console.log(this.name); // OBKoro1 打印三次 this绑定在obj对象上
      // break; // break会报错
      return value; // return只能结束本次回调 会执行下次回调
      console.log('不会执行,因为return 会执行下一次循环回调')
    }, obj);
    console.log(result); // 即使return了一个值,也还是返回undefined
    // 回调函数也接受接头函数写法

every 检测数组所有元素是否都符合判断条件

定义: 方法用于检测数组所有元素是否都符合函数定义的条件

语法:

    array.every(function(currentValue, index, arr), thisValue)

参数:(这几个方法的参数,语法都类似)

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

方法返回值规则:

  1. 如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测。
  2. 如果所有元素都满足条件,则返回 true。=

eg:

    function isBigEnough(element, index, array) { 
      return element >= 10; // 判断数组中的所有元素是否都大于10
    }
    let result = [12, 5, 8, 130, 44].every(isBigEnough);   // false
    let result = [12, 54, 18, 130, 44].every(isBigEnough); // true
    // 接受箭头函数写法 
    [12, 5, 8, 130, 44].every(x => x >= 10); // false
    [12, 54, 18, 130, 44].every(x => x >= 10); // true

some 数组中的是否有满足判断条件的元素

定义:数组中的是否有满足判断条件的元素

语法:

    array.some(function(currentValue, index, arr), thisValue)

参数:(这几个方法的参数,语法都类似)

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

方法返回值规则:

  1. 如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测。
  2. 如果没有满足条件的元素,则返回false

    function isBigEnough(element, index, array) {
    return (element >= 10); //数组中是否有一个元素大于 10
    }
    let result = [2, 5, 8, 1, 4].some(isBigEnough); // false
    let result = [12, 5, 8, 1, 4].some(isBigEnough); // true

filter 过滤原始数组,返回新数组

定义: 返回一个新数组, 其包含通过所提供函数实现的测试的所有元素。

语法:

    let new_array = arr.filter(function(currentValue, index, arr), thisArg)

参数:(这几个方法的参数,语法都类似)

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

eg:

     let a = [32, 33, 16, 40];
    let result = a.filter(function (value, index, array) {
      return value >= 18; // 返回a数组中所有大于18的元素
    });
    console.log(result,a);// [32,33,40] [32,33,16,40]

map 对数组中的每个元素进行处理,返回新的数组

定义:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

语法:

    let new_array = arr.map(function(currentValue, index, arr), thisArg)

参数:(这几个方法的参数,语法都类似)

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

eg:

let a = ['1','2','3','4'];
let result = a.map(function (value, index, array) {
  return value + '新数组的新元素'
});
console.log(result, a); 
// ["1新数组的新元素","2新数组的新元素","3新数组的新元素","4新数组的新元素"] ["1","2","3","4"]

reduce 为数组提供累加器,合并为一个值

定义:reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,最终合并为一个值。

语法:

    array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数:

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. total(必须),初始值, 或者上一次调用回调返回的值
    2. currentValue(必须),数组当前元素的值
    3. index(可选), 当前元素的索引值
    4. arr(可选),数组对象本身

initialValue(可选): 指定第一次回调 的第一个参数。

回调第一次执行时:

  • 如果 initialValue 在调用 reduce 时被提供,那么第一个 total 将等于 initialValue,此时 currentValue 等于数组中的第一个值;
  • 如果 initialValue 未被提供,那么 total 等于数组中的第一个值,currentValue 等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError。
  • 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回。

eg:

    // 数组求和 
    let sum = [0, 1, 2, 3].reduce(function (a, b) {
      return a + b;
    }, 0);
    // 6
    // 将二维数组转化为一维 将数组元素展开
    let flattened = [[0, 1], [2, 3], [4, 5]].reduce(
      (a, b) => a.concat(b),
      []
    );
     // [0, 1, 2, 3, 4, 5]

reduceRight 从右至左累加

这个方法除了与reduce执行方向相反外,其他完全与其一致,请参考上述 reduce 方法介绍。

ES6:find()& findIndex() 根据条件找到数组成员

find()定义:用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined。

findIndex()定义:返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

这两个方法

语法:

    let new_array = arr.find(function(currentValue, index, arr), thisArg)
     let new_array = arr.findIndex(function(currentValue, index, arr), thisArg)

参数:(这几个方法的参数,语法都类似)

function(必须): 数组中每个元素需要调用的函数。

    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身

thisValue(可选): 当执行回调函数时this绑定对象的值,默认值为undefined

这两个方法都可以识别NaN,弥补了indexOf的不足.

eg:

        // find
        let a = [1, 4, -5, 10].find((n) => n < 0); // 返回元素-5
        let b = [1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n));  // 返回元素NaN
        // findIndex
        let a = [1, 4, -5, 10].findIndex((n) => n < 0); // 返回索引2
        let b = [1, 4, -5, 10,NaN].findIndex((n) => Object.is(NaN, n));  // 返回索引4

浏览器兼容(MDN):Chrome 45,Firefox 25,Opera 32, Safari 8, Edge yes,

ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值

定义:三个方法都返回一个新的 Array Iterator 对象,对象根据方法不同包含不同的值。

语法:

    array.keys()
    array.values()
    array.entries()

参数:无。

遍历栗子(摘自ECMAScript 6 入门):

    for (let index of ['a', 'b'].keys()) {
      console.log(index);
    }
    // 0
    // 1
    
    for (let elem of ['a', 'b'].values()) {
      console.log(elem);
    }
    // 'a'
    // 'b'
    
    for (let [index, elem] of ['a', 'b'].entries()) {
      console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"

for..of中如果遍历中途要退出,可以使用break退出循环。

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历:

    let letter = ['a', 'b', 'c'];
    let entries = letter.entries();
    console.log(entries.next().value); // [0, 'a']
    console.log(entries.next().value); // [1, 'b']
    console.log(entries.next().value); // [2, 'c']


entries()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 7.1

keys()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 8,

注意:目前只有Safari 9支持,,其他浏览器未实现,babel转码器也还未实现


结语

呼~终于写好了,断断续续,上班也偷偷划水的写了几天,虽说很辛苦,但是现在对数组操作方法,整体清晰了很多,各个API也理解的更好一点了,收获颇多,文章如有不正确的地方欢迎各位大佬鞭策!希望大家看完可以有所收获,喜欢的话,赶紧点波订阅关注/喜欢。

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

个人blog and 掘金个人主页,如需转载,请放上原文链接并署名。码字不易,感谢支持!本人写文章本着交流记录的心态,写的不好之处,不撕逼,但是欢迎指点。

如果喜欢本文的话,欢迎关注我的订阅号,漫漫技术路,期待未来共同学习成长。

以上2018.5.30

参考资料:

ECMAScript6 入门 数组的扩展

JavaScript Array数组相关汇总

深入了解javascript的sort方法

【深度长文】JavaScript数组所有API全解密

详解JS遍历

判断一个变量是否为数组

在 JavaScript 中,如何求出两个数组的交集和差集?

查看原文

赞 39 收藏 63 评论 4

jersey 赞了文章 · 2018-10-06

阿里、网易、滴滴共十次前端面试碰到的问题

前一段时间一直在不断地面试,无奈个人技术能力有限、项目经验缺乏,最终都没有进入到HR面试环节,全~~挂~~了~~

面试了这么多,结果不是太好,有点儿受打击,也促使我近期静下心来反思自己的问题:哪些技术知识掌握的还不错,哪些还有待提高,哪些是需要去恶补的。

阿里面试了三个部门,都是在二面挂的,网易和滴滴也是各两轮技术面试,加一起就是十次面试经历。在此回忆总结一下,既是给社区朋友的一个参考,反馈社区,更是给自己一个好好的总结。

HTML

  • HTML5新增了哪些内容或API,使用过哪些

  • input和textarea的区别

  • 用一个div模拟textarea的实现

  • 移动设备忽略将页面中的数字识别为电话号码的方法 --- 2017.06.10补充

CSS

  • 左右布局:左边定宽、右边自适应,不少于3种方法

  • CSS3用过哪些新特性

  • BFC、IFC

  • 对栅格的理解

  • (水平)居中有哪些实现方式

  • 1像素边框问题

JavaScript

  • 图片懒加载

  • 实现页面加载进度条

  • 事件委托

  • 实现extend函数

  • 为什么会有跨域的问题以及解决方式

  • jsonp原理、postMessage原理

  • 实现拖拽功能,比如把5个兄弟节点中的最后一个节点拖拽到节点1和节点2之间

  • 动画:setTimeout何时执行,requestAnimationFrame的优点

  • 手写parseInt的实现:要求简单一些,把字符串型的数字转化为真正的数字即可,但不能使用JS原生的字符串转数字的API,比如Number()

  • 编写分页器组件的时候,为了减少服务端查询次数,点击“下一页”怎样能确保还有数据可以加载(请求数据不会为空)?

  • ES6新增了哪些特性,使用过哪些,也有当场看代码说输出结果的

  • JS模块化的实践

  • require.js的实现原理(如果使用过webpack,进一步会问,两者打包的异同及优缺点)

  • promise的实现原理,进一步会问async、await是否使用过

  • 实现gulp的功能

  • 使用前端框架(angular/vue/react)带来哪些好处,相对于使用jQuery

  • vue双向数据绑定的实现

  • 单页应用,如何实现其路由功能

性能优化

  • 项目中使用过哪些优化方法

  • 输入一个URL,Enter之后发生了什么

  • (承上)页面的渲染过程

  • 优化中会提到缓存的问题,问:静态资源或者接口等如何做缓存优化

  • 页面DOM节点太多,会出现什么问题?如何优化?

项目经历

这些大公司招聘都是高级工程师起步,所以对简历上的项目会刨根问底。很多很多问题都是由项目中拓展开的,像优化相关的东西,还有前面提到的require.js、promise、gulp,项目中用到了某项技术,高级工程师的要求是:不仅会用,更要知道其原理。对自己的提醒:项目中用到的技术,不能说完全掌握其原理吧,但大致的实现还是有必要了解一下的。

  • 介绍一下你做的这个项目,进一步细问:整个项目有哪些模块,你主要负责哪些

  • 你在项目中的角色

  • 你在项目中做的最出彩的一个地方

  • 碰到过什么样的困难,怎么解决的

  • (如果你是这个项目的负责人),任务怎么分配的,有没有关注过团队成员的成长问题

  • 前端安全问题:CSRF和XSS

其他

  • 为什么选择做前端(我靠,我都快转前端两年了,还在问这个问题啊...)

  • 你希望进入一个什么样的团队

  • 你有什么问题想问我(面试官)的吗?


前前后后有两个月时间,暂时只回忆起这么多了,如果还有其他的,后期我会补上。

webpack其实也是必问的,由于我说还没使用过webpack,只是了解,写过demo,面试官就没问太深。如果你的简历中有提到webpack,请提前准备好,比如webpack打包原理、如何写webpack插件等。

面试阿里云那个岗位的时候,有要求算法和数据结构,有能力者多多准备吧。

阿里、网易的面试几乎都是围绕项目展开的,所以提醒自己搬砖的时候多想想、多看看,多站在一个高度去看整个项目:用到什么技术,技术实现原理是什么,项目框架怎么搭建的,采取安全措施了吗...


后记

有几个岗位感觉就是挂在了项目上。自己做过一个前后端分离项目,但是经过几次面试,发现这个项目还存在某些问题,比如:整个登录注册系统是不完善的,关于权限的处理上甚至是有很大缺陷的;这个项目的node层只是起到构建前端项目(gulp)、渲染index.ejs、代理转发api接口等作用,但是面试官指出说你这个node也太简单了,导致我都在怀疑这是个假的前后端分离...还是需要大神带多见见世面啊,求带...

虽然五次面试都没成功,但自己也收获了很多很多:认识了大牛hb,一个超有文艺气息的资深前端;多谢fw大大帮我内推阿里,十分感谢您对我的认可;也见到了平时只能在视频上看到的cjf老师,谢谢您的指点;对高级前端工程师所具备的技能有了更清晰的认识;肯定也增加了很多面试经验...

再好好提升一下,打算过段时间重新上阵,也祝自己多点好运气,早日进入心仪的企业,毕竟,当初来杭州的时候就是以网易、阿里为目标的。

查看原文

赞 139 收藏 466 评论 38

jersey 关注了问题 · 2018-08-27

解决关于Object.create()与原型链的面试题?

一:

var obj1 = {name:'one'};
obj2 = Object.create(obj1);
obj2.name = 'two';
console.log(obj1.name);
//one

二:

var obj1 = {prop:{name:'one'}};
obj2 = Object.create(obj1);
obj2.prop.name = 'two';
console.log(obj1.prop.name);
//two

三:

var obj1 = {list:['one','one','one']};
obj2 = Object.create(obj1);
obj2.list[0] = 'two';
console.log(obj1.list[0]);
//two

为什么后面两段代码修改的是原型链上的属性呢?

问题是,为什么二、三中的代码不是像代码一中直接给obj2添加属性,而是修改了原型链上的属性?
求解释下一、二、三的结果?

关注 32 回答 12

jersey 关注了问题 · 2018-08-27

解决一个关于JavaScript中作用域链的问题,prototype 和 __proto__ 的区别?

请看代码

代码一

<script type="text/javascript">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.__proto__ = animal;
var tidy = new dog();

console.log(dog.price) //2000
console.log(tidy.price) // undefined

</script>

代码二

<script type="">

var animal = function(){};
var dog = function(){};

animal.price = 2000;
dog.prototype = animal;
var tidy = new dog();

console.log(dog.price) //undefined
console.log(tidy.price) // 2000

</script>

为什么结果是相反的?

JavaScript中基于原型链的继承是怎么样的?

关注 19 回答 5

jersey 赞了回答 · 2018-01-22

解决this.refs 和 ReactDOM.findDOMNode 区别是什么?

先看ref,第一点就有问题,官方文档说的很清楚,ref添加到Compoennt上获取的是Compoennt实例,添加到原生HTML上获取的是DOM

clipboard.png

再看个demo

class Child extends React.Component {
  render() {
    return <p>child</p>
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <div ref="div">app</div>
        <Child ref="child"/>
        <button onClick={()=> {
          console.log(this.refs)
        }}>log refs
        </button>
      </div>

    )
  }
}

新版本的React已经不推荐我们使用ref string转而使用ref callback

class App extends React.Component {
  render() {
    return (
      <div>
        <div ref={div => {
          this._div = div
        }}>app
        </div>
        <Child ref={child => this._child = child}/>
        <button onClick={()=> {
          console.log(this._div);
          console.log(this._child);
        }}>log refs
        </button>
      </div>

    )
  }
}

在说ReactDOM.findDOMNode,当参数是DOM,返回值就是该DOM(这个没啥卵用);当参数是Component获取的是该Component render方法中的DOM

class App extends React.Component {
  render() {
    return (
      <div>
        <div ref={div => {
          this._div = div
        }}>app
        </div>
        <Child ref={child => this._child = child}/>
        <button onClick={()=> {
          console.log(ReactDOM.findDOMNode(this._div) === this._div);
          console.log(ReactDOM.findDOMNode(this._child));
        }}>log refs
        </button>
      </div>

    )
  }
}

关注 9 回答 4

jersey 赞了文章 · 2018-01-08

如何优雅地使用 VSCode 来编辑 vue 文件?

注:本文首发在 我的个人博客

最近有个项目使用了 Vue.js ,本来一直使用的是 PHPStorm 来进行开发,可是遇到了很多问题。

后来,果断放弃收费的 PHPStorm ,改用 vscode (Visual Stdio Code).
当然 vscode 对 vue 也不是原生支持的,今天来扒一扒如何配置 vscode 以便优雅地编辑 vue 文件

先来扒一扒使用 PHPStorm 遇到的问题:

  1. vue文件虽然可以通过插件来解决高亮问题,但是 <script> 标签中的 ES6 代码的识别老是出问题,箭头函数有的时候能正确识别,有的时候会报错

  2. 无法正确识别 vue 文件中的 jsx 语法

  3. 无法正确识别和高亮 vue 文件 <style> 标签中使用的 less 语法

  4. vue文件中 <template> 部分使用了大量的自定义标签(自定义组件)和自定义属性,会报一堆 warning

  5. 经常性卡死

  6. webpack实时编译的错误不能直接展示在代码编辑器内,还得自己到控制台中查看

如何安装 vscode

很简单,传送门:官网下载安装

第一步,要支持 vue 文件的基本语法高亮

这里,我试过好3个插件: vue, VueHelpervetur ,最终选择使用 vetur

安装插件: Ctrl + P 然后输入 ext install vetur 然后回车点安装即可。

p.s: vscode 的插件安装比 PHPStorm 的插件安装更快捷方便,安装完成后还不用重启整个程序,只要重新加载下工作区窗口就可以了。

安装完 vetur 后还需要加上这样一段配置下:

"emmet.syntaxProfiles": {
  "vue-html": "html",
  "vue": "html"
}

这时可以打开一个vue文件试试,注意下右下角状态栏是否正确识别为 vue 类型:

如果被识别为 texthtml ,则记得要点击切换下。

第二步,要支持 vue 文件的 ESLint

可能还有人会问为什么要 ESLint ?没有 lint 的代码虽然也可能可以正确运行,但是 lint 作为编译前的一道检测成本更小,而且更快。此外, ESLint 还有很多规范是帮助我们写出更加优雅而不容易出错的代码的。

jshint 本来也是个不错的选择,但是 ESLint 对 jsx 的支持让我还是选择了 ESLint.

安装插件: Ctrl + P 然后输入 ext install eslint 然后回车点安装即可。

ESLint 不是安装后就可以用的,还需要一些环境和配置:

首先,需要全局的 ESLint , 如果没有安装可以使用 npm install -g eslint 来安装。

其次,vue文件是类 HTML 的文件,为了支持对 vue 文件的 ESLint ,需要 eslint-plugin-html 这个插件。可以使用 npm install -g eslint-plugin-html 来安装

接着,安装了 HTML 插件后,还需要在 vscode 中配置下 ESLint:

    "eslint.validate": [
        "javascript",
        "javascriptreact",
        "html",
        "vue"
    ],
    "eslint.options": {
        "plugins": ["html"]
    },

最后,别忘了在项目根目录下创建 .eslintrc.json , 如果还没创建,还可以使用下面快捷命令来创建:

这样一来 vue 中写的 js 代码也能正确地被 lint 了。

要是不小心少个括号之类的都可以有对应的报错:

多余 import 也都能报错:

还是蛮智能的。

第三步,配置构建任务

vue 项目的构建我选择用 webpack ,不过,并不是直接使用命令行下的 webpack 而是使用了 webpack 的 API 写的 node 脚本。 脚本主要有两个,一个是 build/bin/build.js 另一个是 build/bin/watch.js 分别是单次构建和实时构建。

于是乎,对应 vscode 中的 tasks 也是有两个: buildwatch ,简单配置如下:

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    // use `Ctrl+P` and type `task` + SPACE + <taskName> to run a task
    "version": "0.1.0",
    "tasks": [
        {
            "taskName": "build",
            "echoCommand": true,
            "command": "node",
            "args": [
                "build/bin/build.js"
            ],
            "suppressTaskName": true,
            "isBuildCommand": true
        },
        {
            "taskName": "watch",
            "echoCommand": true,
            "command": "node",
            "args": [
                "build/bin/watch.js"
            ],
            "suppressTaskName": true,
            "isBackground": true
        }
    ]
}

这样配置好后,按 Ctrl + Shift + B 即可开始单次构建。 不过单次构建比较慢(要10秒+),一般我都用实时构建:Ctrl + P 然后输入 task watch <回车> 即可开始实时构建。实时构建除了第一次比较慢,其他时候还是非常快的,一般1秒内就可以构建好。

最后,webpack 构建错误提示

webpack 构建失败后一般都会有错误提示,会显示在输出窗口中:

为啥是彩色的? 因为装了 Output Colorizer 这个插件。

当然,这样还是不够方便 -- 实时构建是后台运行的,“输出”窗口一般也都是在后台,每次保存完文件还得点开岂不麻烦。

要是能做到像 ESLint 一样直接把错误标到编辑器上面就好了。真的可以吗?翻了下 vscode 的文档,发现有神奇的 problemMatcher -- 可以对任务输出进行解析,解析出的问题会显示在“问题”窗口中,如果还有文件名行号和列号,则会在源代码编辑窗口中对应的位置标出来。

先放个最终效果:

在这个文件的第32行,import 了一个不存在的模块,这样的错误在 ESLint 中当然是检查不出来的,然而在 webpack 的实时构建中会报错:

这个事情的困难在于两点:

  1. 如何通过 problemMatcher 把这个错误给抓出来?

  2. 如何找到错误对应的行号?(如果可能的话,还有列号)

webpack的错误输出格式并不是完全统一的,而且有些还没有行号 -- 一方面可能是 webpack 的 bug ,另一方面 vue 文件在构建的时候会拆成 template, script 和 style 三个方面进行构建,报错的行号可能对不上。

最终我的解决方案是对 webpack 的错误重新格式化输出,然后匹配:

首先,重新格式化输出需要 format-webpack-stats-errors-warnings 这个包(偶新写的)

npm install --save-dev format-webpack-stats-errors-warnings

然后,到 build/bin/build.jsbuild/bin/watch.js 中在 webpack 构建完成的回调函数中增加这个格式化后的输出:

更多使用介绍见 github

最后,在 .vscode/tasks.json 中,每个任务下添加 problemWatcher:

// ...
{
    "taskName": "build",
    // ...
    // build 任务的:
    "problemMatcher": {
        "owner": "webpack",
        "fileLocation": [
            "relative",
            "${workspaceRoot}"
        ],
        "pattern": {
            "regexp": "^!>(\\w+): (\\S+)?:(\\d+),(\\d+)(?:~(?:(\\d+),)?(\\d+))?: (.*)$",
            "severity": 1,
            "file": 2,
            "line": 3,
            "column": 4,
            "endLine": 5,
            "endColumn": 6,
            "message": 7
        }
    }
}

{
    "taskName": "watch",
    // ...
    // watch 任务的:
    "problemMatcher": {
        "owner": "webpack",
        "fileLocation": [
            "relative",
            "${workspaceRoot}"
        ],
        "pattern": {
            "regexp": "^!>(\\w+): (\\S+)?:(\\d+),(\\d+)(?:~(?:(\\d+),)?(\\d+))?: (.*)$",
            "severity": 1,
            "file": 2,
            "line": 3,
            "column": 4,
            "endLine": 5,
            "endColumn": 6,
            "message": 7
        },
        "watching": {
            "activeOnStart": true,
            "beginsPattern": "^\\s*Webpack begin run",
            "endsPattern": "^\\s*Build complete at"
        }
    }
    // ...
}
// ...

注:在 watch 任务中,为了匹配何时开始和何时结束,我在 webpack 构建的 run 和 watch 时增加了一个 console.log('Webpack begin run') 的打印,而在构建完成后增加了一个 console.log("Build complete at ..") 的打印。

OK,终于基本搞定了 vscode ,可以愉快地开发 vue 应用了。

查看原文

赞 43 收藏 147 评论 40

认证与成就

  • 获得 16 次点赞
  • 获得 91 枚徽章 获得 9 枚金徽章, 获得 36 枚银徽章, 获得 46 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2013-09-10
个人主页被 751 人浏览