【面试】最新web前端经典面试题试题及答案(持续更新)-html/css、js、vue、http、web安全、前端性能、浏览器、js算法
阅读目录html/ cssjavascriptes6vuereactjQuerywebpack、gulp、gruntnodehttpweb安全前端性能浏览器算法相关设计模式正则表达式职业规划参考资料html/ css行内元素和块级元素举几个例子?行内元素:span,a,var ,em,input,img,img,textarea,var,em,s......
author: aSuncat
JavaScript知识点大全:https://www.yuque.com/webfront/js
所有最新最全面试题,持续更新在语雀。见
语雀-前端面试题,欢迎点击关注~
阅读目录
- html/ css:https://blog.csdn.net/aSuncat/article/details/88789368
- javascript:2022年版(见语雀)、2019年版
- es6:https://blog.csdn.net/aSuncat/article/details/88818661
- vue:https://blog.csdn.net/aSuncat/article/details/88978775
- react:2022年版(见语雀)、2019年版
- jQuery:https://blog.csdn.net/aSuncat/article/details/108577468
- webpack、gulp、grunt
- node
- 前后端通信http、https、tcp:https://blog.csdn.net/aSuncat/article/details/109180153
- web安全
- 前端性能
- 浏览器:https://blog.csdn.net/aSuncat/article/details/116934548
- 多端
- 算法相关
- 设计模式
- 正则表达式
- hybrid
- 全栈
- 项目
- svn、git
webpack、gulp、grunt
11、前端技术体系
(1)多版本/覆盖
时间戳
发布新版本回滚
(2)ssr & 同构
(3)webpack
vendor:commonchunk
external:全局变量
默认css处理
模块化发展历程? |
可从IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、<script type="module">
这几个角度考虑。
参考答案
模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。
IIFE:使用自执行函数来编写模块化,特点:在一个单独的函数作用域中执行代码,避免变量冲突。
(function(){
return {
data:[]
}
})()
AMD:使用requireJS 来编写模块化,特点:依赖必须提前声明好。
define('./index.js',function(code){
// code 就是index.js 返回的内容
})
CMD:使用seaJS 来编写模块化,特点:支持动态引入依赖文件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
})
CommonJS:nodejs 中自带的模块化。
var fs = require('fs');
UMD:兼容AMD,CommonJS 模块化语法。
webpack(require.ensure):webpack 2.x 版本中的代码分割。
ES Modules:ES6 引入的模块化,支持import 来引入另一个 js 。
import a from 'a';
- webpack
webpack优化方式? |
待完善
24、webpack在使用层面,对插件和loader不够理解。
- gulp
1、
- grunt
1、
grunt和gulp的区别? |
1、易用:Gulp相比Grunt更简洁,而且遵循代码优于配置策略,维护Gulp更像是写代码。
2、高效:Gulp相比Grunt更有设计感,核心设计基于Unix流的概念,通过管道连接,不需要写中间文件。
3、高质量:Gulp的每个插件只完成一个功能,这也是Unix的设计原则之一,各个功能通过流进行整合并完成复杂的任务。例如:Grunt的imagemin插件不仅压缩图片,同时还包括缓存功能。他表示,在Gulp中,缓存是另一个插件,可以被别的插件使用,这样就促进了插件的可重用性。目前官方列出的有673个插件。
4、易学:Gulp的核心API只有5个,掌握了5个API就学会了Gulp,之后便可以通过管道流组合自己想要的任务。
5、流:使用Grunt的I/O过程中会产生一些中间态的临时文件,一些任务生成临时文件,其它任务可能会基于临时文件再做处理并生成最终的构建后文件。而使用Gulp的优势就是利用流的方式进行文件的处理,通过管道将多个任务和操作连接起来,因此只有一次I/O的过程,流程更清晰,更纯粹。
6、代码优于配置:维护Gulp更像是写代码,而且Gulp遵循CommonJS规范,因此跟写Node程序没有差别。
node
(1)nodejs 实践
(2)nodejs性能高在哪里
高并发非阻塞
IO 密集
(3)stream pipe
说不出背压机制
6、node
线上oom(通过重启,打profile,复现寻找bug点)。
基本的node性能监控
alinode等profile工具
浏览器渲染帧率、动画、性能相关,h5页面优化实践,
对小程序深度性能分析优化等方面很感兴趣
13、node服务线上的稳定,日志,安全,监控都有一定的了解,也有一定的线上运维经验
npm 模块安装机制,为什么输入 npm install 就可以自动安装对应的模块? |
1.npm 模块安装机制:
发出npm install命令
查询node_modules目录之中是否已经存在指定模块
npm 向 registry 查询模块压缩包的网址
下载压缩包,存放在根目录下的.npm目录里
解压压缩包到当前项目的node_modules目录
若存在,不再重新安装
若不存在
2. npm 实现原理
输入 npm install 命令并敲下回车后,会经历如下几个阶段(以 npm 5.5.1 为例):
(1)执行工程自身 preinstall
当前 npm 工程如果定义了 preinstall 钩子此时会被执行。
(2)确定首层依赖模块
首先需要做的是确定工程中的首层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加 npm install 参数)。
工程本身是整棵依赖树的根节点,每个首层依赖模块都是根节点下面的一棵子树,npm 会开启多进程从每个首层依赖模块开始逐步寻找更深层级的节点。
(3)获取模块
获取模块是一个递归的过程,分为以下几步:
- 获取模块信息。在下载一个模块之前,首先要确定其版本,这是因为 package.json 中往往是 semantic version(semver,语义化版本)。此时如果版本描述文件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可,如果没有则从仓库获取。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本。
- 获取模块实体。上一步会获取到模块的压缩包地址(resolved 字段),npm 会用此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。
- 查找该模块依赖,如果有依赖则回到第1步,如果没有则停止。
(4)模块扁平化(dedupe)
上一步获取到的是一棵完整的依赖树,其中可能包含大量重复模块。比如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。在 npm3 以前会严格按照依赖树的结构进行安装,因此会造成模块冗余。
从 npm3 开始默认加入了一个 dedupe 的过程。它会遍历所有节点,逐个将模块放在根节点下面,也就是 node-modules 的第一层。当发现有重复模块时,则将其丢弃。
这里需要对重复模块进行一个定义,它指的是模块名相同且 semver 兼容。每个 semver 都对应一段版本允许范围,如果两个模块的版本允许范围存在交集,那么就可以得到一个兼容版本,而不必版本号完全一致,这可以使更多冗余模块在 dedupe 过程中被去掉。
比如 node-modules 下 foo 模块依赖 lodash@^1.0.0,bar 模块依赖 lodash@^1.1.0,则 ^1.1.0 为兼容版本。
而当 foo 依赖 lodash@^2.0.0,bar 依赖 lodash@^1.1.0,则依据 semver 的规则,二者不存在兼容版本。会将一个版本放在 node_modules 中,另一个仍保留在依赖树里。
举个例子,假设一个依赖树原本是这样:
node_modules
– foo
---- lodash@version1
– bar
---- lodash@version2
假设 version1 和 version2 是兼容版本,则经过 dedupe 会成为下面的形式:
node_modules
– foo
– bar
– lodash(保留的版本为兼容版本)
假设 version1 和 version2 为非兼容版本,则后面的版本保留在依赖树中:
node_modules
– foo
– lodash@version1
– bar
---- lodash@version2
(5)安装模块
这一步将会更新工程中的 node_modules,并执行模块中的生命周期函数(按照 preinstall、install、postinstall 的顺序)。
(6)执行工程自身生命周期
当前 npm 工程如果定义了钩子此时会被执行(按照 install、postinstall、prepublish、prepare 的顺序)。
最后一步是生成或更新版本描述文件,npm install 过程完成。
了解v8引擎吗,一段js代码如何执行的? |
在执行一段代码时,JS 引擎会首先创建一个执行栈
然后JS引擎会创建一个全局执行上下文,并push到执行栈中, 这个过程JS引擎会为这段代码中所有变量分配内存并赋一个初始值(undefined),在创建完成后,JS引擎会进入执行阶段,这个过程JS引擎会逐行的执行代码,即为之前分配好内存的变量逐个赋值(真实值)。
如果这段代码中存在function的声明和调用,那么JS引擎会创建一个函数执行上下文,并push到执行栈中,其创建和执行过程跟全局执行上下文一样。但有特殊情况,即当函数中存在对其它函数的调用时,JS引擎会在父函数执行的过程中,将子函数的全局执行上下文push到执行栈,这也是为什么子函数能够访问到父函数内所声明的变量。
还有一种特殊情况是,在子函数执行的过程中,父函数已经return了,这种情况下,JS引擎会将父函数的上下文从执行栈中移除,与此同时,JS引擎会为还在执行的子函数上下文创建一个闭包,这个闭包里保存了父函数内声明的变量及其赋值,子函数仍然能够在其上下文中访问并使用这边变量/常量。当子函数执行完毕,JS引擎才会将子函数的上下文及闭包一并从执行栈中移除。
最后,JS引擎是单线程的,那么它是如何处理高并发的呢?即当代码中存在异步调用时JS是如何执行的。比如setTimeout或fetch请求都是non-blocking的,当异步调用代码触发时,JS引擎会将需要异步执行的代码移出调用栈,直到等待到返回结果,JS引擎会立即将与之对应的回调函数push进任务队列中等待被调用,当调用(执行)栈中已经没有需要被执行的代码时,JS引擎会立刻将任务队列中的回调函数逐个push进调用栈并执行。这个过程我们也称之为事件循环。
14、koa源码了解
web安全
Web攻击方式有哪几种? |
1、CSRF
2、XSS
3、SQL注入攻击
就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
简述HTTPS中间人攻击? |
参考答案
https协议由 http + ssl 协议构成,具体的链接过程可参考SSL或TLS握手的概述
中间人攻击过程如下:
服务器向客户端发送公钥。
攻击者截获公钥,保留在自己手上。
然后攻击者自己生成一个【伪造的】公钥,发给客户端。
客户端收到伪造的公钥后,生成加密hash值发给服务器。
攻击者获得加密hash值,用自己的私钥解密获得真秘钥。
同时生成假的加密hash值,发给服务器。
服务器用私钥解密获得假秘钥。
服务器用加秘钥加密传输信息
防范方法:
服务端在发送浏览器的公钥中加入CA证书,浏览器可以验证CA证书的有效性
- CSRF
CSRF基本概念和缩写? |
CSRF,Cross-site request forgery,通常被称为跨站请求伪造。
CSRF攻击原理? |
1、前提:①用户在注册网站A登录过,②接口有漏洞。
2、引诱点击,往往是一个链接(这个链接自动携带cookie,不会带token),指向网站A的api,接口是get类型。浏览了A,浏览器自动生成新的cookie,网站A拿到cookie,接口运行。
CSRF防御措施? |
1、token验证。
注册网站,或者访问网站,服务器自动向本地存储一个token。
2、refer验证。
服务器判断页面是不是我这个站点来的页面。
3、隐藏令牌。
http的head头,不会放在链接上。
- XSS
XSS基本概念和缩写? |
XSS,cross-site scripting ,跨域脚本攻击。
XSS攻击原理? |
原理:向页面注入html标签或js脚本。
eg:提交区(评论区)写img标签,script标签,利用合法渠道向页面注入js
XSS防御措施? |
宗旨:让xss不可执行。
1、前端替换关键字,如替换<
为<
>
为>
2、后台替换。
3、任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。
- CSRF ,XSS的区别
CSRF ,XSS的区别? |
1、XSS是向页面注入js去运行,然后在js函数体中做他想做的事情。
CSRF是利用网站漏洞,自动执行接口。用户需要登陆网站。
2、XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。
CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。
- SQL注入
SQL注入防御措施? |
1、永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。
2、永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。
3、永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4、不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。
前端性能
1、原则
(1)多使用内存、缓存或者其他方法。
(2)减少cpu占用,减少网络。
提升页面性能的方法有哪些? |
一、加载页面和静态资源
1、静态资源压缩合并,减少http请求。
(1)减少http请求数量
(2)减少请求资源大小
2、非核心代码异步加载。
3、静态资源缓存:通过链接名称控制缓存,只有内容改变的时候,链接名称才会改变。
4、利用浏览器缓存
5、使用cdn让资源加载更快
6、预解析dns
7、使用ssr后端渲染,数据直接输出到html中(ssr:server site render)
二、页面渲染
1、css、js及放置位置
(1)尽量避免在HTML标签中写style属性
(2)css放前面,js放后面。
(3)避免使用CSS Expression
2、图片
(1)避免图片和iFrame等的空Src。空Src会重新加载当前页面,影响速度和效率
(2)懒加载(图片懒加载,下拉加载更多)
<img id="img1" src="preview.jpg" data-realsrc="abc.jpg">
<script type="text/javascript">
var img1 = document.getElementById('img1');
img1.src = img1.getAttribute('data-realsrc');
</script>
3、dom操作
(1)减少dom查询,对dom查询做缓存。
// 未缓存dom查询
var i;
for (i = 0; i < document.getElementsByTagName('p').length; i++) {\
// todo
}
// 缓存了dom查询
var pList = document.getElementByTagName('p');
var i;
for (i = 0; i < pList.length; i++) {
// todo
}
(2)减少dom操作,多个操作尽量合并在一起执行。
var frag = document.createDocumentFragment(); // 片段,循环插入dom,改成先插入到片段,再append到文档流
(3)用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
4、事件
(1)尽早执行操作(如DOMContentLoaded)
(2)事件节流
var textarea = document.getElementById('text');
var timeoutId;
textarea.addEventListener('keyup', function() {
if(timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(function() {
// 触发change事件
}, 100)
})
5、代码细节优化
(1)用hash-table来优化查找
(2)多个变量声明合并
(3)少用全局变量
(4)避免全局查询
(5)避免使用with(with会创建自己的作用域,会增加作用域链长度)
(6)用setTimeout来避免页面失去响应
三、移动端性能优化
1、css
(1)不滥用Float。Float在渲染时计算量比较大,尽量减少使用
(2)不滥用Web字体。Web字体需要下载,解析,重绘当前页面,尽量减少使用。
(3)避免使用css3渐变阴影效果。
2、css动画
(1)尽量使用css3动画,开启硬件加速。
可以用transform: translateZ(0)来开启硬件加速。
CSS中的属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、WebGL、Video)会触发GPU渲染,请合理使用。过渡使用会引发手机过耗电增加
3、合理使用requestAnimationFrame动画代替setTimeout
4、适当使用touch事件代替click事件。
什么是防抖和节流?有什么区别?如何实现?? |
防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
思路:
每次触发事件时都取消之前的延时调用方法
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
思路:
每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
- 非核心代码异步加载
异步加载的方式? |
1、动态脚本加载
script标签,加入到body中
2、defer
加载js的时候,script标签加defer和async
3、async
异步加载的区别? |
1、defer是在html解析完之后才会执行,如果是多个,按照加载的顺序依次进行。
2、async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。
- 预解析dns
预解析dns的方式? |
1、
<link rel="dns-prefetch" href="//host_name_to_prefetch.com">
2、强制打开a标签的dns预解析
<meta http-equiv="x-dns-prefetch-control" content="on">
页面中所有a标签,默认打开了dns预解析,如果链接是https开头的,默认关闭dns预解析
多端
小程序和 H5 有什么区别? |
1、渲染方式与 H5 不同,小程序一般是通过 Native 原生渲染的,但是小程序同时也支持 web 渲染,如果使用 web 渲染的方式,我们需要初始化一个WebView 组件,然后在 WebView 中加载 H5 页面;
所以当我们开发一个小程序时,通常会使用 hybrid 的方式,即会根据具体情况选择部分功能用小程序原生的代码来开发,部分功能通过 WebView 加载 H5 页面来实现。Native 与 Web 渲染混合使用,以实现项目的最优解;
这里值得注意的是,小程序下,native 方式通常情况下性能要优于 web 方式。
2、小程序特有的双线程设计。 H5 下我们所有资源通常都会打到一个 bundle.js 文件里(不考虑分包加载),而小程序编译后的结果会有两个bundle,index.js封装的是小程序项目的 view 层,以及 index.worker.js 封装的是项目的业务逻辑,在运行时,会有两条线程来分别处理这两个bundle,一个是主渲染线程,它负责加载并渲染 index.js 里的内容,另外一个是 Service Worker线 程,它负责执行 index.worker.js 里封装的业务逻辑,这里面会有很多对底层api调用。
算法相关
通常情况下,搞金融的都会考算法。
一、排序
快速排序:https://segmentfault.com/a/1190000009426421
选择排序:https://segmentfault.com/a/1190000009366805
希尔排序:https://segmentfault.com/a/1190000009461832
二、堆栈、队列、链表
堆栈:https://juejin.im/entry/58759e79128fe1006b48cdfd
队列:https://juejin.im/entry/58759e79128fe1006b48cdfd
链表:https://juejin.im/entry/58759e79128fe1006b48cdfd
(1)js数组本身就具备堆栈和队列特性。
(2)堆栈:先进后出。
三、递归
递归:https://segmentfault.com/a/1190000009857470
(1)60%的算法题都用到递归。
四、波兰式和逆波兰式
理论:http://www.cnblogs.com/chenying99/p/3675876.html
源码:https://github.com/Tairraos/rpn.js/blob/master/rpn.js
手写一个冒泡排序? |
1、比较两个相邻的元素,如果后一个比前一个大,则交换位置。
2、 第一轮的时候最后一个元素应该是最大的一个。
3、按照第一步的方法进行两个相邻的元素的比较,由于最后一个元素已经是最大的了,所以最后一个元素不用比较。
function bubbleSort(arr) {
let len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - i - 1; j++) { // j=0时,得到最大的元素,放在了最后面
if (arr[j] > arr[j+1]) {
[arr[j], arr[j+1]] = [arr[j+1], arr[j]];
}
}
}
return arr;
}
let arr = [3,5,1,2,7,8,4,5,3,4];
let sortedArr = bubbleSort(arr);
console.log(sortedArr);
快速排序的思想? |
1、在数据集之中,找一个基准点,将数据分成两个部分,一部分比另外一部分所有的数据都要小,
2、建立两个数组,分别存储左边和右边的数组
3、利用递归进行下次比较
手写一个快速排序? |
function quickSort(arr) {
if (arr.length <= 1) { // 如果数组长度小于等于1无需判断直接返回即可
return arr;
}
var pivotIndex = Math.floor(arr.length / 2); // 取基准点
var pivot = arr.splice(pivotIndex, 1)[0]; // 取基准点的值,splice(index,1)函数可以返回数组中被删除的那个数
var left = []; // 存放比基准点小的数组
var right = []; // 存放比基准点大的数组
for (var i = 0; i < arr.length; i++) { // 遍历数组,进行判断分配
if (arr[i] < pivot) {
left.push(arr[i]); // 比基准点小的放在左边数组
} else {
right.push(arr[i]); // 比基准点大的放在右边数组
}
}
//递归执行以上操作,对左右两个数组进行操作,直到数组长度为<=1;
return quickSort(left).concat([pivot], quickSort(right));
};
var arr = [1, 2, 3, 55, 7, 99, 8, 100, 1021];
var sortedArr = quickSort(arr);
console.log(sortedArr);
设计模式
用过哪些设计模式? |
1、工厂模式:
主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字。将所有实例化的代码集中在一个位置防止代码重复。
工厂模式解决了重复实例化的问题 ,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例。
function createObject(name,age,profession){//集中实例化的函数var obj = newObject();
obj.name =name;
obj.age = age;
obj.profession= profession;
obj.move =function () {
returnthis.name + ' at ' + this.age + ' engaged in ' + this.profession;
};
return obj;
}
var test1 = createObject('trigkit4',22,'programmer');//第一个实例
var test2 =createObject('mike',25,'engineer');//第二个实例
2、构造函数模式
使用构造函数的方法 ,即解决了重复实例化的问题 ,又解决了对象识别的问题,该模式与工厂模式的不同之处在于:
①构造函数方法没有显示的创建对象 (new Object());
②直接将属性和方法赋值给 this 对象;
③没有 renturn 语句。
JS的四种设计模式? |
工厂模式
简单的工厂模式可以理解为解决多个相似的问题;
function CreatePerson(name,age,sex) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.sayName = function(){
return this.name;
}
return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age); // 28
console.log(p1.sex); // 男
console.log(p1.sayName()); // longen
console.log(p2.name); // tugenhua
console.log(p2.age); // 27
console.log(p2.sex); // 女
console.log(p2.sayName()); // tugenhua
单例模式
只能被实例化(构造函数给实例添加属性与方法)一次
// 单体模式
var Singleton = function(name){
this.name = name;
};
Singleton.prototype.getName = function(){
return this.name;
}
// 获取实例对象
var getInstance = (function() {
var instance = null;
return function(name) {
if(!instance) {//相当于一个一次性阀门,只能实例化一次
instance = new Singleton(name);
}
return instance;
}
})();
// 测试单体模式的实例,所以a===b
var a = getInstance("aa");
var b = getInstance("bb");
沙箱模式
将一些函数放到自执行函数里面,但要用闭包暴露接口,用变量接收暴露的接口,再调用里面的值,否则无法使用里面的值
let sandboxModel=(function(){
function sayName(){};
function sayAge(){};
return{
sayName:sayName,
sayAge:sayAge
}
})()
发布者订阅模式
就例如如我们关注了某一个公众号,然后他对应的有新的消息就会给你推送,
//发布者与订阅模式
var shoeObj = {}; // 定义发布者
shoeObj.list = []; // 缓存列表 存放订阅者回调函数
// 增加订阅者
shoeObj.listen = function(fn) {
shoeObj.list.push(fn); // 订阅消息添加到缓存列表
}
// 发布消息
shoeObj.trigger = function() {
for (var i = 0, fn; fn = this.list[i++];) {
fn.apply(this, arguments);//第一个参数只是改变fn的this,
}
}
// 小红订阅如下消息
shoeObj.listen(function(color, size) {
console.log("颜色是:" + color);
console.log("尺码是:" + size);
});
// 小花订阅如下消息
shoeObj.listen(function(color, size) {
console.log("再次打印颜色是:" + color);
console.log("再次打印尺码是:" + size);
});
shoeObj.trigger("红色", 40);
shoeObj.trigger("黑色", 42);
代码实现逻辑是用数组存贮订阅者, 发布者回调函数里面通知的方式是遍历订阅者数组,并将发布者内容传入订阅者数组
对mvvm的理解? |
1、MVVM 是 Model-View-ViewModel 的缩写。
2、Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
3、View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
4、ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
5、在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
mvvm框架实现的三要素/ vue的三要素? |
1、响应式:vue如何监听到data的每个属性变化?
(1)修改data属性后,vue立刻监听到。Object.defineProperty
var __name = 'may';
Object.defineProperty(obj, 'name',{
get: function() {
return __name;
},
set: function(newVal) {
__name = newVal; // 修改__name = 'april',obj.name就会变成april
}
})
(2)data属性代理到vm上
2、模板引擎:vue的模板如何被解析,指令如何处理?
(1)模板是什么
①本质:字符串
②有逻辑,如v-if, v-for等
③与html格式很像,但有很大区别
④模板最终必须转换成Js代码(js函数-render函数),因为
有逻辑(v-if, v-for),必须用js才能实现(图灵完美语言)
转换为html渲染页面,必须用js才能实现
(2)render函数
(3)render函数与vdom
3、渲染:vue的模板如何被渲染成Html?以及渲染过程?
正则表达式
写一个function,清除字符串前后的空格。(兼容所有浏览器)? |
function trim(str) {
if (str && typeof str === 'string') {
return str.replace(/(^\s*)|(\s*)$/g, '');
}
}
hybrid
15、hybrid业务开发经验,对离线包机制熟悉,多各种优化方案也了解得比较清楚。
7、离线包加载和更新机制,
22、servive worker, web worker
全栈
8、图片加载的一个细节,能够从全栈角度给出一些高并发情况下的系统扩展思路
27、跨域access-control-allow-origin多域名白名单处理
cdn部署
前端框架选型
人员协调项目延期
整个部门没有用到mock平台,前后端分离
自己写了服务在服务端用webpack+multi方式融合不同的业务模块打包
Mbox的数据管理方案(mbox的数据状态流转机制)
数据库,数据索引,事务,数据库锁的问题
5、前端核心监控数据来源(performance api + 业务定义为主)
31、逻辑外链如何保持多个数据同步
项目
16、资讯类的项目,有哪些技术特点(本想了解list相关的回答)
详情里面做了哪些
详情页对性能要求高,有哪些优化?配置缓存来优化。
缓存如何配置,
哪些缓存头来控制的缓存
类似静态化的性能优化,采用ssr比较适合详情页的技术
1、哪些工作是偏技术的,需要技术来解决的
svn、git
get fetch、git pull的区别 |
1、git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
2、git pull 则是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。
更多推荐
所有评论(0)