导图社区 前端面试
这是一篇关于前端面试的思维导图,包括:CSS相关、JS相关、vue相关、webpack相关、性能优化、前端安全性问题、前端设计模式、跨域问题的处理。
编辑于2022-04-30 10:08:38前端面试
CSS相关
盒模型
coutent(内容区)
padding
border
margin
精灵图/雪碧图
好处:减少网络请求,风格改变简单
background-img
background-position(定位)
background-repeat
伪元素
:before
:after
作用:清除浮动,添加一些动态效果,图标,分割线等
伪类
状态型::link :hover :active :focus
结构性::first-child :last-child
媒体查询
all
screen
speech
单位(postcss-pxtorem可实现自动化转)
px
vh
vw
em: 相对于父元素
rem: 相对于根元素
弹性布局flex
BFC:一个独立的渲染区域
创建条件
浮动元素
定位(绝对定位、fixed)
display(inline-block、table-cell...)
overflow(hidden、auto、scroll)
特点
垂直摆放
bfc不会与float重叠
独立的容器,子元素不会影响外部元素
垂直居中
display:flex; justify-content:center; align-items:center; top:50%; left:50%; transform:translate(-50%,-50%);
清除浮动
clear:both;
0.5px的线
transform: scaleY(0.5); /*y方向上缩小*/
CSS3实现浏览器的兼容
渐进式兼容,增加浏览器的前缀,可以通过webpack的plugin实现或通过,降级兼容。
css插件处理
JS相关
继承
构造函数
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // false console.log(cat instanceof Cat); // true
方式:使用父类的构造函数来增强子类实例
缺点:只能继承父类的实例属性和方法,不能继承原型属性/方法
原型链(prototype)
function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.eat('fish')); console.log(cat.sleep()); console.log(cat instanceof Animal); //true console.log(cat instanceof Cat); //true
方式:将父类的实例作为子类的原型
缺点:无法实现多继承
组合继承
function Cat(name){ Animal.call(this); this.name = name || 'Tom'; } Cat.prototype = new Animal(); // 感谢 @学无止境c 的提醒,组合继承也是需要修复构造函数指向的。 Cat.prototype.constructor = Cat; // Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true
类Class继承
js事件循环 event loop,宏任务和微任务都属于异步任务
宏任务
浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染
script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)
微任务
可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前
Promise.then
Object.observe
MutationObserver
process.nextTick(Node.js 环境)
闭包
缺点: 内存泄露(消耗) 常驻内存,增加内存使用量 使用场景:封装功能时(需要使用私有的属性和方法),函数防抖、函数节流、函数柯里化、给元素伪数组添加事件需要使用元素的索引值。
闭包就是能读取其他函数内部变量的函数。
apply/call/bind 有什么区别?
apply的用法可以表示如下: A.apply(B, [x, y, z]); 此方法可以改变函数A的this指向,使之指向函数B。第二个参数传的是一个函数参数列表的数组形式。 call的用法和apply差不多,就只有传参方式不一样。类似于这样 : A.apply(B, x, y, z) 可以把多个参数分开来传,而不是像apply一样,需要把所有参数放到一个数组里边传进来。 bind的传参方式和call一样,只不过它的不同之处是,apply和call方法调用之后会立即执行,而bind方法调用之后会返回一个新的函数,它并不会立即执行,需要我们手动执行。
什么是防抖和节流?有什么区别?如何实现?
延迟加载:defer、async、动态加载
TCP三次握手和四次挥手的理解
js数组去重
怎么实现对象的深浅拷贝?
require和import的区别
区别:require是运行时,import是一个编译时,属于ES6的新语法,在编译时,会将它编译成require
require:赋值过程,一般是将require的结果赋值给某个变量;
import:是一个解构的过程,浏览器引擎未实现,一般先将其转为require,可用于动态绑定。
vue相关
1、vue-router如何做历史返回提示?
2、vue-router如何做用户登录权限等?
3、vue生命周期
Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分: 加载渲染过程 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted 子组件更新过程 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated 父组件更新过程 父 beforeUpdate -> 父 updated 销毁过程 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
4、vue组件通信
5、vue服务器渲染
6、vue性能优化
双向绑定
实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。
Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。
Vuex
namespaced
true:具有命名空间
false:不具有命名空间
State
productInfo: { domain: 'DOM-001', }, piInfo: {}
定义data数据
Mutations
mutations: { changeProductInfo: (state, params) => { state.productInfo = params }, changePiInfo: (state, params) => { state.piInfo = params }, },
改变Stste数据
Actions
changeProductInfo(context, data) { context.commit('changeProductInfo', data) }, changePiInfo(context, data) { context.commit('changePiInfo', data) }
异步提交Mutations
Getters
productInfo: state => state.productInfo, piInfo: state => state.piInfo,
获取State的数据,可进行过滤等操作
辅助函数
mapState
// 在单独构建的版本中辅助函数为 Vuex.mapState import { mapState } from 'vuex' export default { // ... computed: mapState({ // 箭头函数可使代码更简练 count: state => state.count, // 传字符串参数 'count' 等同于 `state => state.count` countAlias: 'count', // 为了能够使用 `this` 获取局部状态,必须使用常规函数 countPlusLocalState (state) { return state.count + this.localCount } }) }
命名空间
...mapState({ count:state=>state.moduleB.countB })
mapGetters
import { mapGetters } from 'vuex' export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) } }
命名空间
共有三种方式,如下: //1. commonGetter(){ this.$store.getters['moduleA/moduleAGetter'] }, //2. ...mapGetters('moduleA',['moduleAGetter']),此处的moduleA,不是以前缀的形式出现!!! //3.别名状态下 ...mapGetters({ paramGetter:'moduleA/moduleAGetter' }),
mapActions
命名空间
共有三种方式,如下: 1,3加个前缀moduleA/,都可以实现。2使用辅助函数未变名称的特殊点!!! //1. commonAction(){ this.$store.dispatch('moduleA/moduleAAction') }, //2. ...mapActions('moduleA',['moduleAAction']), //3.别名状态下 ...mapActions({ changeNameAction:'moduleA/moduleAAction' })
mapMutations
命名空间
共有三种方式,如下: //1,3加个前缀moduleA/,都可以实现。2使用辅助函数未变名称的特殊点!!! //1. commonMutation(){ this.$store.commit('moduleA/moduleAMutation'); }, //2. ...mapMutations('moduleA',['moduleAMutation']), //3.别名状态下 ...mapMutations({ changeNameMutation:'moduleA/moduleAMutation' }),
webpack相关
1、hash(contenthash, chunkhash)
2、多页面配置
3、发布上线流程
4、如何加快打包速度,减少打包体积
5、和其他工具的区别(grunt,glup,rollup,parcel,Browserify)
6、状态管理器相关 主要是flux、redux、vuex等
WebPack
基础部分
入口文件entry
单文件入口:entry 是一个字符串
module.exports = { entry: './path/to/my/entry/file.js' };
多文件入口:entry 是⼀个对象
module.exports = { entry: { app: './src/app.js', adminApp: './src/adminApp.js' } };
出口文件OutPut
单入口文件出口
module.exports = { entry: './path/to/my/entry/file.js' output: { filename: 'bundle.js’, path: __dirname + '/dist' } };
多入口文件出口
module.exports = { entry: { app: './src/app.js', search: './src/search.js' }, output: { // 通过占位符确保文件名称的唯一 filename: '[name].js', path: __dirname + '/dist' } };
文件转化:Loaders
常见的loader类型 
Loaders 的⽤法
const path = require('path'); module.exports = { output: { filename: 'bundle.js' }, module: { rules: [ // test 指定匹配规则 // use 指定使用的 loader 名称 { test: /\.txt$/, use: 'raw-loader' } ] } };
解析 ES6:babel-loader
入口文件配置
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.js$/, use: 'babel-loader' } ] } };
增加ES6的babel preset配置:.babelrc
{ "presets": [ "@babel/preset-env” ], "plugins": [ "@babel/proposal-class-properties" ] }
解析 React JSX
{ "presets": [ "@babel/preset-env", // react配置 "@babel/preset-react" ], "plugins": [ "@babel/proposal-class-properties" ] }
解析 CSS
// css-loader ⽤于加载 .css ⽂件,并且转换成 commonjs 对象 // style-loader 将样式通过 <style> 标签插⼊到 head 中 const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } ] } };
解析 Less 和 SaSS
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' , ' less-loader' ] } ] } };
解析图⽚
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] } ] } };
解析字体
// file-loader 也可以⽤于处理字体 const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ 'file-loader' ] } ] } };
图片或字体转为base
减少url请求
使⽤ url-loader
// file-loader 也可以⽤于处理字体 const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [{ loader: 'url-loader’, options: { // 设置最大限制 limit: 10240 } }] } ] } };
插件:Plugins
常用的插件 
Plugins 的⽤法
const path = require('path'); module.exports = { output: { filename: 'bundle.js' }, plugins: [ new HtmlWebpackPlugin({ template: './src/index.html' }) ] };
指定环境变量:Mode
Mode 的内置函数功能

热更新

在配置 webpack.config.js 中设置 watch: true
 
webpack-dev-server
打包压缩配置
高级扩展
PostCSS 插件 autoprefixer ⾃动补⻬ CSS3 前缀
// 使⽤ autoprefixer 插件 module.exports = { module: { rules: [ { test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', { loader: 'postcss-loader', options: { plugins: () => [ require('autoprefixer')({ browsers: ["last 2 version", "> 1%", "iOS 7"] }) ] } } ]} ] } };
分辨率自适应
CSS 媒体查询实现响应式布局
@media screen and (max-width: 980px) { .header { width: 900px; } } @media screen and (max-width: 480px) { .header { height: 400px; } } @media screen and (max-width: 350px) { .header { height: 300px; } }
移动端 CSS px ⾃动转换成 rem
// ⻚⾯渲染时计算根元素的 font-size 值 // 可以使⽤⼿淘的lib-flexible库 module.exports = { module: { rules: [{ test: /\.less$/, use: [ 'style-loader', 'css-loader', 'less-loader', { loader: "px2rem-loader", options: { remUnit: 75, remPrecision: 8 } } ] }] } };
debugger调试
使用devtool开启source-map
通过 source map 定位到源代码 
优化:基础库分离
1、思路:将 react、react-dom 基础包通过 cdn 引入,不打入 bundle 中 2、方法:使用 html-webpackexternals-plugin  3、利用 SplitChunksPlugin 进行公共脚本分离 module.exports = { optimization: { splitChunks: { chunks: 'async', minSize: 30000, maxSize: 0, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', name: true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 } } } } };
【production默认开启】tree shaking(摇树优化)
概念:1 个模块可能有多个⽅法,只要其中的某个⽅法使⽤到了,则整个⽂件都会被打到bundle ⾥⾯去,tree shaking 就是只把⽤到的⽅法打⼊ bundle ,没⽤到的⽅法会在uglify 阶段被擦除掉
使⽤:webpack 默认⽀持,在 .babelrc ⾥设置 modules: false 即可
· production mode的情况下默认开启
要求:必须是 ES6 的语法,CJS 的⽅式不⽀持
性能优化
圈复杂度
代码规范
组件懒加载
数据扁平化
CDN
字体图标
图片格式的优先等级(webp,jpg,png)
webpack提取三方库,按需加载
去除冗余代码
减少DOM操作,较少回流和重回
前端安全性问题
1、xss跨站脚本攻击(原理、如何进行的、防御手段是什么,要说清楚)
2、CSRF跨站请求伪造(如何伪造法?怎么防御?等等都要说清楚)
3、sql脚本注入(注入方式,防御方式)
4、上传漏洞 (防御方式)
前端网络
HTTP请求流程

构建请求
查找缓存
准备IP地址和端口
等待TCP队列(一个域名最多请求6条)
建立TCP连接
TCP/IP 数据包的旅程
1、IP:把数据包(数据部分和附加的IP头)送达目的主机
2、UDP:把数据包送达应用程序>快,但数据不可靠
3、TCP:把数据包完整送达应用程序(数据包丢失,进行重新请求)
建立连接
三次握手(发送三个数据包确认连接)
1、确认双方的接受能力、发送能力是否正常。 2、指定自己的初始化序列号,为后面的可靠传送做准备。
1、第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN©。此时客户端处于 SYN_Send 状态。
2、第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
3、第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
传输数据
断开连接
四次挥手(确保双方都能断开连接)
1、第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。
2、第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 + 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT状态。
3、第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
4、第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态
发送HTTP请求
返回请求
断开连接
URL导航流程(浏览器url)
1、用户输入
2、URL请求过程
① 判断是否需要重定向(根据响应头信息,301、302需要)
② 根据Content-Type判断类型
下载类型:提交浏览器下载管理器
页面类型:准备渲染进程
......
③ 准备渲染进程
3、准备渲染过程(域名一致,复用父页面的渲染进程)
4、提交文档(将网络进程中接收到的HTML数据提交给渲染进程)
① 更新前进后退状态
② 更新安全状态
③ 更行地址栏URL地址
④ 更新web页面
5、渲染阶段(停止标签图标的动画加载)
HTTPS思考
安全性考虑
HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用
SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行
中间人攻击(MITM攻击)是指,黑客拦截并篡改网络中的通信数据。又分为被动MITM和主动MITM,被动MITM只窃取通信数据而不修改,而主动MITM不但能窃取数据,还会篡改通信数据。最常见的中间人攻击常常发生在公共wifi或者公共路由上
成本考虑
SSL证书需要购买申请,功能越强大的证书费用越高
SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗(SSL有扩展可以部分解决这个问题,但是比较麻烦,而且要求浏览器、操作系统支持,Windows XP就不支持这个扩展,考虑到XP的装机量,这个特性几乎没用)。
根据ACM CoNEXT数据显示,使用HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电。
HTTPS连接缓存不如HTTP高效,流量成本高。
HTTPS连接服务器端资源占用高很多,支持访客多的网站需要投入更大的成本。
HTTPS协议握手阶段比较费时,对网站的响应速度有影响,影响用户体验。比较好的方式是采用分而治之,类似12306网站的主页使用HTTP协议,有关于用户信息等方面使用HTTPS。
HTTPS实现原理
client向server发送请求https://baidu.com,然后连接到server的443端口,发送的信息主要是随机值1和客户端支持的加密算法。
server接收到信息之后给予client响应握手信息,包括随机值2和匹配好的协商加密算法,这个加密算法一定是client发送给server加密算法的子集。
随即server给client发送第二个响应报文是数字证书。服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。传送证书,这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。
客户端解析证书,这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值(预主秘钥)。
客户端认证证书通过之后,接下来是通过随机值1、随机值2和预主秘钥组装会话秘钥。然后通过证书的公钥加密会话秘钥。
传送加密信息,这部分传送的是用证书加密后的会话秘钥,目的就是让服务端使用秘钥解密得到随机值1、随机值2和预主秘钥。
服务端解密得到随机值1、随机值2和预主秘钥,然后组装会话秘钥,跟客户端会话秘钥相同。
客户端通过会话秘钥加密一条消息发送给服务端,主要验证服务端是否正常接受客户端加密的消息。
同样服务端也会通过会话秘钥加密一条消息回传给客户端,如果客户端能够正常接受的话表明SSL层连接建立完成了。
HTTPS和HTTP的主要区别
1、https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
客户端在使用HTTPS方式与Web服务器通信时的步骤

(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL/TLS连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥。
(6)Web服务器利用会话密钥加密与客户端之间的通信。
网络安全
XSS攻击
1、服务器对输入脚本进行过滤或转码;
2、充分利用CSP
CORS与CSP区别 CORS,跨域资源共享,允许站点A通过XHR请求向B站点请求数据。通常AJAX跨站的话,为了避免浏览器同源策略,服务端设置CORS。 CSP允许站点阻止 本身 从意外来源加载(潜在恶意)内容(例如作为对XSS的防御)。
① 限制加载其他域下的资源文件;
② 禁止向第三方域提交数据;
③ 禁止执行内联脚本和未授权的脚本;
④ 上报机制,提前发现
配置方式
配置项 script-src:设置脚本来源 object-src:设置<object>标签 style-src:设置样式表来源 frame-src:设置iframe框架 default-src:各个选项的默认值, ’self’加载本站 report-uri:设置个url,会把行为报告发送到这个上面 配置值(多个值用空格间隔,多个配置项用分号间隔) 域名:www.az1314.cn https:www.az1314.cn:443 路径名:xx.com/xx/xx 通配符:*.xx.com *://*.xx.com:* 协议名:https: http: xxx: 'Self':当前域名 'none':禁止加载任何外部资源
设置http header头信息 Content-Security-Policy
Content-Security-Policy:script-src 'self'; object-src 'none';style-src cdn.example.org third-party.org; child-src https:
设置meta信息
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; frame-src https:">
3、HttpOnly属性
防止cookie被盗取,服务器设置
CSRF攻击
1. 充分利用好 Cookie 的 SameSite 属性
Strict:严格模式,禁止第三方Cookie
Lax:一般模式,get第三方请求可以携带Cookie
None:任何情况都携带Cookie
2、验证请求的来源站点
服务端通过Referer的Origin属性来判断
3、CSRF Token
① 服务器生成CSRF Token字符串给到客户端
② 操作请求会带这个字符串,否则就会被拒绝
前端设计模式
1. 单例模式
这种设计模式的思想是确保一个类只有唯一实例,一般用于全局缓存,比如全局window,唯一登录浮窗等。采用闭包的方式实现
var single = (function(){ let instance; function getInstance(){ // 如果该实例存在,则直接返回,否则就对其实例化 if( instance=== undefined ){ instance= new Construct(); } return instance; } function Construct(){ // ... 生成单例的构造函数的代码 } return { getInstance : getInstance } })();
2. 工厂模式
工厂模式是创建对象的常用设计模式,为了不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就称为一个工厂。本质上是一个负责生产对象实例的工厂。工厂模式根据抽象程度的不同可以分为:简单工厂,工厂方法和抽象工厂。通常用于根据权限生成角色的场景,抽象工厂方法的实现
//安全模式创建的工厂方法函数 let UserFactory = function(role) { if(this instanceof UserFactory) { var s = new this[role](); return s; } else { return new UserFactory(role); } } //工厂方法函数的原型中设置所有对象的构造函数 UserFactory.prototype = { SuperAdmin: function() { this.name = "超级管理员", this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理'] }, Admin: function() { this.name = "管理员", this.viewPage = ['首页', '通讯录', '发现页', '应用数据'] }, NormalUser: function() { this.name = '普通用户', this.viewPage = ['首页', '通讯录', '发现页'] } } //调用 let superAdmin = UserFactory('SuperAdmin'); let admin = UserFactory('Admin') let normalUser = UserFactory('NormalUser')
3. 策略模式
策略模式的本意将算法的使用与算法的实现分离开来,避免多重判断调用哪些算法。适用于有多个判断分支的场景,如解决表单验证的问题。你可以创建一个validator对象,有一个validate()方法。这个方法被调用时不用区分具体的表单类型,它总是会返回同样的结果——一个没有通过验证的列表和错误信息
// 对于vip客户 function vipPrice() { this.discount = 0.5; } vipPrice.prototype.getPrice = function(price) { return price * this.discount; } // 对于老客户 function oldPrice() { this.discount = 0.3; } oldPrice.prototype.getPrice = function(price) { return price * this.discount; } // 对于普通客户 function Price() { this.discount = 1; } Price.prototype.getPrice = function(price) { return price ; } // 上下文,对于客户端的使用 function Context() { this.name = ''; this.strategy = null; this.price = 0; } Context.prototype.set = function(name, strategy, price) { this.name = name; this.strategy = strategy; this.price = price; } Context.prototype.getResult = function() { console.log(this.name + ' 的结账价为: ' + this.strategy.getPrice(this.price)); } var context = new Context(); var vip = new vipPrice(); context.set ('vip客户', vip, 200); context.getResult(); // vip客户 的结账价为: 100 var old = new oldPrice(); context.set ('老客户', old, 200); context.getResult(); // 老客户 的结账价为: 60 var Price = new Price(); context.set ('普通客户', Price, 200); context.getResult(); // 普通客户 的结账价为: 200
4. 代理模式
代理模式是为其他对象提供一种代理,也就是当其他对象直接访问该对象时,如果开销较大,就可以通过这个代理层控制对该对象的访问。常见的使用场景为懒加载,合并http请求和缓存。
(function(){ // 目标对象,是真正被代理的对象 function Subject(){} Subject.prototype.request = function(){}; function Proxy(realSubject){ this.realSubject = realSubject; } Proxy.prototype.request = function(){ this.realSubject.request(); }; }());
5. 观察者模式
也叫发布订阅模式,在这种模式中,一个订阅者订阅发布者,当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者。
var EventCenter = (function(){ var events = {}; function on(event, handler){ events[event] = events[event] || []; events[event].push({ handler: handler }); } function fire(event, args){ if (!events[event]) {return} for (var i = 0; i < events[event].length; i++) { events[event][i].handler(args); } } function off(event){ delete events[event]; } return { on: on, fire: fire, off: off } })(); EventCenter.on('event', function(data){ console.log('event received...'); });
6. 模块模式
模块模式可以指定类想暴露的属性和方法,并且不会污染全局。采用闭包的形式
var Person = (function() { var name = 'xxx' function sayName() { console.log(name) } return{ name: name, sayName: sayName } })()
7. 构造函数模式、混合模式
构造函数和混合模式就是js中继承的两种实现方式,前者通过构造函数的形式定义类,通过new新增实例。而后者是将构造函数的引用属性和方法放到其原型上,子类是父类原型的一个实例。
跨域问题的处理
nginx代理
proxy代理
JSONP
iframe+document.domain
postMessage
CORS 同源策略
node作为中间件
nodejs相关
1、nodejs常用模块
2、nodejs爬虫
3、nodejs 流
4、nodejs请求如何返回大文件