阅读 11466

五月前端面试报告 | 上海

五月前端面试报告 | 上海

create by db on 2020-5-14 22:37:37
Recently revised in 2020-6-8 22:29:17

前言

 后浪,你摆摊了吗?

 时至6月,新冠影响渐小。

 虽然今年注定是全世界的经济寒冬,但地摊还是要摆的,面试也是也要去的。

 先说下本人的情况:坐标上海,三非本科(非双一流非计算机专业),三年多年前端老菜鸟,小公司在职,技术栈Vue + ElementUI。本来打算年后跳槽的,被疫情逼到了年中。五一开始做面试准备,主要投递渠道是Boss, 历经近一个月时间,先后面试了有七家(不包括电话面试),拿到三家offer。在此向大家做一次面试报告,也是自己的阶段总结。

正文

 先写面试经历及面试问题,后附自己的一些答案。请按需查阅。

公司A

公司情况:

大厂

 只能说我胆子比较肥——刚开始就撞到了新BAT头上。这个是猎头推荐的。做了一些准备,果不其然还是栽了。一面是视频面面试,面试官是非常nice的,会根据个人简历去提一些问题,基础知识较多,要手写代码!要手写代码!要手写代码!

问题:

  1. 三栏布局的实现及优缺点

  2. 文字单行显示/三行显示

  3. 重绘和回流

  4. 手写斐波那契数列及其优化

  5. 查看代码输出,什么是宏任务和微任务,都包括哪些?

  6. 编写javascript深度克隆函数deepClone

  7. Vue路由的两种模式,介绍其原理和优缺点

  8. 编写js事件绑定函数

  const Event = {
    on() {} // 绑定
    off() {} // 解绑
    trigger() {} // 触发事件
};
复制代码
  1. 手写去重函数
 const arr = [1, '1', '1', 'NaN',NaN,'NaN', {a: 1}, '{a: 1}', {a: 1}]
复制代码
  1. Vue里面 mixin/自定义指令/v-if/ diff算法

结果:

 挂了

收获:

 由于准备不足(我菜我不说),加上有一些知识平时没有认真做储备,磕磕巴巴的答完了大部分问题,异步/mixin/diff之类没深入了解过,好在面试官很nice,会一步一步引导你思考——然而还是挂了。

 得到的教训就是:不能光满足于做一个coder(CV工程师?),知其然也要知其所以然,基础很重要,理解很重要,最重要的还是能把你想说话的清晰的表达出来。

公司B

公司情况:

 外包公司的一个业务部门,做的是自己项目,需要中高级前端配合后台实现(本身无前端)

一面:

 由于本部门没有前端,他们从其他部门借了一个前端leader(估计高级)来做面试官。可能因为对他们项目不是特别了解,着重考察基础。

问题:

  1. HTML语义化

  2. CSS3新特性

  3. 重绘和回流

  4. 闭包及其应用

  5. ES6新特性,追问了let、promise、class 等

  6. 简述webpack配置项

  7. 你所知道的排序算法,及其实现方式

  8. Vue组件传参的几种方式

……还有很多关于js对象的一些API之类

二面:

 一面完了就是直接进的二面,二面面试官是本部门的架构师(很有范),主要聊得是项目和一些问题解决方案。

  1. 为什么跳槽

  2. 基于项目需求,如何需求调研,选择合适的框架及方案,详述过程

  3. 做技术为什么考PMP(我简历上有写,比较关心能不能踏实做技术)

  4. 工作中遇到最困难的问题是什么

  5. 有没有带过团队

  6. 以后的职业规划

HR面:

 然后就进入HR面,HR是个身材很棒的小姐姐(带着口罩没看到脸)

  1. 什么时候能入职

  2. 期望薪资多少

……口水话

结果:

 拿到了offer,但是薪资没达到期望值,拒了

收获:

 很有收获的的一次面试经历,增加了见识,也给了我一定的信心。面试毕竟是一个双向选择的过程,能遇到一些值得学习的人,思考一些平时没想过的问题,也是一次不错经历。

公司C

公司情况:

 小公司,做自己项目,有两个前端(都很年青),估计要招个前端leader,但是JD并没有写

面试:

 公司十几个人的规模,是总经理在boss上发的面试邀请,离我住的地方颇近,所以还是很感兴趣的。但是不知道是领导不在还是其他原因,派出两个小朋友做面试官。

 这段面试经历并不是很好。两位面试官他们估计没什么经验,也没有准备好需要问的问题。问的一些话语焉不详,还有就是拿工作中的一些问题来提问构思……

结果:

 也许相互都不是特别满意,没下文了

收获:

 很糟糕的一次面试经历。

公司D

公司情况:

 中型公司,做政府项目

一面:

 同样不是特别愉快的一次面试经历。

 面试定的是下午两点,我提前十分钟到的。然而同一块来的有1个前端,3个测试,6个后台(压力颇大)——他们的HR把所以有人订在了同一时间?!然后前台让大家排排队领个人信息表,现场打印面试题……一波操作20分钟过去了,然后就是半个小时做题,做完了等着被面试。。。

 等到接近四点我才被通知可以面试——来都来了,就面一下咯——出来面试,心态要好。然后最骚的操作来了,我被HR带到一间小办公室,里面只有一台电脑——面试官在远程面试。当时心里一万匹草泥马跑过……但是,来都来了……

问题:

  1. 做下自我介绍

  2. 用户登录流程及权限判定,用户信息存储

  3. 路由跳转,页面如何刷新

  4. 介绍下平时工作内容及流程

  5. 介绍个人工作经历

  6. 发了一个闭包的题看输出

  7. 会不会React

  8. 有什么想问我的

 面完第一件事告诉HR——如果有二面,而且还需要远程的话,烦请提前电话告知(我真有礼貌(⊙o⊙)…)

二面:

 二面是晚上下班在家视频面试的(微信群聊)

 定的8点,面试官迟到十分钟。HR跟我说是老总面试,我以为要聊项目,吹牛B谈理想(一面基本都没怎么问),结果碰到了一个很接地气的老总。

问题:

  1. 说说盒模型类型及区别

  2. div垂直居中

  3. 怎样写一个可拖曳的div,怎样将他拖到其他节点内

  4. Vue生命周期

  5. Vue路由守卫

  6. Vue组件传参

  7. 现场画一个三行三列自适应布局

结果:

 拿到了offer,但是由于对该公司的观感和上班距离原因,拒了

收获:

 很奇葩的一次面试经历,长见识的那种。

公司E

公司情况:

 中大型公司(听过名字那种),做内部项目,JD要求资深

面试:

 很舒适的一次面试(可能我是自虐狂)。

 HR是个大帅哥(很少见男性又很帅的HR),公司很多人,有些嘈杂。面试直接是在门口待客小桌子上进行的。面试官居然老乡,很厉害,问的非常详细,由浅入深——基本上对所有前端知识做了一个梳理。

问题:

  1. HTML语义化

  2. 用过的一些HTML5标签

  3. 和 import的作用及区别
  4. 盒模型分类和区别

  5. 绝对定位和相对定位

  6. BFC定义/作用/触发条件

  7. display的属性

  8. 选择器优先级

  9. 三栏布局

  10. ES6语法:promise/箭头函数/class

  11. 闭包的用法和作用域

  12. 原型链,实现继承的方法

  13. 异步及其解决方案,宏任务和微任务及其流程

  14. 跨域

  15. 实现深拷贝

  16. 实现promise

  17. http的GET和POST区别/状态码

  18. http的GET和POST

  19. Vue生命周期/组件通信/响应式实现/路由模式/路由守卫

  20. webpack的打包原理、常用的loader和plugin,以及一些常用配置

  21. node.js的异步操作

  22. TypeScript的认识

  23. 前端微服务的实现方法

  24. 有没有带过团队

  25. 你最擅长什么领域

 前面回答基本没问题,但到后面webpack、node、微服务那一块,就有些捉襟见肘了。不过面试官很有耐心,每个回答都是记录,不清楚的也很耐心的解释。经过沟通发现,他这几天面了很多人了。。。

 一面面试官对我的评价不错(也许是老乡加成),然而被很遗憾的通知二面面试官没时间。。。

结果:

 挂了

收获:

 很有收获的一次面试,面试官给了很多指导性意见及建议,自己对资深有了更切身的认识,也明确了进一步学习的方向。

公司F

公司情况:

 中小型文创公司

面试:

面试官: 接受996吗?

:请问贵公司招聘预算多少?

结果:

 这是我面试时间最短的一次面试。

收获:

 很感谢面试官的单刀直入,避免了浪费大家时间。

 个人可以接受加班的,赶项目的话义务加班也可以,但我真不想进ICU。

公司G(最终入职)

公司情况:

 创业公司

电话面试:

 面试官应该是看着我博客来面的我,一开始问的是一些基础知识,后来就问项目。问题就不一一列举了,跟之前列的大都重复,毕竟前端知识虽然比较杂,主要知识点还是可以抓住脉络的。

一面:

 现场一面也是没有太多的问题,小姐姐很随和,没问过多关于技术的问题,因为之前电话都聊过了,主要聊得一些就是怎么入行,平时工作遇到的问题,解决方案,平时下班都做什么,个人发展规划,写博客什么感受,最近在学什么,对加班的看法……诸如此类

二面:

 二面是应该是技术总监,主要问的就是关于平时的学习(我自己介绍有说这些),平时的一些学习路径、个人发展规划及期望。

三面:

 三面是公司CEO和HR一块面的,主要问的就是关于项目的问题,个人负责的的模块,关于一些项目选型的问题,还有一些项目上线的流程,临场问题的解决方案,为什么想跳槽,对公司的期待,还有一些公司的介绍。

结果:

 隔天拿到了offer,蛮喜欢公司氛围的,大家都比较随和。比较期待入职。

收获:

 待入职公司也是希望找一个能干活的前端。希望首先将分内工作做好,多接触些其他的业务(类似多端、小程序、app之类),踏踏实实积累沉淀,提升技术,熟悉业务,深入行业,争取做一个斜杠青年。

总结

 以上即是本次面试历程的一些总结了,有详有略,应为有的面试我有录音做复盘,有的不方便或忘记录了,就全靠记忆,可能有的地方有些遗漏,见谅哈。

 通过这几场面试下来,感觉今年的就业压力其实蛮大的。当然,是金子总会发光,人才在哪里都不愁找工作——所以说小伙伴们加油啊——不要怂,盘它!

 个人现在的定级在中级+左右,在上海来说,大厂的薪资相对会高一些,不过对基础及算法的要求也高一些;小公司的话,对算法的要求不高,浏览器原理和JS、ES占的比例较大,框架其次,最好还要有些项目打包经验。

 写这篇博客呢,主要是自己水一篇博客做个阶段总结,如果能帮助到大家是最好了。

建议

 最后对小伙伴们提些建议:

  1. 平时的积累很重要:工作中遇到的一些问题及其解决方式,我们可以记录下来,抽空做个总结。最好就是找个平台写博客,掘金、GitHub、博客园、CSDN都可以,好记性不如烂笔头,自己写一遍比看一遍好使的多。在线博客不进方便自己查阅,面试玩意碰到记不太清的知识点还能说——我博客上有总结过。

  2. 简历不要给自己挖坑:会啥就写啥,写啥就会啥。正常的面试官都会根据简历来提问,所以简历上不要出现自己不懂的名词——不懂就去查,去背,哪怕不特别理解到时候也能说个一二三来。不然一问三不知真的尴尬。

  3. 面试之前要做准备:面试就是考试,考试我们就得复习。前端的知识体系还是很明朗的——页面、样式、JS、框架、项目打包及构建,先掌握自己拿手的,然后扫除盲点。

  4. 别在一个坑掉进去两次:和面试官切磋,为了了解目前的就业形势。了解并正视自己的不足;当这个面试有些问题你答不上来时,没关系,回去查资料,看视频,做复盘,一定要记住它,下次你就可以游刃有余了。怕忘的话可以录音。

  5. 不要裸辞!不要裸辞!不要裸辞!:重要事情说三遍,如果有房贷车贷的话,裸辞找工作压力真的很大——手里有粮,心里不慌。当然,土豪随意……

  6. 尽量不要把时间线拖太久:一边上班一般找工作是蛮辛苦的,要想请假理由,下班要准备面试。所以要一鼓作气,尽量突击半个月多拿几家offer,时间线拉太长可能会懈怠(我就是拖得有些久了)。尽量有选择则的、找匹配(薪资、能力、通勤之类)的公司去面试,决不能接到面试邀请就去,不然只是浪费自己的时间。

  7. 端正心态,坚持学习:技术日新月异,框架层出不穷。既然选择了这个行业,就要端正心态,努力学习,提升技术,熟悉业务,深入行业,别让后浪拍死。

附录

参考文档

五月中级前端面试报告 | 宅神king - 掘金

1. 三栏布局的实现及优缺点

布局方案 实现 优点 缺点
Float布局 左右中三列,左列左浮动,右列右浮动,中间列设置左右margin 比较简单,兼容性也比较好 浮动元素脱离文档流,使用的时候只需要注意一定要清除浮动。
Position布局 左中右三列(无顺序),根据定位属性去直接设置各个子元素位置 快捷,设置很方便 元素脱离了文档流,后代元素也脱离了文档流,高度未知的时候,会有问题,有效性和可使用性比较差
Table布局 左中右三列,父元素display: table;子元素display: table-cell;居中子元素不设宽度 使用起来方便,兼容性也不存在问题 ①无法设置栏边距;②对seo不友好;③当其中一个单元格高度超出的时候,两侧的单元格也是会跟着一起变高的
Flex布局 左中右三列,父元素display: flex;两侧元素设宽;居中子元素flex: 1; 比较完美 存在IE上兼容性问题,只能支持到IE9以上
Grid布局 左中右三列,父元素display: grid;利用网格实现 最强大和最简单 兼容性不好,IE10+上支持,而且也仅支持部分属性

2. 文字单行显示/三行显示

单行文本溢出隐藏变为...

 p {
    /* 隐藏元素溢出内容 */
    overflow: hidden;
    /* 单行显示 */
    white-space: nowrap;
    /* 溢出显示省略号 */
    text-overflow: ellipsis;
  }
复制代码

多行文本溢出隐藏变为...

  p {
    overflow: hidden;
    /* 将对象作为弹性伸缩盒子模型显示 。 */
    display: -webkit-box;
    /* 限制在一个块元素显示的文本的行数,即行数设置 */
    -webkit-line-clamp: 3;
    /* 规定框从上向下垂直排列子元素 */
    -webkit-box-orient: vertical;
  }
复制代码

3. 重绘和回流

回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(其实我觉得叫重新布局更简单明了些)。每个页面至少需要一次回流,就是在页面第一次加载的时候。

重绘:当render tree中的一些元素需要更新属性,这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

区别

  • 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流

  • 当页面布局和几何属性改变时就需要回流 比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变

  • 回流往往伴随着布局的变化,代价较大:

  • 重绘只是样式的变化,结构不会变化:

4. 手写斐波那契数列及其优化

什么是斐波那契数列: 斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)(n>2,n∈N*), 这个数列从第3项开始,每一项都等于前两项之和

function Fibonacci (n) {
  if ( n <= 1 ) {return 1};
  return Fibonacci(n - 1) + Fibonacci(n - 2);
}
复制代码

这个方法算是代码最少也容易理解,但是当n较大时很快产生栈溢出,引发原因是“调用帧”过多,出现浏览器假死现象。详情参阅函数扩展之尾调用优化——阮一峰

  1. 递归(优化版)
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};
  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
复制代码

此方式是函数尾调用优化之后的写法(默认参数ES6及以后版本支持,ES5请使用常规默认值写法),理解上稍微复杂,但是不会发生栈溢出,推荐使用。

  1. 普通循环版
function Fibonacci3(n) {
  if (n===1 || n===2) {
        return 1;
    }
    var n1 = 1, n2 = 1, sum;
    for (let i = 2; i < n; i++) {
        sum = n1 + n2
        n1 = n2
        n2 = sum
    }
    return sum
}
复制代码

循环版本最好理解,就是给初始值,然后不断的累加即可

  1. 解构赋值版
var Fibonacci4 = function (n) {
  if (n===1 || n===2) {
        return 1;
    }
    let n1 = 1; n2 = 1;
    for (let i = 2; i < n; i++) {
        [n1, n2] = [n2, n1 + n2]
    }
    return n2
}
复制代码

循环版本最好理解,就是给初始值,然后不断的累加即可(用了解构赋值,代码更简洁)。

5. 查看代码输出,什么是宏任务和微任务,都包括哪些?

  setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for( var i=0 ; i<10000 ; i++ ) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);
复制代码

输出:

2
3
5
4
1
复制代码

宏任务

# 浏览器 Node
setTimeout
setInterval
setImmediate x
requestAnimationFrame x

微任务

# 浏览器 Node
process.nextTick x
MutationObserver x
Promise.then catchfinally Async/Await

执行机制

  1. 先执行主线程
  2. 遇到宏队列(macrotask)放到宏队列(macrotask)
  3. 遇到微队列(microtask)放到微队列(microtask)
  4. 主线程执行完毕
  5. 执行微队列(microtask),微队列(microtask)执行完毕
  6. 执行一次宏队列(macrotask)中的一个任务,执行完毕
  7. 执行微队列(microtask),执行完毕
  8. 依次循环。。。

6. 编写javascript深度克隆函数deepClone

// 深拷贝
/**
 * 深拷贝对象,可以正确序列化日期
 * @param {*} obj
 */
export const DEEP_CLONE = function (obj) {
  let objClone = Array.isArray(obj) ? [] : {}
  if (obj && typeof obj === 'object') {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        // 判断ojb子元素是否为对象,如果是,递归复制
        if (obj[key] && typeof obj[key] === 'object') {
          // 深拷贝日期类型
          if (obj[key] instanceof Date) {
            objClone[key] = new Date(obj[key].valueOf())
            // console.log('deepClone', objClone[key])
          } else {
            objClone[key] = DEEP_CLONE(obj[key])
          }
        } else {
          // 如果不是,简单复制
          objClone[key] = obj[key]
        }
      }
    }
  }
  return objClone
}

复制代码

7. Vue路由的两种模式

hash

原理

  • 早期的前端路由的实现就是基于location.hash来实现的,location.hash的值就是URL中#后面的内容 其实现原理就是监听#后面的内容来发起Ajax请求来进行局部更新,而不需要刷新整个页面。
  • 使用hashchange事件来监听 URL 的变化,以下这几种情况改变 URL 都会触发 hashchange 事件:浏览器前进后退改变 URL、a标签改变 URL、window.location改变URL。

优点

兼容低版本浏览器,Angular1.x和Vue默认使用的就是hash路由 只有#符号之前的内容才会包含在请求中被发送到后端,也就是说就算后端没有对路由全覆盖,但是不会返回404错误 hash值的改变,都会在浏览器的访问历史中增加一个记录,所以可以通过浏览器的回退、前进按钮控制hash的切换 会覆盖锚点定位元素的功能

缺点

不太美观,#后面传输的数据复杂的话会出现问题

history

原理

  • history 提供了 pushState 和 replaceState 两个方法来记录路由状态,这两个方法改变 URL 不会引起页面刷新
  • history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或a标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和a标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便。
  • pushState(state, title, url) 和 replaceState(state, title, url)都可以接受三个相同的参数。

优点

使用简单,比较美观

pushState()设置新的URL可以是任意与当前URL同源的URL,而hash只能改变#后面的内容,因此只能设置与当前URL同文档的URL

pushState()设置的URL与当前URL一模一样时也会被添加到历史记录栈中,而hash#后面的内容必须被修改才会被添加到新的记录栈中

pushState()可以通过stateObject参数添加任意类型的数据到记录中,而hash只能添加短字符串 pushState()可额外设置title属性供后续使用

缺点

前端的URL必须和向发送请求后端URL保持一致,否则会报404错误

由于History API的缘故,低版本浏览器有兼容行问题

两种不同使用场景

  • 从上文可见,hash模式下url会带有#,当你希望url更优雅时,可以使用history模式。
  • 当使用history模式时,需要注意在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
  • 当需要兼容低版本的浏览器时,建议使用hash模式。
  • 当需要添加任意类型数据到记录时,可以使用history模式。

8. 编写js事件绑定函数

  const Event = {
    on() {} // 绑定
    off() {} // 解绑
    trigger() {} // 触发事件
};
复制代码
class Event {
  constructor() {
    // 为了查找迅速使用了对象
    this._cache = {};
  }
  // 绑定事件
  on(eventName, callback) {
    /* 为了查找方便和节省空间, 把同一类型事件放到数组中
     *  因为数组是有序的, 逻辑上是队列, 先绑定先触发
     * */
    // 如果有就放入, 没有就新建, 然后再看下是否有放入函数,没有就加入
    let fns = (this._cache[eventName] = this._cache[eventName] || []);
    // 如果事件方法没有的话就放入到字典进去
    if (fns.indexOf(callback === -1)) {
      fns.push(callback);
    }
    return this;
  }
  // 触发事件
  trigger(eventName, data) {
    // 看下字典里有没有这个函数名字, 有的话就触发它
    let fns = this._cache[eventName];
    if (Array.isArray(fns)) {
      // 有的话就对立面的每一个function传入参数data
      fns.forEach(fn => {
        fn(data);
      });
    }
    return this;
  }
  /*解绑, 看下字典里如果有这个事件名字就去
   * 看下要删除什么
   * */
  off(eventName, callback) {
    let fns = this._cache[eventName];
    if (Array.isArray(fns)) {
      if (callback) {
        let index = fns.indexOf(callback);
        if (index !== -1) {
          fns.splice(index, 1);
        }
      } else {
        // 全部清空
        fns.length = 0;
      }
    }
    return this;
  }
}
const event = new Event();
event.on("test", a => {
  console.log(a);
});
event.trigger("thet", "hello world"); // 绑定后就输出

event.off("test");
event.trigger("test", "hello world"); // 解绑后就不显示了

复制代码

9. 手写去重函数

  const arr = [1, '1', '1', 'NaN',NaN,'NaN', {a: 1}, '{a: 1}', {a: 1}]

复制代码
// 利用for嵌套for,然后splice去重(ES5中最常用)
function unique2(arr) {
   for (var i = 0; i < arr.length; i++) {
     for (var j = i + 1; j < arr.length; j++) {
       if (arr[i] == arr[j]) {         //第一个等同于第二个,splice方法删除第二个
         arr.splice(j, 1);
         j--;
       }
     }
   }
   return arr;
 }

  // 利用hasOwnProperty
  function unique7(arr) {
   var obj = {};
   return arr.filter(function (item, index, arr) {
     return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
   })
 }


// 利用filter
function unique4(arr) {
   return arr.filter(function (item, index, arr) {
     //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
     return arr.indexOf(item, 0) === index;
   });
 }
复制代码

10. mixin / 自定义指令/ v-if/ v-ssd/ diff算法

11 手写ajax

// 手写ajax
function get(){  
  var req = new XMLHTTPRequest();  
  if(req){  
      req.open("GET", "http://test.com/?keywords=手机", true);  
      req.onreadystatechange = function(){  
          if(req.readyState == 4){  
              if(req.status == 200){  
                  alert("success");  
              }else{  
                  alert("error");  
              }  
          }  
      }  
      req.send();  
  }  
}

复制代码

12 事件绑定函数+事件委托

function delegate(element, eeventType, selector, fn) {
  element.addEventListener(eventType, e => {
    let el = e.target
  })
}

复制代码

13 Promise实例

// Promise
function fn(){ 
  var promise = new Promise(function(resolve,reject){
    // ... some code
    if(/*异步操作成功*/) {
    resolve(value) // 成功调用resolve 往下传递参数 且只接受一个参数
    }else {
    reject(error)  // 失败调用reject  往下传递参数 且只接受一个参数
    }   
  });
  return promise
}

fn.then(function (r) {
  console.log('成功: ' + r);
}).catch(function (reason) {
  console.log('失败: ' + reason);
});
// 全部成功调用
Promise.all([promise1,promise2]).then(sucess1,fail1)
// 有一个成功就调用
Promise.race([promise1,promise2]).then(sucess1,fail1)
复制代码

14 自己实现Promise

class MyPromise{
    constructor(executor) {
        this.status = "pending";     // 初始化状态为pending
        this.value = undefined;      // 初始化返回的成功的结果或者失败的原因
        
        // 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
        let resolve = result => {
            if(this.status !== "pending") return;  // 状态一旦改变,就不会再变
            this.status = "resolved";
            this.value = result;
        }
        
        // 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
        let reject = reason => {
            if(this.status !== "pending") return;
            this.status = "rejected";
            this.value = reason;
        }
        // try、catch捕获异常,如果错误,执行reject方法
        try {
            executor(resolve, reject)
        } catch(err) {
            reject(err)
        }
    }
}

复制代码

15 VueX的原理和使用

16 Vue权限路由实现方式

Vue权限路由实现方式总结

17 js判断对象和数组

判断对象或数组

18 js 中的new()到底做了什么?

19 排序

进阶

20 Vue路由传参--------params和query的区别

背景:项目中需要跨页面传值,如试题id,遇到了刷新后,传的值消失,所以研究了以下两者的区别

  1. params只能用name来引入路由,query用path来引入
  2. params类似于post,query更加类似于我们ajax中get传参,说的再简单一点,前者在浏览器地址栏中不显示参数,后者显示,所以params传值相对安全一些。
  3. 取值用法类似分别是this.route.params.name和this.route.query.name。
  4. params传值一刷新就没了,query传值刷新还存在
this.$router.push({
path:"/detail",
params:{
name:'nameValue',
code:10011
}
});
this.$router.push({
path:'/xxx'
query:{
id:id
}
})
复制代码

总结:少量参数可以用此方法,如果有大量公共数据,可以采用Vuex或本地存储的方式。

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

文档协议

知识共享许可协议
db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。