web面试必知宝典

一、个人简历模版
https://a.lmongo.com/offer/resume.html
二、自我介绍话术
您好,我叫 xxx ,今年 xxx 岁,老家 xxx 。做前端开发已经 xxx 年了,期间换过 xxx 家公司,第一家是一个
xxxx 类型公司,叫 xxxxx ;上一家家叫 xxxx ,上一家公司是自己公司或外包公司,做自己的产品或者做
别人的产品,在上一家公司呆了 xxx( 多长时间 ) ,上一家公司一共 xxx 人,开发团队 xxx 人,一共 xxx 个小
组,我们小组 xxx 人,前端 xxx 人,后端 xxx 人,大概做了 xxx 个项目,上一家公司前端主要是用 xxx
术,后端是用 xxx 技术,从上一家公司离职主要是因为 xxx
三、面试官问题

1. 你从上一家公司离职的原因是什么?

家里有人生病 , 回家需要照顾 为什么不请假呢 ? 因为请的时间比较久 , 所以选择了离职
因为有家人或者女朋友 / 男朋友在这边 , 所以选择来这边发展
公司资金链出现问题 , 你坚持了 3 个月还没有发工资 , 由于经济原因实在撑不住了 , 所以选择离职

2. 你上一家公司在那里?

找自己简历上面写的公司的地址

3. 那你现在住在那里?

北京 - 昌平区 - 天通苑 北京 - 昌平区 - 沙河地铁站附近

4. 你到你上一家公司需要多久,坐什么车,需要走多久,到那个地方下车?

自己去查

5. 你上一家是做什么的?

自己在网上查

6. 上一家公司多少人?或者你们公司技术团队多少人? 前端几个人,后端几个人?

公司具体多少人我记不清了 , 刚好钉钉群或者微信群离职的时候给退了 , 所以现在具体多少人我也不
太清楚 , 但是我们那个开发小组大概个 5 ,1 个前端 ,2 个后端 ,1 个产品 ,1 个测试

7. 上一家公司给你的薪资是多少?

根据自己的需求

8. 你是那个学校毕业的?

你简历上写的学历

9. 你是什么学历?

本科

10. 你的学历是公办还是民办?

我上的这个学校是一个民办的学校

11. 你学的是那个专业?

根据自己办的学历的内容去说

12. 这个专业你都学过哪些课程或者你上大学都学过计算机哪些课程?你学的这些知识你还记得多少?们的校训是什么?

我们的课程分为专业课和非专业课
非专业课 : 马克思主义 大学英语 高等数学
专业课 : 计算机基础 软件工程 数据库原理 c 语音 java asp c#
时间太久很多东西都忘了
校训在网上查一
13. 你为什么选择前端或者你是怎么接触到前端的 ?
我们公司当时也有开发部门 , 当时我和开发部门的领导关系比较好 , 他说让我自学一些技术 , 而且
技术部门的工资比较高 , 所以我当时就转技术了
我家里的亲人或者你哥你姐是做技术的 , 而且工资比较高 , 后来过年在一块的时候 , 他们就让我去
跟着他们学技术 , 然后平时他们会让我帮忙做一些东西 , 后台他们觉得我感觉做的东西还行 , 就帮
忙给我推荐了一家公司
我大学就是计算机专业的 , 所以大学毕业后就做的是开发
从培训机构
14. 你有没有在培训机构培训过 ?
没有
, 我大概两年前在培训机构培训过 , 现在已经工作三年了
15. 你找工作找了多久了 ?
错误回答 : 1 个月 或者 2 几个月
正确回答 : 刚开始找 , 咱们公司是我第一面试的 , 我已经面了两三家 , 目前已经收到了 1 offer
16. 你感觉你的优点是什么 ? 缺点是什么 ?
努力 , 学习能力强 , 有毅力 , 做事比较大气
我还真的没有去思考过这个问题
17. 谈谈你对前端的看法 ?
自己在网上搜相关的资料 , 自己去总结一套话术
18. 你上一家公司用的什么技术 ?
你擅长什么技术就说什么技术
19. 你感觉那一个项目是你最满意的项目 ? 你为什么觉得她是你最满意的项目 ?
我觉得我里面写的项目都挺满意的
可以选一个刚开始用 vue 做的项目 可以选一个最近做的一个项目
20. 那你对我们公司有没有了解 ?
没有 , 您能不能给我介绍一下
, 我在来咱们公司之前 , 我在网上搜索过咱们公司的一些内容
21. 你上一家公司什么类型公司 ? 是外包呢还是自己公司的产品 ?
自己简历上写的公司自己去查
22. 你的项目有没有上线地址 ?
结合实际情况去说 ,
23. 你感觉你上家公司怎么样 ?
公司团队氛围好 , 领导对你特别好 , 不管是在技术或者在其他生活方面都可以给你带来很多的帮助
24. 你在你上家公司的收获是什么 ?
收获了技术和同事 , 领导
四、项目介绍
项目背景
您好 , 我给您说一下最近这个公司做的项目。我去这家公司的时候公司项目刚起步 ( 或者项目已
经开发了一部分了 ), 我去了之后主要是负责项目的切页面以及功能的开发 . 我当时的这个项目小组 ,
前端就我一个人或者两个人 , 后端 3 个人 , 产品和测试各 1 . 我当时做的这个项目的名字叫 x x x, 这是
一个什么样的项目 ( 项目概述 )
项目所使用的技术
在这个项目开发的时候 , 主要用到了 ( 项目所使用的技术栈 )
负责的内容
我在写这个项目的时候主要负责哪些模块 ( 简历上负责模块 )
项目功能
这个项目都有哪些功能 ( 可以是简历上写的功能 , 也可以是简历上没写的功能 , 但是说到的功能必须自
己会 , 并且能说出这个功能的思路 )
项目难点
我在写这个项目的时候主要遇到了哪些难点 ( 自己去总结 )
解决方案
这些难点的解决方式是 ( 自己去总结 )
项目总结
做完这个项目之后 , 我的收获是 ( 自己总结 )
五、面试官问题
1. 你们公司前端几个人 ? 后端几个人 ?
自己去说 , 一般是 1 2 或者 1 3 的一个比例
2. 后端用的语言 ?
Java php python node
3. 你这个项目是前后端分离吗 ?
4. 什么是前后端分离 ? 说说你对前后端分离的理解 ? 什么是前后端分离 ?
什么是前后端不分离 ?
传统开发 ( 前后端不分离 ) 的缺点
前后端分离的优点
5. 你们的接口规范是什么 ? 你知道 RESTFUL ?
自己去查
6. 那你说一下你和后台是如何交互的 ? 或者说你和后台交互的方式有哪些
原生的 ajax
jquery ajax
axios
fetch
websocket
7. ajax 的通信原理是什么或者 axios 的原理是什么 ?
自己去查
8. axios 或者 ajax 或者 vue 中跨域是什么解决的 , 都有哪些方案 ?
为什么要有跨域 ?
跨域限制是服务端的一个行为,当开启对某些域名的访问限制后,只有同域或指定域
下的页面可以调用,这样相对来说更安全,图片也可以防盗链 跨域限制一般只在浏
览器端存在,对于服务端或 OS Android 等客户端是不存在的。
什么时候跨域 ? 介绍同源策略
跨域的方式有哪几种 ?
jsonp
vue 中通过 config.js 在里面的 proxy( 代理 ) 里面进行配置
iframe
node 写一个代理
Cros( 后端允许跨域 )
在项目开发中一般都用那种方式 ?
自己总结
9. 请你说一下 jsonp 的原理 ?
自己查
10. 请你说一下 iframe 是如何跨域的 ?
自己查
11. 你项目的难点是什么 ?
自己去总结
12. 你是怎么解决这些难点的 ?
自己去总结
13. 你这个项目是 H5 还是 app?
根据自己简历写的项目去回答
14. 如果是 app 的话 , 请问你们的这个 app 是那种 app? 是原生 app? 还是混合 app? 还是 webapp?
根据自己找的项目来回答
15. 什么是原生 app?
自己去查
16. 什么是混合 app? 自己去查
17. 什么是 webapp?
自己去查
18. app 是如何打包的 ?
将写好的 vue 项目通过 npm run build 命令进行打包 , 但是打包之前我们需要将路由配置 hash,
vu e.config.js 里面的 publicPath 配置为 ./
打开 hbuilderx, 创建 5+app 项目
保留 manifest.json 文件和 unpackaged.json 文件
vue 打包后的 dist 目录的内容放到 5+app 目录里面
manifest.json 文件里面可以配置打包的一些配置
点击发行 -> 选择云打包 -> 最后生成打包后的链接或者 .apk 这样的一个文件
19. 你们项目是如何开发的 ?
需求 - 原型图 - 流程图 - 设计图 - 接口文档
项目开发前的准备工作
项目使用什么技术开发
先开发的什么 , 后开发的什么
开发完成之后做什么
在公司里面一天
20. 什么是模块化开发 ?
自己去查
21. 项目里都用到了哪些插件 ?
自己总结
22. 项目都用到了哪些技术点 ?
自己去总结
23. 请你说一下你项目当中具体的某几个功能是怎么实现的 ?
自己去总结
24. 你是如何完成第三方登录 ? 或者说一下微信登录和 qq 登录的思路 ?
自己去总结
25. 项目如何上线 ?
不是你负责的 , 公司的项目上线是运维负责的 , 但是你知道项目大概怎么上线的
购买域名以及购买服务器 ( 域名需要认证 , 将近 1 个月的时间 )
购买的服务器上面搭建 nginx 服务器
通过 ftp 工具将我们的项目上传到 nginx 服务器就可以 ( 配置 nginx 跨域 / 代理 )
26. 项目开发了多久 ?
按项目的开发周期
27. 项目开发了几期 ? 每一期都有哪些功能或者优化了哪些部分 ?
自己去总结
28. 你对后端了解多少 ?
使用过 , 但很久了 , 很多东西已经忘了 , 但是可以快速上手
29. 你们项目开发的时候有没有什么代码规范 ? 如果有的话你们的规范是什么 ?
30. 你们这个项目都有了哪些框架 ?
自己总结 31. 你们为什么用 vue 开发 ? 为什么不用 react?
学习成本
踩坑成本
32. 在你写的项目中 , 你有没有封装过哪些模块或者组件 ? 你是如何进行封装的 ?
自己总结
33. 你是如何进行组件话开发的 ? 或者说一下你对最简化开发的理解 ?
自己总结
34. 说一下你在项目中是如何封装组件的 ? 有没有什么需要注意的 ?
自己总结
35. 我们开发组件应该尊重什么原则 ?
你对组件开发的理解以及官方的描述 :
开放性
封闭性
粒度
36. 你觉你这个项目的亮点是什么 ?
代码层面
37. 你最近写的项目写完之后你的收获是什么 ?
自己总结
38. 你一共做过多少个项目 ? 这些项目都有哪些类型的
自己总结
39. 最近做的这个项目 ?
自己根据自己简历写的项目去说
40. 你写的这个项目是 h5 还是 app, 还是 pc , 还是安卓 , 还是 ios?
自己总结
41. 为什么公司要做这个项目 ?
我刚去的时候项目都已经开始了 , 所以我也不太了解
42. 项目的开发周期多久 ?
自己总结
43. 项目的开始时间以及项目的结束时间 ?
自己根据简历上写的内容去总结
44. 项目一共分为多少期 ?
自己总结
45. 这几期每一期做到什么程度 ? 每一期之间的区别是什么 ? 或者描述一些每一期都做了耐饿功能和优
?
自己总结
46. 项目一共有多少个功能 ? 那个功能你觉得最难 ? 为什么 ? 你是怎么解决的 ?
自己总结
47. 项目的开发流程 ?
开会讨论需求 -> 出原型图 - 》流程图 - 〉设计图 - 》编码 - 〉测试 - 》上线
48. 项目是几个人开发的 ?
自己总结 49. 项目各自的分工是什么 ? 你们是怎么划分任务的 ?
我都负责了哪些模块的开发
领导划分任务
50. 公司的版本控制工具用的什么 ?
git svn - gitlab
51. 你们是如何进行多人协同开发的 ?
自己总结
52. 项目开发的前期你都在干什么 ?
自己总结
53. 如果让你重新再做这个项目 , 你觉的哪些 地方还可以再优化 ?
代码层面去分析
54. 你在做这个项目的时候 , 你是如何去排查 bug?
自己总结
55. 你们这个项目前后端是怎么交互的 ?
ajax axios fetch ifrmae
56. 项目是如何上线的 ? 项目上线流程是什么 ?
不是我负责的 , 但是我知道大概的流程
六、 vue 面试题
6.1 . MVC MVP MVVM 模式
MVC
MVC 是应用最广泛的软件架构之一,一般 MVC 分为:
Model
模型 ) Controller
控制器 ) View
视图 )
这主要是基于分层的目的,让彼此的职责分开。 View 一般通过 Controller 来和 Model 进行联系。
Controller Model View 的协调者, View Model 不直接联系。基本联系都是单向的。 1 View 传送指令到 Controller
2 Controller 完成业务逻辑后,要求 Model 改变状态
3 Model 将新的数据发送到 View ,用户得到反馈
MVP
MVP 模式将 Controller 改名为 Presenter ,同时改变了通信方向。
1 、各部分之间的通信,都是双向的。
2 View Model 不发生联系,都通过 Presenter 传递。
3 View 非常薄,不部署任何业务逻辑,称为 " 被动视图 " Passive View ),即没有任何主动性,
Presenter 非常厚,所有逻辑都部署在那里。
MVVM
MVVM 是把 MVC Controller MVP Presenter 改成了 ViewModel View 的变化会自动更新到 ViewModel ViewModel 的变化也会自动同步到 View 上显示。这种自动
同步是因为 ViewModel 中的属性实现了 Observer ,当属性变更时都能触发对应的操作。
6.2 MVVM 模式的优点以及与 MVC 模式的区别
MVVM 模式的优点:
1 、低耦合: 视图( View )可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同
"View" 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
2 、可重用性: 你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。
3 、独立开发: 开发人员可以专注于业务逻辑和数据的开发( ViewModel ),设计人员可以专注于页面设
计。
4 、可测试: 界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。 MVVM MVC 的区别:
mvc mvvm 其实区别并不大。都是一种设计思想。
主要区别
mvc Controller 演变成 mvvm 中的 viewModel
mvvm 通过数据来显示视图层而不是节点操作。
mvvm 主要解决了 : mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
6.3 常见的实现 MVVM 数据绑定的做法有哪些?
实现数据绑定的做法有大致如下几种:
发布者 - 订阅者模式( backbone.js
脏值检查( angular.js
数据劫持( vue.js
1 、发布者 - 订阅者模式 :
一般通过 sub , pub 的方式实现数据和视图的绑定监听,
更新数据方式通常做法是 vm.set('property', value)
这种方式现在毕竟太 low 了,我们更希望通过 vm.property = value 这种方式更新数据,同时自动更
新视图,于是有了下面两种方式。
2 、脏值检查 :
angular.js 是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,
最简单的方式就是通过 setInterval() 定时轮询检测数据变动,
angular 只有在指定的事件触发时进入脏值检测,大致如下:
3 、数据劫持 :
vue.js 则是采用 数据劫持 结合 发布者 - 订阅者 模式的方式,
通过 Object.defineProperty() 来劫持各个属性的 setter getter
在数据变动时发布消息给订阅者,触发相应的监听回调。
1 DOM 事件,譬如用户输入文本,点击按钮等。 ( ng-click )
2 XHR 响应事件 ( $http )
3 、浏览器 Location 变更事件 ( $location )
4 Timer 事件 ( $timeout , $interval )
5 、执行 $digest() $apply() 6.4 Object.defineProperty() 方法的作用是什么?
Object.defineProperty() 方法 会直接在一个对象上定义一个新属性,或者修改一个对象的现
有属性, 并返回这个对象。
语法:
参数说明:
返回值:
针对属性,我们可以给这个属性设置一些特性,比如是否只读不可以写;是否可以被 for..in
Object.keys() 遍历。
给对象的属性添加特性描述,目前提供两种形式:数据描述和存取器描述。
6.5 vue.js 的两个核心是什么?
1 、数据驱动,也叫双向数据绑定。
Vue.js 数据观测原理在技术实现上,利用的是 ES5Object.defineProperty 和存储器属性 : getter
setter (所以只兼容 IE9 及以上版本),可称为基于依赖收集的观测机制。核心是 VM ,即
ViewModel ,保证数据和视图的一致性。
2 、组件系统。
.vue 组件的核心选项 :
1 、模板( template ):模板声明了数据和最终展现给用户的 DOM 之间的映射关系。
2 、初始数据( data ):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状
态。
3 、接受的外部参数 (props) :组件之间通过参数来进行数据的传递和共享。
4 、方法( methods ):对数据的改动操作一般都在组件的方法内进行。
5 、生命周期钩子函数( lifecycle hooks ):一个组件会触发多个生命周期钩子函数,最新 2.0 版本
对于生命周期函数名称改动很大。
6 、私有资源( assets ): Vue.js 当中将用户自定义的指令、过滤器、组件等统称为资源。一个组
件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
等等。
6.6 请详细说下你对 vue 生命周期的理解?
6.6.1 什么是 vue 生命周期?
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载 Dom 、渲染 更新
渲染、销毁等一系列过程,我们称这是 Vue 的生命周期。通俗说就是 Vue 实例从创建到销毁的过程,就
是生命周期。
Object.defineProperty(obj, prop, descriptor)
obj :必需。目标对象
prop :必需。需定义或修改的属性的名字
descriptor :必需。目标属性所拥有的特性
传入函数的对象。即第一个参数 obj 6.6.2 vue 生命周期钩子函数都有哪些?分别是什么意思?
组件通过 new Vue() 创建出来之后会初始化事件和生命周期,然后就会执行 beforeCreate 钩子函
数,这个时候,数据还没有挂载呢,只是一个空壳,无法访问到数据和真实的 dom ,一般不做操作
挂载数据,绑定事件等等,然后执行 created 函数,这个时候已经可以使用到数据,也可以更改数
, 在这里更改数据不会触发 updated 函数,在这里可以在渲染前倒数第二次更改数据的机会,不会
触发其他的钩子函数,一般可以在这里做初始数据的获取
接下来开始找实例或者组件对应的模板,编译模板为虚拟 dom 放入到 render 函数中准备渲染,然
后执行 beforeMount 钩子函数,在这个函数中虚拟 dom 已经创建完成,马上就要渲染 , 在这里也可
以更改数据,不会触发 updated ,在这里可以在渲染前最后一次更改数据的机会,不会触发其他的
钩子函数,一般可以在这里做初始数据的获取下来开始 render ,渲染出真实 dom ,然后执行
mounted 钩子函数,此时,组件已经出现在页面中,数据、真实 dom 都已经处理好了 , 事件都已经
挂载好了,可以在这里操作真实 dom 等事情 ...
当组件或实例的数据更改之后,会立即执行 beforeUpdate ,然后 vue 的虚拟 dom 机制会重新构建
虚拟 dom 与上一次的虚拟 dom 树利用 diff 算法进行对比之后重新渲染,一般不做什么事儿
当更新完成后,执行 updated ,数据已经更改完成, dom 也重新 render 完成,可以操作更新后的虚
dom
经过某种途径调用 $destroy 方法后,立即执行 beforeDestroy ,一般在这里做一些善后工作,例如
清除计时器、清除非指令绑定的事件等等 , 组件的数据绑定、监听 ... 去掉后只剩下 dom 空壳,这个时
候,执行 destroyed ,在这里做善后工作也可以
如果觉得上面的太长 , 也可以如下回答 :
总共分为 8 个阶段创建前 / 后,载入前 / 后,更新前 / 后,销毁前 / 后。
创建前 / 后: 在 beforeCreated 阶段, vue 实例的挂载元素 el 还没有。在 created 阶段 ,vue 实例
的数据对象 data 有了 ,el 还没有 .
载入前 / 后:在 beforeMount 阶段, vue 实例的 $el data 都初始化了,但还是挂载之前为虚拟
dom 节点, data.message 还未替换。在 mounted 阶段, vue 实例挂载完成, data.message
功渲染。
更新前 / 后:当 data 变化时,会触发 beforeUpdate updated 方法。
销毁前 / 后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已
经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在
6.6.3 vue 生命周期的作用是什么?
生命周期中有多个事件钩子,让我们在控制整个 vue 实例的过程时更容易形成好的逻辑
6.6.4 第一次页面加载会触发哪几个钩子?
第一次加载会触发 beforeCreate created beforeMount mounted
6.6.5 简述每个周期具体适合哪些场景 ?
生命周期钩子的一些使用方法:
beforecreate : 可以在这加个 loading 事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束 loading 事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到 DOM 节点 updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 可以做一个确认停止事件的确认框 nextTick : 更新数据后立即操作 dom 6.6.6 created mounted 的区别 ?
created: 在模板渲染成 html 前调用,即通常初始化某些属性值,然后再渲染成视图。
mounted: 在模板渲染成 html 后调用,通常是初始化页面完成后,再对 html dom 节点进行一些需
要的操作。
6.6.7 vue 获取数据在哪个周期函数 ?
看实际情况,一般在 created (或 beforeRouter ) 里面就可以,如果涉及到需要页面加载完成之后
的话就用 mounted
created 的时候,视图中的 html 并没有渲染出来,所以此时如果直接去操作 html dom 节点,一
定找不到相关的元素
而在 mounted 中,由于此时 html 已经渲染出来了,所以可以直接操作 dom 节点,(此时
document.getelementById 即可生效了)
6.7 说一下你对 vue 路由的理解吧
6.7.1 什么是 vue 路由?
“Vue 路由就是指 vue-router, 其中 router 是指根据 url 分配到对应的处理程序 , 所以说路由就是用来解析
URL 以及调用对应的控制器并返回从视图对象中提取好的网页代码给 web 服务器 , 最终返回给客户端。
6.7.2 vue 路由的优点以及缺点是什么?
优点:
不需要每次都从服务器获取,渲染页面更快速
缺点 :
不利于 SEO
使用浏览器前进、后退按键时重新发送请求,未合理利用缓存
单页面无法记住之前滚动的位置
6.7.3 请简单说一下 vue 路由的原理?
Vue 的路由实现: hash 模式 和 history 模式
hash 模式:在浏览器中符号 “#” # 以及 # 后面的字符称之为 hash ,用 window.location.hash 读取;
特点:
hash 虽然在 URL 中,但不被包括在 HTTP 请求中;用来指导浏览器动作,对服务端安全无用,
hash 不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com ,因此对
于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history 模式: history 采用 HTML5 的新特性;且提供了两个新方法: pushState (),
replaceState ()可以对浏览器历史记录栈进行修改,以及 popState 事件的监听到状态变更。
特点 :
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.co
m/items/id 。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。 Vue-Router 官网里
如此描述: 不过这种模式要玩好,还需要后台配置支持 …… 所以呢,你要在服务端增加一个覆
盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html
面,这个页面就是你 app 依赖的页面。 6.7.4 怎么定义 vue-router 的动态路由 ? 如何获取动态路由传过来的值?
定义动态路由:
router 目录下的 index.js 文件中,对 path 属性加上 /:id
获取动态路由传过来的值 :
使用 router 对象的 params.id 获取
6.7.5 请描述 vue-router 路由守卫的作用?
vue-router 的导航钩子,主要用来作用是拦截导航,让他完成跳转或取消。
6.7.6 路由守卫使用的方式有几种?
全局的
单个路由独享的
组件级的
6.7.7 路由守卫的钩子函数都有哪些?分别是什么意思?
vue-router 全局有三个守卫:
router.beforeEach 全局前置守卫 进入路由之前
router.beforeResolve 全局解析守卫 (2.5.0+) beforeRouteEnter 调用之后调用
router.afterEach 全局后置钩子 进入路由之后
组件内的守卫:
beforeRouteEnter
beforeRouteUpdata(2.2 新增 )
beforeRouteLeave
6.7.8 路由守卫钩子函数里面的三个参数分别是什么?
to,from,next 这三个参数:
to from 是将要进入和将要离开的路由对象 , 路由对象指的是平时通过 this.$route 获取到的路
由对象。
next:Function 这个参数是个函数,且必须调用,否则不能进入路由 ( 页面空白 )
next() 进入该路由。
next(false): 取消进入路由, url 地址重置为 from 路由地址 ( 也就是将要离开的路由地址 )
next 跳转新路由,当前的导航被中断,重新开始一个新的导航。
我们可以这样跳转: next('path 地址 ') 或者 next({path:''}) 或者 next({name:''})
且允许设置诸如 replace: true name: 'home' 之类的选项以及你用在 router-link router.push
对象选项。
// 全局获取动态路由传递过来的值
$route.params.id
// 局部或者是在方法内获取
this.$route.params.id 6.7.9 路由守卫的解析流程?
导航被触发
在失活的组件里调用离开守卫
调用全局的 beforeEach 守卫
在重用的组件里调用 beforeRouteUpdate 守卫
在路由配置里调用 beforEnter
解析异步路由组件
在被激活的组件里调用 beforeRouteEnter
调用全局的 beforeResolve 守卫
导航被确认
调用全局的 afterEach 钩子
触发 DOM 更新
在创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数
6.7.10 vue-router 路由传参的方式一共有几种?他们是如何就收传递过来的参数?
三种:
分别是 query params ,动态路由传参
接收:
通过 query 方式传递过来的参数一般是通过 this.$route.query 接收
通过 params 方式传递过来的参数一般是通过 this.$route.params 接收
通过动态路由传参方式传递过来的参数一般是通过 this.$route.params 接收
6.7.11 query 传参和 params 方式的区别是什么?
query 使用 path name 传参跳转都可以,而 params 只能使用 name 传参跳转。
传参跳转页面时, query 不需要再路由上配参数就能在新的页面获取到参数, params 也可以不用
配,但是 params 不在路由配参数的话,当用户刷新当前页面的时候,参数就会消失。
也就是说使用 params 不在路由配参数跳转,只有第一次进入页面参数有效,刷新页面参数就会消
失。
6.7.12 什么是路由懒加载?以及路由懒加载是如何实现的?
按需加载
当打包构建应用时, Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组
件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合 Vue 异步组件 Webpack 代码分割 功能,轻松实现 路由组件的懒加载。
6.8 说一下你对 vuex 的理解 ?
6.8.1 什么是 vuex?
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 。它采用集中式存储管理应用的所有组件的
状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我的个人理解是 vuex 其实就是一个管理数据的工具,通过 vuex 我们可以解决组件之间数据共享的
问题,后期也方便我们管理以及维护
6.8.2 vuex 的优点和缺点是什么?
优点:
解决了非父子组件的消息传递(将数据存放在 state 中)
减少了 AJAX 请求次数,有些情景可以直接从内存中的 state 获取
数据方便管理以及维护
缺点: 小型项目使用的话, vuex 会显得有点繁琐冗余
刷新浏览器, vuex 中的 state 会重新变为初始状态,我们如何要解决这个问题就可能需要用本
地存储或者 vuex 的一个插件
6.8.3 一般什么情况下使用 vuex
官方说的是在大型项目中推荐使用 vuex ,但是我个人的理解是当页面的组件比较多,业务比较复杂
时,数据难以维护,这个时候我一般会使用 vuex
6.8.4 vuex 的原理是什么?
每个 Vuex 应用的本质是 store( 仓库),包含应用中大部分的状态。
state, getters,mutations,actions,module
6.8.5 请你说一下 vuex 的用法?
安装 vuex
src 目录下创建 store 文件夹,在该文件夹内创建 index.js
store 文件夹内的 index.js 文件内引入 vuex
然后在引入 vue
调用 Vue.use() 方法注册 vuex
vuex 进行实例化
进行实例化之后通过 export default 导出
main.js 文件内引入 store 文件夹内的 index.js 文件
挂载到 new Vue 实例上面
初始化 vuex 的状态和属性
6.8.6 你在项目中哪些地方用到了 vuex
登录模块,购物车模块,订单模块,商品模块。。。。
6.8.7 vuex 的运行机制什么?
vue 组件里面,通过 dispatch 来触发 actions 提交修改数据的操作。
然后再通过 actions commit 来触发 mutations 来修改数据。
mutations 接收到 commit 的请求,就会自动通过 Mutate 来修改 state (数据中心里面的数据状态)
里面的数据。
最后由 store 触发每一个调用它的组件的更新
6.8.8 vuex 都有哪些属性?
State Getter Mutation Action Module 五种
state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹 mutations ,使之可以异步。
modules => 模块化 Vuex
6.8.9 你是如何获取 state 的值,如何调用 gettes 里面定义的方法?如何调用 mutations 的方法?如何调
actions 的方法?
state 的值获取的方式有两种:
第一种是组件里面进行获取 this.$store.state. 状态
第二种是在 vuex 内部进行获取
函数参数里面会有一个 state 参数,通过这个 state 参数我们可以直接拿到 state 的值 getters 的方法在组件内调用的话是通过 this.$store.getters 来进行获取,而 getters 的作
用一般是用来获取 state 的值
mutations 的方法在组件内调用时一般是通过 this.$store.commit() 来进行调用,而
mutations 的作用一般是用来改变 state 里面状态,只不过通过同步的方式去改变
actions 的方法在组件内调用的话是通过 this.$store.dispatch() 来进行调用,而 actions
作用一般是用来异步的改变状态, actions 也支持 promise
6.8.10 vuex 里面 module 属性的作用是什么?
module 属性相当于是 vuex 里面的模块化方法, module 属性可以让每一个模块拥有自己的 state
mutation action getters, 使得结构非常清晰,方便管理。
比如:购物车模块,订单模块,商品模块 ... 每个模块都有自己的数据,建立多个模块文件来保存各
自对应模块的数据,最后,在 module 属性里面进行合并
6.8.11 不使用 vuex 会带来什么问题?
可维护性会下降,想修改数据要维护三个地方;
可读性会下降,因为一个组件里的数据,根本就看不出来是从哪来的;
增加耦合,大量的上传派发,会让耦合性大大增加,本来 Vue Component 就是为了减少耦合,现
在这么用,和组件化的初衷相背
6.8.12 Vue.js ajax 请求代码应该写在组件的 methods 中还是 vuex actions 中?
如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex
state 里。
如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入 action 里,方便复用,
并包装成 promise 返回,在调用处用 async await 处理返回的数据。如果不要复用这个请求,那么直
接写在 vue 文件里很方便。
6.8.13 Vuex 中如何异步修改状态?
actions 去异步的改变 state 的状态, mutations 是同步改变状态,调用 actions 内定义的方法,需要通过
this.$store.dispatch(),mutations 方法是通过 this.$store.commit() 来进行调用 , 而在 actions 要调用
mutations 的方法,通过 commit 来进行调用
6.8.14 Vuex actions mutations 的区别?
Action 提交的是 mutation ,而不是直接变更状态。
Action 可以包含任意异步操作
mutations 只能是同步操作
6.8.15 页面刷新后 vuex state 数据丢失怎么解决?
localStorage 或者就是 sessionStorage ,或者借用辅助插 vuex-persistedstate
6.8.16 vuex 怎么知道 state 是通过 mutation 修改还是外部直接修改的?
通过 $watch 监听 mutation commit 函数中 _committing 是否为 true
6.9 请你说一下你对 vue 组件通信的理解?
vue 组件的通信是为了解决组件之间数据传递的问题,分为
父子组件之间的通信
非父子组件的通信 6.10 父组件如何与子组件怎么通信?
父组件将数据绑定在子组件上
子组件通过 props 属性来进行接收, props 的接收方式有两种,分别是数组的方式接收,以及对象
的方式接收,他们两个的不同是对象接收的方式可以设置默认值以及传递过来的类型
6.11 子组件如何与父组件进行通信?
在子组件里用 $emit 向父组件触发一个事件,父组件监听这个事件就行了
6.12 非父子组件之间如何进行通信?
非父子组件之间通信我们可以使用 vuex 或者 event bus ,而这个 event bus 我们把它称之为中央时间
总线, vue 中央事件总线这种方法适用于任何情况的父子组件通信,同级别组件通信,相当于组件
通信间的万金油。但是碰到多人合作时,代码的维护性较低,代码可读性低(这个缺点可以忽
略)。
6.15 除了组件之间的这种通信方式以外,还是什么方式可以让组件的数据进行共
享?
路由, vuex ,本地存储
6.16 props 接收父组件发送过来的数据有几种形式?
两种,一种是数组,另外一种是对象
6.17 非父子组件之间通信的原理是什么?
非父子组件之间通信我们一般使用 event bus ,中央时间总线来进行解决,而中央事件总线的鱼哪
里是通过 vue 实例化之后的对象调用 bus.emit 来进行数据的发送 , 通过 bus.$on 来进行接收
6.18 请描述 vue 的优点是什么?缺点是什么?
vue 的优点 :
简单易用
灵活渐进式
轻量高效
压索之后 20KB 大小
虚拟 DOM
MVVM
数据驱动视图
常规的操作方式都是 DOM
普通的 javascript 数据
组件化
组件化优点
提高开发效率
方便重复使用
简化调试步骤
提升整个项目的可维护性
便于协同开发
vue 的缺点:
VUE 不支持 IE8 6.19 请你描述一下 vue 中让元素隐藏的方式有几种?区别是什么?
v-show v-if
共同点 :
v - if v - show 都是动态显示 DOM 元素。
区别
编译过程: v-if 是 真正 的 条件渲染,因为它会确保在切换过程中条件块内的 事件监听器
子组件 适当地 被销毁 重建 v - show 的元素 始终会 被渲染并保留 DOM v -
show 只是简单地 切换 元素的 CSS 属性 display
编译条件: v-if 是惰性的:如果在初始渲染时条件为假,则什么也不做。直到条件第一
次变为真时,才会开始渲染条件块。 v - show 不管初始条件是什么,元素总是会被渲染,
并且只是简单地基于 CSS 进行切换
性能消耗: v - if 有更高的切换消耗。 v - show 有更高的 初始渲染消耗 `
应用场景: v-if 适合运行时条件很少改变时使用。 v-show 适合频繁切换
6.20 你在 vue 中怎么样让你写的 css 样式只在你当前写的组件中显示?
6.21 请你说一下 keep-alive ?你在哪些地方用过它?
keep - alive 主要用于 保留组件状态 避免重新渲染
比如: 有一个 列表页面 和一个 详情页面 ,那么用户就会经常执行打开 详情 => 返回列表 => 打开详情
样的话 列表 和 详情 都是一个 频率很高 的页面,那么就可以 对列表组件 使用 ` 进行缓存,这样用户每
返回列表 的时候,都能 从缓存中快速渲染 ,而 不是重新渲染 `
6.22 请你说一下在 vue 中如何获取 dom 元素?
ref
6.23 请你说一下 vue 中常用的指令有哪些?
自己总结
6.24 请你说一下为什么使用 key
key 值:用于 管理可复用的元素。因为 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是
从头开始渲染。这么做使 Vue 变得非常快,但是这样也不总是符合实际需求。
6.25 说一下你对 axios 的理解?
什么是 axios
axios 一般什么时候用?
使用场景?
使用的时候遇到过什么问题?
6.26 说一下 axios 如何解决跨域?
自己总结 6.27 请描述 v-model 是什么?以及他的作用?以及他的原理是什么?
v-model 就是 vue 的双向绑定的指令 , 能将页面上控件输入的值同步更新到相关绑定的 data 属性,也
会在更新 data 绑定属性时候,更新页面上输入控件的值
v-model 主要提供了两个功能, view 层输入值影响 data 的属性值, data 属性值发生改变会更新 view
层的数值变化
6.28 请描述 computed watch methods 的区别?以及他们的使用场景?
自己总结
6.29 请你描述一下你对 $nextTick 的理解 ?
nextTick vue 里面提供的一个方法 , dom 更新循环结束之后执行延迟回调 , 在修改数据之后可以使
nextTick, 那么我们可以在回调中获取更新后的 dom ,我们写项目的时候,当时点击按钮要获取
一个元素的内容,但是发现了第二次点击的时候才回去到了,后台在网上查了一下,发现是 vue
步更新队列的问题,后来是通过 $nextTick 解决的
6.30 说一下你对渐进式框架的理解?
就是主张最少,可以只用一部分功能,而不必使用全部,而且可以跟其他框架结合使用,
没有多做职责之外的事
6.31 说一下你对 vue 数据双向绑定的理解?
就是利用了 Object.defineProperty() 这个方法重新定义了对象获取属性 get 和设置属性 set 来操作实
现的
6.32 说一下 vue 单页面和多页面的区别?
单页面就是组件之间来回跳转,跳转速度快,不需要请求数据 缺点:首屏加载慢,跳转快
多页面就是页面之间来回跳转,跳转速度慢,每次跳转都需要向后台请求数据 缺点:首屏加载
快,跳转速度慢
6.33 请你说一下什么是 vue 的过滤器?你在项目中哪些地方使用过过滤器 ?
自己总结
6.34 请你说一下你对 vue 指令的理解?以及他的使用场景? 并描述你在项目中安歇
地方使用过 vue 自定义指令 ?
自己总结
6.35 请你说一下 vue 的核心是什么?
vue 的核心是:数据驱动,组件化开发
数据驱动:
mvvm 模式
组件化开发 :
就是内聚性和藕合度(高内聚,低藕合 ) 6.36 请你说一下 vue jquery 的区别?
jquery 是直接操作 DOM 的而 vue 是操作数据的
vue 做到了数据和视图完全分离,他首先把值和 JS 对象进行绑定,然后在修改 JS 对象的值, vue 框架
就会自动把 DOM 的值进行更新,对数据进行操作不在需要引用相应的 DOM 对象,他们通过 Vue
象实现数据和视图的相互绑定
jquery 则是先使用选择器 ($) 来选取 Dom 对象,然后对 Dom 对象进行操作(如赋值丶取值丶事件绑
定等)
6.37 请你说一下你在 vue 打包项目的时候有没有出现什么问题?你是如何解决的?
自己总结
6.38 请你描述一下 react vue 的区别是什么?
自己总结
6.39 请你说一下如何优化 vue 首屏加载的速度?
自己总结
6.40 请你说一下你对 slot 的理解?
自己总结
6.41 请你描述一下封装 vue 组件的过程?
自己总结
6.42 如果说你在开发项目的时候,后台的接口还没有写完,请问这个时候你一般会
怎么做
自己总结
6.43 vue 如何封装通用组件?
自己总结
6.44 vue 常用的 ui 组件库有哪些?
自己总结
6.45 vue 常用的修饰符一共有哪些?
自己总结
6.46 请你说一下 ajax axios 的区别是什么?
自己总结
6.47 vue 组件如何适配移动端?
自己总结 6.48 说一下在 vue 中如何使用背景图片?
自己总结
6.49 如何解决禁用表单后移动端样式不统一问题?
自己总结
6.50 请你说一下数据双向绑定的原理是什么?
自己总结
6.51 什么是请求拦截,什么响应拦截? 拦截点分别是那几个?
自己总结
七、 ECMAScript6 面试题
7.1 请描述 let const 以及 var 的区别?以及什么是暂时性死区?什么是变量提升?
区别:
let 具有块级作用 不能重复声明 可以重复赋值
const 具有块级作用域 不能重复声明 不能重复赋值
var 全局作用域 可以重复声明 可以重复赋值
暂时性死区:
我个人理解,所谓的暂时性死区就是在会计作用域内使用 let 声明了变量,那么这个变量就不
会受外部的影响,这个我把它理解为暂时性死区。
变量提升:
我个人理解,所谓的变量提升就是为了先事先声明变量,然后在进行赋值
7.2 请说一下你对 es6 的模版字符串的理解?有什么特点?
我个人理解,所谓的模版字符串其实指的是我们拼接字符串的时候,是通过连接符 ”+” 来接的,并且
如果换行等需要使用转义字符,否则就会报错。这样让我们书写十分不便。所以, ES6 中就引入了
模板字符串,帮助我们解决这一问题,并且,在模版字符串内使用 ${} 包裹变量,就是将声明的变
量进行解析
使用方式为 反单引号 可以直接插入变量 可以进行换行们不需要使用转义符号进行换行
7.3 请说一下箭头函数与普通函数的区别?
普通函数是很早就提出的,而箭头函数是 es6 提出的,他们两个在语法上不一样,并在普通函数与
箭头函数他们 this 的指向也不要一样,普通函数内的 this 指向是如果没有绑定事件元素的时候, this
指向的 window ,或者在闭包中 this 指向的也是 window ,如果函数绑定了事件,但并没有产生闭
包,这个 this 指向的是当前调用的事件对象,箭头函数内 this 的指向是父作用域
箭头函数不能使用 arguments ,普通函数可以使用, arguments 是以集合的方式获取函数传递的参
箭头函数不能实例化为构造函数,而普通函数可以进行实例化
7.4 请说一下什么是函数的默认参数?
所谓的函数的默认参数其实指的就是当没有给函数参数进行传参的时候,可以给函数的形参制定默
认值 7.5 请说一下 Object.assign() 的有什么作用
Object.assign() 方法主要是用于将源对象复制到目标对象, Object.assign() 方法有两个参数,第一
个参数的表示目标对象,第二个参数表示源对象。
在项目一般使用 object.assign() 用来对对象进行合并
7.6 请说一下你对 promise 的理解?并说一下 promise 你是如何使用的?
我个人对 promise 的理解是, promise 是异步编程的一种解决方案,他比传统的回调函数加事件更
加合理和强大,目前我用 promise 除了使用他的异步操作外,还使用 promise 在项目中解决了回调
地狱等问题。
接下来,我在说一下 promise 的特点, promise 一共有两个特点 :
对象不受外界影响,并且 promise 一共有三个状态,分别是进行中,成功,或者失败,只有异
步操作的结果,可以决定是哪一种状态,任何其他的操作都无法改变这个状态
一旦状态改变,就不会再变,任何时候都可以得到这个结果, promise 的状态改变只有两种可
能,要么是成功,要么失败。
如果要使用 promise 必须还要对 promise 进行实例化,实例化之后 promise 内有一个回调函数,这
个函数里面有两个参数,分别是 resolve reject ,当我们的状态发生变化的时候,如果是成功则会
通过 resolve 将成功的结果返回出去,如果失败,那么可以通过 reject 将错误的信息也返回出去,我
们可以通过 .then 方法接收返回的是成功的结果,通过 catch 可以接受失败的结果。
promise 常用的方法还有 promise.all 方法,主要是用来将多个实例包装成一个新的实例,以及
promise.rece() 方法
那么在项目中我一般使用 promise 来对 api 接口进行封装,以及一些异步的操作,都会使用到
promise
7.7 请说一下你对 es6 模块化的理解
在说这个 es6 模块化之前,我觉得我个人有必要先介绍什么是模块化,模块化开发是一种管理方
式, = 是一种生产方式,也可以理解为是一种解决方案,一个模块其实就是代表一个实现特定功能
文件,那么在这里大家也可以理解为一个 js 文件就是一个模块,有了模块之后我们就可以方便的使
用别人的代码,想要什么功能,就加载什么模块,但是模块开发需要一定的方案,所以后来慢慢衍
生出了 amd cmd 规范,不过 es6 也给我们提供了一种模块化的方案,分别是 import export ,也
就是模块的导入和导出
amd cmd 规范,其实 amd 规范是浏览器端的一种规范,而 cmd 规范是服务器端模块化的一种规
范,之间 amd 规范的主要代表是 sea.js ,不过 sea.js 目前淘汰了,在前台开发中使用模块化的开
发,我们使用的都是 es6 提供模块化方法,而 cmd 规范目前在 node 中我使用的 cmd 规范
es6 模块的语法是 import export
cmd 规范的语法是 require("") module.export
7.8 请说一下 es5 es6 的区别?
es5 其实指的是 ecmascript 第五个版本
es6 其实指的是 ecmascript 第六个版本
es6 es5 的基础上新增了很多的新特性
7.9 请说一下使用箭头函数应该要注意什么?
自己总结 7.10 请说一下 es6 有哪些新增的特性?
变量的声明方式,分别是 let const
变量的解构赋值
增加了一些字符串与数组的方法,比如:模版字符串, includes 方法, startsWith 方法 ,endsWith
法,还是其他等等,数组方法有 map 方法, filter 方法, foreach 方法, find 方法, findindex() 方法
增加了 symbol 类型
箭头函数,函数参数默认值
promise
模块化
class
async await
set map 数据结构
正则
7.11 请说一下你对 es6 class 类的理解?
所谓的 class 类其实就是 es5 构造函数的语法糖,所谓的语法糖其实就是构造函数的另外一种写法而
7.12 请说一下 Promise reject catch 处理上有什么区别?
reject 是用来抛出异常, catch 是用来处理异常
reject Promise 的方法,而 catch Promise 实例的方法
reject 后的东西,一定会进入 then 中的第二个回调,如果 then 中没有写第二个回调,则进入 catch
网络异常(比如断网),会直接进入 catch 而不会进入 then 的第二个回调
7.13 请说一下什么是深拷贝,什么是浅拷贝?以及如何实现深拷贝与浅拷贝?用
es6 如何实现深拷贝 ?
自己总结
7.14 举一些 ES6 String 字符串类型做的常用升级优化 ?
优化部分:
ES6 新增了字符串模板,在拼接大段字符串时,用反斜杠 ( )` 取代以往的字符串相加的形式,
能保留所有空格和换行,使得字符串拼接看起来更加直观,更加优雅
升级部分:
ES6 String 原型上新增了 includes() 方法,用于取代传统的只能用 indexOf 查找包含字
符的方法 ( indexOf 返回 - 1 表示没查到不如 includes 方法返回 false 更明确,语义更清晰 ),
此外还新增了 startsWith() , endsWith(), padStart() , padEnd() , repeat() 等方法,
可方便的用于查找,补全字符串
7.15 举一些 ES6 Array 数组类型做的常用升级优化?
ES6 Array 原型上新增了 find() 方法,用于取代传统的只能用 indexOf 查找包含数组项目的方法 ,
修复了 indexOf 查找不到 NaN bug([NaN].indexOf(NaN) === - 1) . 此外还新增了
copyWithin() , includes() , fill() , flat() 等方法,可方便的用于字符串的查找,补全 , 转换等 7.16 Map 是什么,有什么作用?
Map ES6 引入的一种类似 Object 的新的数据结构, Map 可以理解为是 Object 的超集,打破了以
传统键值对形式定义对象,对象的 key 不再局限于字符串,也可以是 Object 。可以更加全面的描述对
象的属性
7.17 Set 是什么,有什么作用?
Set ES6 引入的一种类似 Array 的新的数据结构, Set 实例的成员类似于数组 item 成员,区别是
Set 实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重
7.18 Proxy 是什么,有什么作用?
Proxy ES6 新增的一个构造函数,可以理解为 JS 语言的一个代理,用来改变 JS 默认的一些语言行为,包括拦截
默认的 get/set 等底层方法,使得 JS 的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的
get/set 方法,可以轻松地定制自己想要的 key 或者 value 。下面的例子可以看到,随便定义一个 myOwnObj
key`, 都可以变成自己想要的函数
7.19 Class extends 是什么,有什么作用?
ES6 class 可以看作只是一个 ES5 生成实例对象的构造函数的语法糖。它参考了 java 语言,定义
了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。 Class 类可以通过
extends 实现继承。它和 ES5 构造函数的不同点
7.20 常前端代码开发中,有哪些值得用 ES6 去改进的编程优化或者规范?
常用箭头函数来取代 var self = this ; 的做法。
常用 let 取代 var 命令。
常用数组 / 对象的结构赋值来命名变量,结构更清晰,语义更明确,可读性更好
在长字符串多变量组合场合,用模板字符串来取代字符串累加,能取得更好地效果和阅读体验
Class 类取代传统的构造函数,来生成实例化对象
在大型应用开发中,要保持 module 模块化开发思维,分清模块之间的关系,常用 import
export 方法。
7.21 什么是 Babel?
Babel 是一个 JS 编译器,自带一组 ES6 语法转化器,用于转化 JS 代码。
这些转化器让开发者提前使用最新的 JS 语法 (ES6/ES7) ,而不用等浏览器全部兼容
Babel 默认只转换新的 JS 句法 (syntax) ,而不转换新的 API
八、微信小程序面试题
8.1 简单描述下微信小程序的相关文件类型
微信小程序项目结构主要有四个文件类型
WXML WeiXin Markup Language )是框 * ** 设计 * 的一套标签语言,结合基础组件、事件
系统,可以构建出页面的结构。内部主要是微信自己定义的一套组件
WXSS (WeiXin Style Sheets) 是一套样式语言,用于描述 WXML 的组件样式
js 逻辑处理,网络请求
json 小程序设置,如页面注册,页面标题及 tabBar
主要文件
· app.json 必须要有这个文件,如果没有这个文件,项目无法运行,因为微信框架把这个作为
配置文件入口,整个小程序的全局配置。包括页面注册,网络设置,以及小程序的 window 背景色,配置导航条样式,配置默认标题
· app.js 必须要有这个文件,没有也是会报错!但是这个文件创建一下就行 什么都不需要写以
后我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量
· app.wxss 可选
8.2 简述微信小程序原理
微信小程序采用 JavaScript WXML WXSS 三种技术进行开发 , 本质就是一个单页面应用,所有的页面
渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口
微信的架构,是数据驱动的架构模式,它的 UI 和数据是分离的,所有的页面更新,都需要通过对数据的
更改来实现
小程序分为两个部分 webview appService 。其中 webview 主要用来展现 UI appService 有来处理
业务逻辑、数据及接口调用。它们在两个进程中运行,通过系统层 JSBridge 实现通信,实现 UI 的渲
染、事件的处理
8.3 小程序的双向绑定和 vue 哪里不一样
小程序直接 this.data 的属性是不可以同步到视图的,必须调用:
8.4 小程序的 wxss css 有哪些不一样的地方 ?
WXSS CSS 类似,不过在 CSS 的基础上做了一些补充和修改
尺寸单位 rpx
rpx 是响应式像素 , 可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx 。如在 iPhone6 上,屏幕
宽度为 375px ,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素
使用 @import 标识符来导入外联样式。 @import 后跟需要导入的外联样式表的相对路径,用 ; 表示
语句结束
8.5 小程序页面间有哪些传递数据的方法
· 使用全局变量实现数据传递 在 app.js 文件中定义全局变量 globalData , 将需要存储的信息存放
在里面使用的时候,直接使用 getApp() 拿到存储的信息
this.setData({
// 这里设置
})
/** index.wxss **/
@import './base.wxss';
.container{
color: red;
} · 使用 wx.navigateTo wx.redirectTo 的时候,可以将部分数据放在 url 里面,并在新页面
onLoad 的时候初始化
需要注意的问题:
wx.navigateTo wx.redirectTo 不允许跳转到 tab 所包含的页面
onLoad 只执行一次
· 使用本地缓存 Storage 相关
8.6 小程序的生命周期函数
小程序生命周期函数
页面生命周期函数
组件生命周期函数
具体的钩子函数自己去查
8.7 怎么封装微信小程序的数据请求 ?
自己总结
App({
// 全局变量
globalData: {
userInfo: null
}
})
/pageA.js
// Navigate
wx.navigateTo({
url: '../pageD/pageD?name=raymond&gender=male',
})
// Redirect
wx.redirectTo({
url: '../pageD/pageD?name=raymond&gender=male',
})
// pageB.js
...
Page({
onLoad: function(option){
console.log(option.name + 'is' + option.gender)
this.setData({
option: option
})
}
}) 8.8 请说一下小程序的授权登录 ?
自己总结
8.9 哪些方法可以用来提高微信小程序的应用速度 ?
提高页面加载速度
用户行为预测
减少默认 data 的大小
组件化方案
8.10 怎么解决小程序的异步请求问题 ?
小程序支持大部分 ES6 语法
在返回成功的回调里面处理逻辑
Promise 异步
8.11 小程序关联微信公众号如何确定用户的唯一性 ?
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 unionid 来区分用户的
唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用
户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用, unionid 是相同
8.12 小程序如何实现下拉刷新 ?
首先在全局 config 中的 window 配置 enablePullDownRefresh
Page 中定义 onPullDownRefresh 钩子函数 , 到达下拉刷新条件后,该钩子函数执行,发起请求
方法
请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新
参考 这里 :( https://juejin.im/post/5a781c756fb9a063606eb742
下拉刷新和上拉加载是业务上一个很常见的需求,在微信小程序里,提供了下拉刷新的方法
onPullDownRefresh 。而实现上拉加载相对来说就比较不方便了
* 下拉刷 ** *
虽然微信的官方文档有很多坑,但下拉刷新介绍的还是很全面的。在这里稍稍带过。
· 首先在全局 config 中的 window 配置 enablePullDownRefresh .
· Page 中定义 onPullDownRefresh 钩子函数。到达下拉刷新条件后,该钩子函数执行,发起请求方
法。
· 请求返回后,调用 wx.stopPullDownRefresh 停止下拉刷新。
### *config\****
config = {
pages: [
'pages/index'
],
window: { backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#ccc',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: '#000',
enablePullDownRefresh: true
}
}
### *page\****
onPullDownRefresh() {
wepy.showNavigationBarLoading()
setTimeout(()=>{
this.getData = ' 数据拿到了 '
wepy.stopPullDownRefresh()
wepy.hideNavigationBarLoading()
this.$apply()
},3000)
}
效果如下:
img
你会发现下拉的过程有些僵硬。这实际上是没有添加背景色的原因,加上背景色后再试试。
img
现在感觉好多了吧。下拉刷新有现成的配置和方法,很容易实现,可上拉加载就不同了。
上拉加载
首先看一下要实现的效果,这是 3g 端的上拉加载。小程序要实现同样的效果。
img
首先功能有
· 点击回到顶部 这个很好实现,有对应的回到顶部函数
· 滑动屏幕记录当前页数 这个也很好实现,主要是监听滚动事件,判断对应滚动条高度,去计算其与子
容器的高度即可。
· 上拉加载动画 这里有两个实现的方案。一个是 page 自带的下拉触底钩子事件 onReachBottom 能做的只是下拉到底
部的时候通知你触底了,一个是 scroll-view 标签自带事件。现在用两个方法分别实现一下上拉加载。
上拉触底事件 onReachBottom
模板
<template>
<view class="loading"></view>
<view class="container"
@touchmove="moveFn"
@touchstart="startFn"
@touchend="endFn"
style="transform:translate3d(0,{{childTop}}px,0)">
<repeat for="{{list}}"
key="index"
index="index"
item="item">
<view>{{ item }}<text>{{index}}</text></view>
</repeat>
</view>
</template> 复制代码
钩子函数
data = {
getData: '',
top: 0,
lastTop: 0,
canDrag: false,
list: []
}
onReachBottom() {
this.canDrag = true
} methods = {
moveFn(ev) {
let nowY = ev.changedTouches[0].clientY
nowY = nowY-this.lastTop
if(nowY > 0 )
this.canDrag = false
if( nowY<=0 && this.canDrag ) {
this.top = nowY
}
if( -this.top>= this.maxTop )
this.top = -this.maxTop
},
startFn(ev) {
this.lastTop = ev.changedTouches[0].clientY
},
endFn() {
if(this.top <= -this.maxTop) {
this.text = " 去请求数据了 "
setTimeout(()=>{
this.text = " 请求回来了 "
this.canDrag = false
this.list.push(...[" 数据 "," 数据 "," 数据 "])
this.$apply()
this.top = 0;
return
},1000)
}
},
gotoTop() { 完成后看一下效果:
img
滚动容器实现上拉加载
scroll-view : 可滚动视图区域。
它的具体用法不赘述,看官方文档就行了。这里提解决上述问题的方法即可。
· bindscrolltolower 类比原生全局钩子 onReachBottom
模板
以上就是最终的模板,你可能在想为什么这么复杂。虽然复杂,但每个属性都是有用的,当然这其中有
几个坑在等着我们。
首先节点分为滚动容器和子容器。
8.13 bindtap catchtap 的区别是什么 ?
相同点:首先他们都是作为点击事件函数,就是点击时触发。在这个作用上他们是一样的,可以不
做区 -
不同点:他们的不同点主要是 bindtap 是不会阻止冒泡事件的, catchtap 是阻值冒泡的
wepy.pageScrollTo({
scrollTop: 0
})
}
}
<scroll-view scroll-y
id="content"
@scroll="scroll"
@scrolltolower="lower"
scroll-top="{{gotoTopNum}}"
lower-threshold="100"
style="transform:translate3d(0,{{childTop}}px,0)">
<view class="sty-search"
@touchmove="moveContent"
@touchstart="startContent"
@touchend="endContent">...</view>
</scroll-view> 8.14 简述下 wx.navigateTo() , wx.redirectTo() , wx.switchTab() ,
wx.navigateBack() , wx.reLaunch() 的区别
wx.navigateTo() :保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面
wx.redirectTo() :关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
wx.switchTab() :跳转到 abBar 页面,并关闭其他所有非 tabBar 页面
wx.navigateBack() 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前
的页面栈,决定需要返回几层
wx.reLaunch() :关闭所有页面,打开到应用内的某个页面
8.15 说一下小程序组件中如何进行通信 ?
自己总结
8.16 说一下小程序中的 behaviors 的作用 ?
自己总结
8.17 说一下小程序的 observe 的理解 ?
自己总结
8.18 说一下微信小程序支付功能如何实现 ?
自己总结
8.19 说一下什么是小程序云开发 ?
自己总结
8.20 说一下小程序中 wxs?
自己总结
8.21 说一下在小程序中如何使用 npm? 以及如何使用 echarts?
自己总结
九、 JavaScript 面试题
9.1 闭包
什么是闭包?
MDN 的解释:闭包是函数和声明该函数的词法环境的组合。
按照我的理解就是:闭包 = 『函数』和『函数体内可访问的变量总和』
说白了就是函数嵌套函数,内部函数能够访问外部函数的变量 add 函数本身,以及其内部可访问的变量,即 a = 1 ,这两个组合在一起就被称为闭包,仅此而
已。
闭包的作用
闭包最大的作用就是隐藏变量,闭包的一大特性就是 内部函数总是可以访问其所在的外部函数
中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
基于此特性, JavaScript 可以实现私有变量、特权变量、储存变量等
我们就以私有变量举例,私有变量的实现方法很多,有靠约定的(变量名前加 _ , 有靠 Proxy
代理的,也有靠 Symbol 这种新数据类型的。
闭包的优点
可以隔离作用域,不造成全局污染
闭包的缺点
由于闭包长期驻留内存,则长期这样会导致内存泄露
如何解决内存泄露:将暴露全外部的闭包变量置为 null
适用场景:封装组件, for 循环和定时器结合使用 ,for 循环和 dom 事件结合 . 可以在性能优化的过程
, 节流防抖函数的使用 , 导航栏获取下标的使用
写一个返回闭包的函数
9.2 谈谈你对原型链的理解?
答:原型链是理解 JS 面向对象很重要的一点,这里主要涉及到两个点,一是 _ proto ,二是 prototype,
个例子吧,这样还好说点,例如:我用 function 创建一个 Person 类,然后用 new Person 创建一个对象的
实例假如叫 p1 吧,在 Person 类的原型 prototype 添加一个方法,例如: play 方法 , 那对象实例 p1 如何查
找到 play 这个方法呢,有一个查找过程,具体流程是这样的:
( function () {
var a = 1 ;
function add () {
var b = 2
var sum = b + a
console . log ( sum ); // 3
}
add ()
})()
function outer(){
var val = 0;
return function (){
val += 1;
document.write(val + "<br />");
};
}
var outObj = outer();
outObj();//1 ,执行 val += 1 后, val 还在
outObj();//2
outObj = null;//val 被回收
var outObj1 = outer();
outObj1();//1
outObj1();//2 首先在 p1 对象实例上查找是否有有 play 方法,如果有则调用执行,如果没有则用 p1. proto ( proto 是一
个指向的作用 , 指向上一层的原型 ) 往创建 p1 的类的原型上查找,也就是说往 Person.prototype 上查找,
如果在 Person.prototype 找到 play 方法则执行,否则继续往上查找,则用 Person.prototye. proto 继续
往上查找,找到 Object.prototype ,如果 Object.prototype play 方法则执行之,否则用
Object.prototype. proto 继续再往上查找,但 Object.prototpye. proto 上一级是 null, 也就是原型链的顶
级,结束原型链的查找,这是我对原型链的理解
9.3 说一下 JS 继承(含 ES6 的) -- 或者人家这样问有两个类 A B,B 怎么继承 A
JS 继承实现方式也很多,主要分 ES5 ES6 继承的实现
先说一下 ES5 是如何实现继承的
ES5 实现继承主要是基于 prototype 来实现的,具体有三种方法
一是 原型链继承 :即 B.prototype=new A()
二是借 用构造函数继承 (call 或者 apply 的方式继承 )
是组合继承
组合继承是结合第一种和第二种方式
再说一下 ES6 是如何实现继承的
ES6 继承是目前比较新,并且主流的继承方式,用 class 定义类,用 extends 继承类,用 super() 表示
父类 , 【下面代码部分只是熟悉,不用说课】
function B(name,age) {
A.call(ths,name,age)
}
例如:创建 A
class A
constructor() {
// 构造器代码, new 时自动执行
}
方法 1( ) { //A 类的方法 }
方法 2( ) { //A 类的方法 }
创建 B 类并继承 A 9.3 说一下 JS 原生事件如何绑定
JS 原生绑定事件主要为三种:
一是 html 事件处理程序
二是 DOM0 级事件处理程序
三是 DOM2 级事件处理程序
其中: html 事件现在早已不用了,就是在 html 各种标签上直接添加事件,类似于 css 的行内样
式,缺点是不好维护,因为散落在标签中 , 也就是耦合度太高
class B extends A {
constructor() {
super() // 表示父类
}
}
实例化 B 类: var b1=new B( )
b1. 方法 1( ) 例如:
点我 < /button>
第二类是 DOM0 级事件,目前在 PC 端用的还是比较多的绑定事件方式,兼容性也好,主要是先
获取 dom 元素,然后直接给 dom 元素添加事件
DOM0 事件如何移除呢?很简单: btn.οnclick=null; 置为空就行
优点:兼容性好
缺点:只支持冒泡,不支持捕获
第三类是 DOM2 级事件,移动端用的比较多,也有很多优点,提供了专门的绑定和移除方法
例如: var btn=document.getElementById(‘id 元素 ’)
// 绑定事件
btn.addEventListener(‘click’, 绑定的事件处理函数名 ,false)
// 移除事件
btn.removeEventListener(‘click’, 要移除的事件处理函数名, false)
优点:支持给个元素绑定多个相同事件,支持冒泡和捕获事件机制
9.4 说一下 JS 原生常用 dom 操作方法?
js 原生 dom 操作方法有
查找:
getElementByid,
getElementsByTagName,
querySelector,
querySelectorAll
插入:
appendChild,insertBefore
删除:
removeChild
克隆:
cloneNode
设置和获取属性:
setAttribute(“ 属性名 ”,” ”)
getAttibute(“ 属性名 ”)
例如: var btn=document.getElementById(‘id 元素 ’)
btn.οnclick=function() {
// 要处理的事件逻辑
} 9.5 说一下 ES6 新增特性 ?
1. 新增了块级作用域 (let,const)
2. 提供了定义类的语法糖 (class)
3. 新增了一种基本数据类型 (Symbol)
4. 新增了变量的解构赋值
5. 函数参数允许设置默认值,引入了 rest 参数,新增了箭头函数
6. 数组新增了一些 API ,如 isArray / from / of 方法 ; 数组实例新增了 entries() keys() values()
方法
7. 对象和数组新增了扩展运算符
8. ES6 新增了模块化 (import/export)
9. ES6 新增了 Set Map 数据结构
10. ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
11. ES6 新增了生成器 (Generator) 和遍历器 (Iterator)
9.6 JS 设计模式有哪些 ( 单例模式观察者模式等 )
JS 设计模式有很多,但我知道的有单例模式,观察者模式
单例模式:
就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果
不存在就创建了再返回,这就确保了一个类只有一个实例对象。在 JavaScript 里,单例作为一个命
名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式 :
观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象
需要改变的时候,就应该考虑使用观察者模式。
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而
使得各自的变化都不会影响到另一边的变化
9.7 说一下你对 JS 面试对象的理解
JS 面向对象主要基于 function 来实现的,通过 function 来模拟类,通过 prototype 来实现类方法的共享,
跟其他语言有着本质的不同,自从有了 ES6 后,把面向对象类的实现更像后端语言的实现了,通过 class
来定义类,通过 extends 来继承父类,其实 ES6 类的实现本质上是一个语法糖,不过对于开发简单了好多
9.8 说一下 JS 数组常用方法(至少 6 个)
在开发中,数组使用频率很频繁, JS 数组常用方法有
push
pop
unshift
shift
splice
join
concat
forEach
filter
map
sort some
every
好多,不过都是平时开发中很常用的方法 , 大家可以补充一点儿 es6
9.9 说一下 JS 数组内置遍历方法有哪些和区别
JS 数组内置遍历(遍历就是循环的意思)方法主要有:
forEach
这个方法是为了取代 for 循环遍历数组的,返回值为 undefined 例如:
其中:
item 代码遍历的每一项 ,
index: 代表遍历的每项的索引,
arr 代表数组本身
filter
是一个过滤遍历的方法,如果返回条件为 true ,则返回满足条件为 true 的新数组
map
这个 map 方法主要对数组的复杂逻辑处理时用的多,特别是 react 中遍历数据,也经常用到,
写法和 forEach 类似
some
这个 some 方法用于只要数组中至少存在一个满足条件的结果,返回值就为 true, 否则返回
fasel, 写法和 forEach 类似
every
这个 every 方法用于数组中每一项都得满足条件时,才返回 true ,否则返回 false, 写法和
forEach 类似
let arrInfo=[4,6,6,8,5,7,87]
arrInfo.forEach((item,index,arr)=>{
// 遍历逻辑
})
let arrInfo=[4,16,6,8,45,7,87]
let resultArr=arrInfo.filter((item,index,arr)=>{
// 例如返回数组每项值大于 9 的数组
return item>9
}) 9.10 说一下 JS 作用域
JS 作用域也就是 JS 识别变量的范围,作用域链也就是 JS 查找变量的顺序
先说作用域, JS 作用域主要包括全局作用域、局部作用域和 ES6 的块级作用域
全局作用域:也就是定义在 window 下的变量范围,在任何地方都可以访问,
局部作用域:是只在函数内部定义的变量范围
块级作用域:简单来说用 let const 在任意的代码块中定义的变量都认为是块级作用域中的变
量,例如在 for 循环中用 let 定义的变量,在 if 语句中用 let 定义的变量等等
: 1. 尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对 bug 查找不利。
2. 而所谓的作用域链就是由最内部的作用域往最外部 , 查找变量的过程 . 形成的链条就是作用域链
9.11 说一下从输入 URL 到页面加载完中间发生了什么?
大致过程是这样的:
1. 通过 DNS 服务器: url=>ip 地址;
2. 到达 ip 地址对应的服务器;
3. 服务器接收用户的请求;
4. 把处理后的结果返回给客户端;
5. 客户端把结果渲染到浏览器即可,最后页面显示出来
输入了一个域名 , 域名要通过 DNS 解析找到这个域名对应的服务器地址 (ip), 通过 TCP 请求链接服务 , 通过
WEB 服务器 (apache) 返回数据 , 浏览器根据返回数据构建 DOM , 通过 css 渲染引擎及 js 解析引擎将页面渲
染出来 , 关闭 tcp 连接
9.12 说一下 JS 事件代理(也称事件委托)是什么,及实现原理?
JS 事件代理就是通过给父级元素(例如: ul )绑定事件,不给子级元素 ( 例如: li) 绑定事件,然后当点击
子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,主要目的是为了提升性能,因
为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了 , 在原生 js 里面是通过 event 对象的
targe 属性实现
jq 方式实现相对而言简单 $(“ul”).on(“click”,“li”,function(){// 事件逻辑 }) 其中第二个参数指的是触发事件的
具体目标, 特别是给动态添加的元素绑定事件,这个特别起作用
var ul = document.querySelector("ul");
ul.onclick = function(e){//e event, 事件对象
var target = e.target || e.srcElement; //target 获取触发事件的目标 (li)
if(target.nodeName.toLowerCase() == 'li'){// 目标 (li) 节点名转小写字母,不转的话是大写
字母
alert(target.innerHTML)
}
} 9.13 说一下 js 数据类型有哪些?
js 数据类型有:
基本数据类型
umber
string
Boolean
null
undefined
symbol ES6 新增)
复合类型有
Object
function
9.14 说一下 call,apply,bind 区别
call,apply,bind 主要作用都是改变 this 指向的,但使用上略有区别,说一下区别
call apply 的主要区别是在传递参数上不同, call 后面传递的参数是以逗号的形式分开的, apply
传递的参数是数组形式 [Apply 是以 A 开头的 , 所以应该是跟 Array( 数组 ) 形式的参数 ]
bind 返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如: bind(obj, 参数 1, 参数
2,)(),bind 只能以逗号分隔形式,不能是数组形式
9.15 JavaScript 的作用域链理解吗
JavaScript 属于静态作用域,即声明的作用域是根据程序正文在编译时就确定的,有时也称为词法作用
域。
其本质是 JavaScript 在执行过程中会创造可执行上下文,可执行上下文中的词法环境中含有外部词法环境
的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的
词法环境,因此形成了作用域链。
9.16 ES6 模块与 CommonJS 模块有什么区别?
ES6 Module CommonJS 模块的区别:
CommonJS 是对模块的浅拷贝, ES6 Module 是对模块的引用 , ES6 Module 只存只读,不能改变
其值,具体点就是指针指向不能变,类似 const
import 的接口是 read-only (只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但
可以改变变量内部指针指向 , 可以对 commonJS 对重新赋值(改变指针指向),但是对 ES6 Module
赋值会编译报错。
ES6 Module CommonJS 模块的共同点:
CommonJS ES6 Module 都可以对引入的对象进行赋值,即对对象内部属性的值进行改变。 9.17 null undefined 的区别是什么?
null 表示为空,代表此处不应该有值的存在,一个对象可以是 null ,代表是个空对象,而 null 本身也是对
象。
undefined 表示『不存在』, JavaScript 是一门动态类型语言,成员除了表示存在的空值外,还有可能根
本就不存在(因为存不存在只在运行期才知道),这就是 undefined 的意义所在
9.18 那么箭头函数的 this 指向哪里?
箭头函数不同于传统 JavaScript 中的函数 , 箭头函数并没有属于自己的 this, 它的所谓的 this 是捕获其所在上
下文的 this 值,作为自己的 this , 并且由于没有属于自己的 this, 而箭头函数是不会被 new 调用的,这个
所谓的 this 也不会被改变 .
9.19 async/await 是什么?
async 函数,就是 Generator 函数的语法糖,它建立在 Promises 上,并且与所有现有的基于 Promise
API 兼容。
1. Async— 声明一个异步函数 (async function someName(){...})
自动将常规函数转换成 Promise ,返回值也是一个 Promise 对象
只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数
异步函数内部可以使用 await
2. Await— 暂停异步的功能执行 (var result = await someAsyncCall() 😉
放置在 Promise 调用之前, await 强制其他代码等待,直到 Promise 完成并返回结果
只能与 Promise 一起使用,不适用与回调
只能在 async 函数内部使用
9.20 async/await 相比于 Promise 的优势?
代码读起来更加同步, Promise 虽然摆脱了回调地狱,但是 then 的链式调用也会带来额外的阅读负
Promise 传递中间值非常麻烦,而 async/await 几乎是同步的写法,非常优雅
错误处理友好, async/await 可以用成熟的 try/catch Promise 的错误捕获非常冗余
调试友好, Promise 的调试很差,由于没有代码块,你不能在一个返回表达式的箭头函数中设置断
点,如果你在一个 .then 代码块中使用调试器的步进 (step-over) 功能,调试器并不会进入后续
.then 代码块,因为调试器只能跟踪同步代码的『每一步』。
9.21 JavaScript 的基本类型和复杂类型是储存在哪里的?
基本类型储存在栈中,但是一旦被闭包引用则成为常住内存,会储存在内存堆中。
复杂类型会储存在内存堆中。
9.22 简述同步与异步的区别
同步:
浏览器访问服务器请求,用户看得到页面刷新,重新发请求 , 等请求完,页面刷新,新内容出
现,用户看到新内容 , 进行下一步操作
代码从上往下依次执行,执行完当前代码,才能执行下面的代码。(阻塞)
异步: 浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内
容也会出现,用户看到新内容
代码从上往下依次执行,没执行完当前代码,也能执行下面的代码。(非阻塞)
9.23 JavaScript 垃圾回收原理?
javascript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收;
如果两个对象互相引用,而不再被第 3 者所引用,那么这两个互相引用的对象也会被回收。
9.24 请描述值类型 ( 基本数据类型 ) 和引用类型的区别?
值类型
占用空间固定,保存在栈中(当一个方法执行时,每个方法都会建立自己的内存栈,在这个方
法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自
然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及
一些对象的引用变量, 基础变量的值是存储在栈中 ,而引用变量存储在栈中的是 指向堆中的数组
或者对象的地址 ,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。)
保存与复制的是值本身
使用 typeof 检测数据的类型
基本类型数据是值类型
引用类型
占用空间不固定,保存在堆中(当我们在程序中创建一个对象时,这个对象将被保存到运行时
数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。
堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变
量所引用(方法的参数传递时很常见),则这个对象 依然不会被销毁 ,只有当一个对象没有任
何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。)
保存与复制的是指向对象的一个指针
使用 instanceof 检测数据类型
使用 new() 方法构造出的对象是引用型
9.25 深拷贝和浅拷贝的区别?如何实现
浅拷贝 只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。浅拷贝只复制
对象的第一层属性
深拷贝 会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
对对象的属性进行递归复制
实现方式
浅拷贝
使用 Object.assign({},obj) 第一个参数是一个空对象,第二个参数是你要复制的对象;通过这
个方法我们知道浅拷贝不能修改基础的数据类型,可以修改引用的数据类型;
ES6 中的 ... 扩展运算符来进行浅拷贝的实现;
Object.assign() 实现 Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目
标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本
身。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
注意:当 object 只有一层的时候,是深拷贝,例如如下
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
深拷贝
对象只有一层的话可以使用上面的: Object.assign() 函数
转成 JSON 再转回来
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
JSON.stringify 把对象转成字符串,再用 JSON.parse 把字符串转成新的对象。
使用 Object.create() 方法
直接使用 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;
} 9.26 浏览器是如何渲染页面的?
渲染的流程如下:
1. 解析 HTML 文件,创建 DOM 树。
自上而下,遇到任何样式( link style )与脚本( script )都会阻塞(外部样式不阻塞后续外部脚本的加
载)。
2. 解析 CSS 。优先级:浏览器默认设置 < 用户设置 < 外部样式 < 内联样式 <HTML 中的 style 样式;
3. CSS DOM 合并,构建渲染树( Render Tree
4. 布局和绘制,重绘( repaint )和重排( reflow
9.27 什么是 JavaScript 原型,原型链 ? 有什么特点?
每个对象都会在其内部初始化一个属性,就是 prototype ( 原型 ) ,当我们访问一个对象的属性时
如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这 prototype
会有自己的 prototype ,于是就这样一直找下去,也就是我们平时所说的原型链的概念
关系: instance.constructor.prototype = instance.__proto__
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的
原型副本。当我们修改原型时,与之相关的对象也会继承这一改变
当我们需要一个属性的时, Javascript 引擎会先看当前对象中是否有这个属性, 如果没有的
就会查找他的 Prototype 对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象
9.28 json jsonp 的区别 ?
json 返回的是一串 json 格式数据;而 jsonp 返回的是脚本代码(包含一个函数调用)
jsonp 的全名叫做 json with padding ,就是把 json 对象用符合 js 语法的形式包裹起来以使其他的网站可以
请求到,也就是将 json 封装成 js 文件传过去。
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
} 9.29 如何阻止冒泡?
冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标 (document 对象 ) 的顺序触发。
w3c 的方法是 e.stopPropagation() IE 则是使用 e.cancelBubble = true
9.30 如何阻止默认事件?
w3c 的方法是 e.preventDefault() IE 则是使用 e.returnValue = false
9.31 JavaScript 事件流模型都有什么?
事件冒泡 ”: 事件开始由最具体的元素接受,然后逐级向上传播
事件捕捉 ”: 事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
“DOM 事件流 ”: 三个阶段 : 事件捕捉,目标阶段,事件冒泡
9.32 js 实现随机选取 10–100 之间的 10 个数字,存入一个数组,并排序。
// 阻止冒泡行为
function stopBubble(e) {
// 如果提供了事件对象,则这是一个非 IE 浏览器
if ( e && e.stopPropagation )
// 因此它支持 W3C stopPropagation() 方法
e.stopPropagation();
else
// 否则,我们需要使用 IE 的方式来取消事件冒泡
window.event.cancelBubble = true;
}
// 阻止浏览器的默认行为
function stopDefault( e ) {
// 阻止默认浏览器动作 (W3C)
if ( e && e.preventDefault )
e.preventDefault();
//IE 中阻止函数器默认动作的方式
else
window.event.returnValue = false;
return false;
}
function randomNub(aArray, len, min, max) {
if (len >= (max - min)) {
return ' 超过 ' + min + '-' + max + ' 之间的个数范围 ' + (max - min - 1)
+ ' 个的总数 ';
}
if (aArray.length >= len) {
aArray.sort(function(a, b) {
return a - b 9.33 有这样一个 URL: http://item.taobao.com/item.htm?a=1&b=2&c=&d=xx
x&e , 请写一段 JS 程序提取 URL 中的各个 GET 参数 ( 参数名和参数个数不确定 )
将 其按 key-value 形式返回到一个 json 结构中,如 {a:’1′, b:’2′, c:”, d:’ xxx’,
e:undefined}
####
9.34 请你谈谈 cookie 的弊端?
缺点 :
1.Cookie 数量和长度的限制。每个 domain 最多只能有 20 cookie ,每个 cookie 长度 不能超过
4KB ,否则会被截掉。
});
return aArray;
}
var nowNub = parseInt(Math.random() * (max - min - 1)) + (min + 1);
for (var j = 0; j < aArray.length; j++) {
if (nowNub == aArray[j]) {
randomNub(aArray, len, min, max);
return;
}
}
aArray.push(nowNub);
randomNub(aArray, len, min, max);
return aArray;
}
var arr=[];
randomNub(arr,10,10,100);
function serilizeUrl(url) {
var urlObject = {};
if (/\?/.test(url)) {
var urlString = url.substring(url.indexOf("?") + 1);
var urlArray = urlString.split("&");
for (var i = 0, len = urlArray.length; i < len; i++) {
var urlItem = urlArray[i];
var item = urlItem.split("=");
urlObject[item[0]] = item[1];
}
return urlObject;
}
return null;
} 2. 安全性问题。如果 cookie 被人拦截了,那人就可以取得所有的 session 信息。即使加密也与事无补,
因为拦截者并不需要知道 cookie 的意义,他只要原样转发 cookie 就可以达到目的了。
3. 有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存 一个计数
器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。
9.35 哪些操作会造成内存泄漏 ?
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。 垃圾回收器定期扫描对象,并计算引用了每
个对象的其他对象的数量。如果一个对象的引用 数量为 0( 没有其他对象引用过该对象 ) ,或对该对象的
惟一引用是循环的,那么该对象 的内存即可回收。
1. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
2. 闭包
3. 控制台日志
4. 循环 ( 在两个对象彼此引用且彼此保留时,就会产生一个循环 )
9.36 你如何优化自己的代码 ?
代码重用
避免全局变量 ( 命名空间,封闭空间,模块化 mvc..)
拆分函数避免函数过于臃肿
9.37 JavaScript 中的强制转型是指什么?
JavaScript 中,两种不同的内置类型间的转换被称为强制转型。强制转型在 JavaScript 中有两种形
式:显式和隐式。
这是一个显式强制转型的例子:
这是一个隐式强制转型的例子:
var a = "42" ;
var b = Number ( a );
a ; // "42"
b ; // 42 -- 是个数字 !
var a = "42" ;
var b = a * 1 ; // "42" 隐式转型成 42
a ; // "42"
b ; // 42 -- 是个数字 ! 9.38 解释 JavaScript 中的相等性。
JavaScript 中有严格比较和类型转换比较:
严格比较(例如 === )在不允许强制转型的情况下检查两个值是否相等;
抽象比较(例如 == )在允许强制转型的情况下检查两个值是否相等。
一些简单的规则:
如果被比较的任何一个值可能是 true false ,要用 === ,而不是 ==
如果被比较的任何一个值是这些特定值(
0 “” [] ),要用 === ,而不是 ==
在其他情况下,可以安全地使用 == 。它不仅安全,而且在很多情况下,它可以简化代码,并且提
升代码可读性。
9.39 你能解释一下 ES5 ES6 之间的区别吗?
ECMAScript 5 ES5 ): ECMAScript 的第 5 版,于 2009 年标准化。这个标准已在所有现代浏览
器中完全实现。
ECMAScript 6 ES6 )或 ECMAScript 2015 ES2015 ):第 6 ECMAScript ,于 2015 年标准
化。这个标准已在大多数现代浏览器中部分实现。
以下是 ES5 ES6 之间的一些主要区别:
箭头函数和字符串插值:
常量
常量在很多方面与其他语言中的常量一样,但有一些需要注意的地方。常量表示对值的 固定引用 。因
此,在使用常量时,你实际上可以改变变量所引用的对象的属性,但无法改变引用本身。
var a = "42" ;
var b = 42 ;
a == b ; // true
a === b ; // false
const greetings = ( name ) => {
return `hello ${ name }` ;
}
const greetings = name => `hello ${ name }` ;
const NAMES = [];
NAMES . push ( "Jim" );
console . log ( NAMES . length === 1 ); // true
NAMES = [ "Steve" , "John" ]; // error 块作用域变量。
新的 ES6 关键字 let 允许开发人员声明块级别作用域的变量。 let 不像 var 那样可以进行提升。
默认参数值
默认参数允许我们使用默认值初始化函数。如果省略或未定义参数,则使用默认值,也就是说 null 是有
效值。
类定义和继承
ES6 引入了对类(关键字 class )、构造函数(关键字 constructor )和用于继承的 extend 关键字的支
持。
for…of 操作符
for…of 语句将创建一个遍历可迭代对象的循环。
用于对象合并的 Spread 操作
promise
promise 提供了一种机制来处理异步操作结果。你可以使用回调来达到同样的目的,但是 promise 通过
方法链接和简洁的错误处理带来了更高的可读性。
// 基本语法
function multiply ( a , b = 2 ) {
return a * b ;
}
multiply ( 5 ); // 10
const obj1 = { a : 1 , b : 2 }
const obj2 = { a : 2 , c : 3 , d : 4 }
const obj3 = {... obj1 , ... obj2 }
const isGreater = ( a , b ) => {
return new Promise (( resolve , reject ) => {
if ( a > b ) {
resolve ( true )
} else {
reject ( false )
}
})
}
isGreater ( 1 , 2 )
. then ( result => {
console . log ( 'greater' )
})
. catch ( result => { 模块导出和导入
9.40 解释 JavaScript “undefined” “not defined” 之间的区别
JavaScript 中,如果你试图使用一个不存在且尚未声明的变量, JavaScript 将抛出错误 “var name is
not defined” ,让后脚本将停止运行。但如果你使用 typeof undeclared_variable ,它将返回
undefined
在进一步讨论之前,先让我们理解声明和定义之间的区别。
“var x” 表示一个声明,因为你没有定义它的值是什么,你只是声明它的存在。
“var x = 1” 既是声明又是定义(我们也可以说它是初始化), x 变量的声明和赋值相继发生。在
JavaScript 中,每个变量声明和函数声明都被带到了当前作用域的顶部,然后进行赋值,这个过程被称
为提升( hoisting )。
当我们试图访问一个被声明但未被定义的变量时,会出现 undefined 错误。
当我们试图引用一个既未声明也未定义的变量时,将会出现 not defined 错误。
9.41 匿名和命名函数有什么区别?
console . log ( 'smaller' )
})
const myModule = { x : 1 , y : () => { console . log ( 'This is ES5' ) }}
export default myModule ;
import myModule from './myModule' ;
var x ; // 声明 x
console . log ( x ); // 输出 : undefined
var x ; // 声明
if ( typeof x === 'undefined' ) // 将返回 true
console . log ( y ); // 输出 : ReferenceError: y is not defined 9.42 什么是 JavaScript 中的提升操作?
提升( hoisting )是 JavaScript 解释器将所有变量和函数声明移动到当前作用域顶部的操作。有两种类
型的提升:
变量提升 —— 非常少见
函数提升 —— 常见
无论 var (或函数声明)出现在作用域的什么地方,它都属于整个作用域,并且可以在该作用域内的任
何地方访问它。
十、 WEB 页面性能优化面试题
10.1 为什么要进行页面性能优化
网站页面的快速加载,能够建立用户对网站的信任,增加回访率,大部分的用户其实都期待页面
能够在 2 秒内加载完成,而当超过 3 秒以后, [ 就会有接近 40% 的用户离开你的网站 ]
因此 , 我们要强调即使没有对性能有实质的优化 , 通过设计提高用户体验的这个过程 , 也算是性能优化 ,
因为 GUI 开发直面用户 , 你让用户有了性能快的 错觉 , 这也叫性能优化了 , 毕竟用户觉得快 , 才是真的
...
10.2 减少 HTTP 请求
数据量不大的页面,就减少 http 请求数量,一次性返回过来,对于数据量大的页面,可以分段异步请
求,让用户先看到一部分,再继续加载另外一部分
http 协议是 无状态 的应用层协议,意味着 每次 http 请求都需要建立通信链路、进行数据传输 ,而在 服务
器端 ,每个 http 都需要启动 独立的线程 去处理。这些通信和服务的开销都很昂贵,减少 http 请求的数目
可有效提高访问性能。
var foo = function () { // 赋给变量 foo 的匿名函数
// ..
};
var x = function bar (){ // 赋给变量 x 的命名函数 bar
// ..
};
foo (); // 实际执行函数
x ();
var a = 2 ;
foo (); // 因为 `foo()` 声明被 " 提升 " ,所以可调用
function foo () {
a = 3 ;
console . log ( a ); // 3
var a ; // 声明被 " 提升 " foo() 的顶部
}
console . log ( a ); // 2 10.3 合并 CSS 、合并 javascript 、合并图片。
将浏览器一次访问需要的 javascript CSS 合并成一个文件,这样浏览器就只需要一次请求。图片也可以
合并,多张图片合并成一张,如果每张图片都有不同的超链接,可通过 CSS 偏移响应鼠标点击操作,构
造不同的 URL
10.4 合并 CSS 、合并 javascript 、合并图片
将浏览器一次访问需要的 javascript CSS 合并成一个文件,这样浏览器就只需要一次请求。图片也可以
合并,多张图片合并成一张,如果每张图片都有不同的超链接,可通过 CSS 偏移响应鼠标点击操作,构
造不同的 URL
10.5 合理设置缓存
很少变化的图片资源可以直接通过 HTTP Header 中的 Expires 设置一个很长的过期头 ; 变化不频繁而又可
能会变的资源可以使用 Last-Modifed 来做请求验证。尽可能的让资源能够在缓存中待得更久。
10.6 将更新频率比较低的 CSS javascript logo 、图标等静态资源文件缓存在浏
览器中
避免频繁的 http 请求。通过设置 http 头中的 cache-control expires 的属性,可设定浏览器缓存,缓存时
间可以是数天,甚至是几个月。
10.7 CSS 放在页面最上部, javascript 放在页面最下面
浏览器会在下载完成全部 CSS 之后才对整个页面进行渲染,因此最好的做法是将 CSS 放在页面最上面,让
浏览器尽快下载 CSS 。如果将 CSS 放在其他地方比如 BODY 中,则浏览器有可能还未下载和解析到 CSS
就已经开始渲染页面了,这就导致页面由无 CSS 状态跳转到 CSS 状态,用户体验比较糟糕,所以可以考
虑将 CSS 放在 HEAD 中。
Javascript 则相反,浏览器在加载 javascript 后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,
因此 javascript 最好放在页面最下面。但如果页面解析时就需要用到 javascript ,这时放到底部就不合适
了。
10.8 减少作用域链查找
前文谈到了作用域链查找问题,这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作
用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局
变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。
低效率的写法:
< span style = "font-size:14px;" > // 全局变量
var globalVar = 1 ;
function myCallback ( info ){
for ( var i = 100000 ; i -- ;){
// 每次访问 globalVar 都需要查找到作用域链最顶端,本例中需要访问 100000
globalVar += i ;
}
}
</ span > 10.9 css 和注释
尽量减少页面中的空格和注释,从而减少页面的大小。对于 css js 可以使用压缩工具进行压缩后再发布
10.10 CSS Sprites
精灵图 || 雪碧图 )
很多的小图片整合成一张大图,通过 css 来移动位置显示,从而减少向服务器请求数据的次数
10.11 减少 dome 请求
页面的 dom 层级尽量的减少,没有用的,多余的 dom 层级都移除掉,越简洁运行越快
10.11 使用外部的 JavaScript CSS
10.12 节流和防抖
防抖是控制次数,节流是控制频率
函数防抖和节流 是优化 高频率 执行 js 代码的一种手段, js 中的一些事件如浏览器的 resize scroll ,鼠
标的 mousemove mouseover input 输入框的 keypress 等事件在触发时,会不断地调用绑定在事件上
的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。
10.12.1 防抖( debounce
防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函
数的间隔小于 wait ,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数 wait )调用函
数。
延迟的抖动函数
该实现思路很简单,就是将执行函数放到一个定时器中,如果在定时器触发之前没有事件执
行,那么就触发该执行函数,否则清空定时器
这是一个简单版的防抖,但是有缺陷,这个防抖只能在最后调用。一般的防抖会有
immediate 选项,表示是否立即调用。这两者的区别,举个栗子来说:
改进版的(立即执行的防抖动函数)
// func 是用户传入需要防抖的函数
// wait 是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器 id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
// 不难看出如果用户调用该函数的间隔小于 wait 的情况下,上一次的时间还未到就被清除了,并不会执行函
// 这个是用来获取当前时间戳的
function now() {
return +new Date()
}
/**
* 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait func 才会执行
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {boolean} immediate 设置为 ture 时,是否立即调用函数
* @return {function} 返回客户调用函数
*/
function debounce (func, wait = 50, immediate = true) {
let timer, context, args
// 延迟执行函数
const later = () => setTimeout(() => {
// 延迟函数执行完毕,清空缓存的定时器序号
timer = null
// 延迟执行的情况下,函数会在延迟函数中执行
// 使用到之前缓存的参数和上下文
if (!immediate) {
func.apply(context, args)
context = args = null
}
}, wait)
// 这里返回的函数是每次实际调用的函数
return function(...params) {
// 如果没有创建延迟执行函数( later ),就创建一个
if (!timer) {
timer = later()
// 如果是立即执行,调用函数
// 否则缓存参数和调用上下文
if (immediate) {
func.apply(this, params)
} else {
context = this
args = params
}
// 如果已有延迟执行函数( later ),调用的时候清除原来的并重新设定一个
// 这样做延迟函数会重新计时
} else {
clearTimeout(timer)
timer = later()
}
}
}
总结
对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,
就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,
你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null ,就可以再次
点击了。
对于延时执行函数来说的实现:清除定时器 ID ,如果是延迟调用就调用函数 10.12.2 节流( throttle
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一
段时间执行。
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
使用时间戳的节流方案
使用时间戳 + 定时器版的节流方案
const debounce=(func,delay=1000)=>{
let context,args;
var prev=Date.now();
return function(...parms){
var now=Date.now();
if((now-prev)>=delay){
context=this;
args=parms
func.apply(context,args);
prev=Date.now();
}else{
context=args=null;
}
}
}
/**
* underscore 节流函数,返回函数连续调用时, func 执行频率限定为 次 / wait
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {object} options 如果想忽略开始函数的的调用,传入 {leading: false}
* 如果想忽略结尾函数的调用,传入 {trailing: false}
* 两者不能共存,否则函数不能执行
* @return {function} 返回客户调用函数
*/
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 之前的时间戳
var previous = 0;
// 如果 options 没传则设为空对象
if (!options) options = {};
// 定时器回调函数
var later = function() {
// 如果设置了 leading ,就将 previous 设为 0
// 用于下面函数的第一个 if 判断
previous = options.leading === false ? 0 : _.now();
// 置空一是为了防止内存泄漏,二是为了下面的定时器判断
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() { 十一、 WebPack 面试题
11.01 webpack grunt gulp 的不同?
Grunt Gulp 是基于任务运行的工具:
它们会自动执行指定的任务,就像流水线,把资源放上去然后通过不同插件进行加工,它们包含活跃的
社区,丰富的插件,能方便的打造各种工作流。
Webpack 是基于模块化打包的工具 :
自动化处理模块 ,webpack 把一切当成模块,当 webpack 处理应用程序时,它会递归地构建一个依赖关
系图 (dependency graph) ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个
bundle
因此这是完全不同的两类工具 , 而现在主流的方式是用 npm script 代替 Grunt Gulp,npm script 同样可以
打造任务流 .
// 获得当前时间戳
var now = _.now();
// 首次进入前者肯定为 true
// 如果需要第一次不执行函数
// 就将上次时间戳设为当前的
// 这样在接下来计算 remaining 的值时会大于 0
if (!previous && options.leading === false) previous = now;
// 计算剩余时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果当前调用已经大于上次调用时间 + wait
// 或者用户手动调了时间
// 如果设置了 trailing ,只会进入这个条件
// 如果没有设置 leading ,那么第一次会进入这个条件
// 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
// 其实还是会进入的,因为定时器的延时
// 并不是准确的时间,很可能你设置了 2
// 但是他需要 2.2 秒才触发,这时候就会进入这个条件
if (remaining <= 0 || remaining > wait) {
// 如果存在定时器就清理掉否则会调用二次回调
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
// 没有的话就开启一个定时器
// 并且不能不能同时设置 leading trailing
timeout = setTimeout(later, remaining);
}
return result;
};
}; 11.02 webpack rollup parcel 优劣?
webpack 适用于大型复杂的前端站点构建 : webpack 有强大的 loader 和插件生态 ,
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值