CSS实现3D效果

transform相关属性实现3D效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<style>
._3Dbox{
width: 200px;
margin: 200px auto;
perspective: 2000px; /* 视距 */
}
._3Dbox .cube{
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d; /* 以3d的形式展示 !!! */
animation: _3dmove 10s linear infinite;
}
._3Dbox .cube div{
position: absolute;
width: 200px;
height: 200px;
border: 2px solid lightblue;
line-height: 200px;
font-size: 30px;
opacity: 0.9;
text-align: center;
background: url('/images/avatar.jpg');
background-size: contain;
}
._3Dbox .cube div:nth-child(1){ /* 上 */
transform:rotateX(90deg) translateZ(102px);
}
._3Dbox .cube div:nth-child(2){ /* 下 */
transform:rotateX(-90deg) translateZ(102px);
}
._3Dbox .cube div:nth-child(3){ /* 前 */
transform:translateZ(102px);
}
._3Dbox .cube div:nth-child(4){ /* 后 */
transform: translateZ(-102px);
}
._3Dbox .cube div:nth-child(5){ /* 左 */
transform: translateX(-102px) rotateY(90deg);
}
._3Dbox .cube div:nth-child(6){ /* 右 */
transform: translateX(102px) rotateY(-90deg);
}
@keyframes _3dmove {
to{
transform: rotateX(360deg) rotateY(360deg) ;
}
}
</style>
<div class="_3Dbox">
<div class="cube">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>

CSS中的3D坐标轴

rotateX/Y/Z() 相当于绕X/Y/Z轴旋转 多少度
translateX/Y/Z() 相当于沿着X/Y/Z轴的方向移动多少像素

【第七章】 迭代器模式

迭代器模式

提供一种 “顺序访问” 一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
迭代模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序方位其中的每个元素。

自己实现一个迭代器:

1
2
3
4
5
6
7
8
var each = function(ary,callback){
for(var i=0;i<ary.length;i++){
callback.call(null,i,ary[i]) // 把下标和元素当作参数传给callback函数
}
}
each([1,2,3],function(i,n){
console.log([i,n])
})

内部迭代器

上面编写的 each 函数属于内部迭代器,each 函数的内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用。

内部迭代器在调用的时候非常方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅仅是一次初始调用,但这也刚好是内部迭代器的缺点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 内部迭代器实现 比较两个数组
function compare(ary1,ary2){
if(ary1.length != ary2.length){
console.log('不相等')
return
}
each(ary1,function(i,n){
if(ary2[i]!=n){
throw new Error('不相等') // console.log('不相等')
}
})
console.log('相等')
}
compare([1,2,3],[1,2,3]) // 相等

外部迭代器

外部迭代器必须显式地请求迭代下一个元素。
外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var Iterator = function(obj){
var current = 0
var next = function(){
current +=1
}
var isDone = function(){
return current >= obj.length
}
var getCurrentItem = function(){
return obj[current]
}
return {
next: next,
isDone: isDone,
getCurrentItem: getCurrentItem
}
}

// 外部迭代器实现 比较两个数组
function compare(Iterator1,Iterator2){
while(!Iterator1.isDone()&&!Iterator2.isDone()){
if(Iterator1.getCurrentItem() !== Iterator2.getCurrentItem()){
throw new Error('不相等')
}
Iterator1.next()
Iterator2.next()
}
console.log('相等')
}
var Iterator1 = Iterator([4,5,6])
var Iterator2 = Iterator([4,5,6])
compare(Iterator1,Iterator2) // 相等

迭代类数组对象和字面量对象

类数组:arguments
字面量对象:{‘0’:’a’,’1’:’b’}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function each(obj,callback){
var value,
i=0,
length = obj.length,
isArray = isArraylike(obj); //假装封装好了一个判断是否是类数组的方法

if(isArray){ // 迭代类数组
for(;i<length;i++){
value = callback.call(null,i,obj[i])
if(value === false){
break;
}
}
}else{
for(i in obj){
value = callback.call(null,i,obj[i])
if(value === false){
break;
}
}
}
return obj
}

中止迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
var each = function(ary,callback){
for(var i=0;i<ary.length;i++){
if(callback(i,ary[i]) === false){
break
}
}
}
each([11,21,31,41,51],function(){
if(n>30){
return false
}
console.log(n)
}) // 11 21

vue-element-admin权限管理&动态路由配置

vue-element-admin模板地址

需求: 调用登陆接口之后 后台传递路由数据给前端 进行动态创建和显示

问题: 各种问题 主要是不清楚这个模板的默认权限动态传递的配置

用到的核心文件地址

1
2
3
4
src/router/index.js                     路由
src/store/modules/user.js 用户登录,用户信息、路由信息传递
src/permission.js 信息传递(user -> permission 的中间商) 之前就是一直没注意到这个 导致一直弄不好
src/store/modules/permission.js 权限获取&配置

src/router/index.js 路由配置

constantRoutes: 不需要动态判断权限的路由 eg:登陆、404等通用页面
asyncRoutes:需要动态判断权限并通过addRoutes动态添加的页面

需要把动态路由先配置好 ‘permission’ 权限要写好
到时候才能正确的获取和显示相应的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页', icon: 'dashboard', affix: true }
}]
}
]

export const asyncRoutes = [
// 待审核
{
path: '/toPend',
component: Layout,
redirect: '/toPend',
permission: 'toPend',
title: '待审核',
children: [
{
permission: 'toPend',
path: '/toPendIndex',
component: () => import('@/views/toPendManagement/toPendIndex'),
name: 'toPend',
meta: {
title: '待审核',
icon: 'form',
noCache: false // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
}
}
]
},
....
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})

const router = createRouter()

export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}

export default router

src/store/modules/user.js 用户登陆获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
正常流程是在login接口获取到 token,存在vuex中 供登陆
然后在getInfo接口获取到 路由和用户信息 存储并发送出去 供路由的动态配置

但是 我们这个项目把getInfo接口取消了 在login接口就直接获取到路由信息
本来是不需要自己封装info数据的
*/
const actions = {
// user login
login({commit}, userInfo) {
const { username,password } = userInfo
return new Promise((resolve, reject) => {
login({ account: username.trim(), password: password }).then(response => {
const {
data
} = response
// 存储到cookie中 !很重要 不然登陆不了 接口在 src/utils/auth.js
setToken({ username: 'admin', password: 'admin' })
var info = { username: 'admin', password: 'admin', roles: ['admin'], routes: data.permission }
commit('SET_TOKEN', info) // 存储到 vuex 中 数据格式如上 ↑
resolve()
}).catch(error => {
reject(error)
})
})
},
getInfo({ commit,state }) {
return new Promise((resolve) => {
// 可以把这三个写在 info 中 从info里获取
const roles = 'admin'
const name = 'Super Admin'
const avatar = 'http://pic.51yuansu.com/pic3/cover/03/47/85/5badd30d6526d_610.jpg'
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar) // 存储在cookie中
resolve(state.token) // 发送 info 值 ,传递给 permission
})
},
// get user info 如果是有info接口的传递方法
/* getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
const { roles, name, avatar } = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
console.log(data)
resolve(data)
}).catch(error => {
reject(error)
})
})
}, */
......
}
export default {
namespaced: true,
state,
mutations,
actions
}

src/store/modules/permission.js 权限获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
本来是根据角色roles权限过滤路由,并显示到侧边栏
roles.
但是现在是不根据权限 而是后台传递的路由信息 所以不用判断权限 修改为
*/
const actions = {
generateRoutes({ commit }, userinfo) {
return new Promise(resolve => {
const strRoutes = JSON.stringify(userinfo.routes)
const accessedRoutes = filterAsyncRoutes(asyncRoutes, strRoutes) //路由的过滤器 获取相同的路由
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}

src/permission.js 用户信息传递&配置路由

首先确定userinfo需要的格式 就是getInfo函数传递过来的 resolve(state.token)

path对应的是路由中的permission

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
router.beforeEach(async(to, from, next) => {
if (hasToken) {
if (to.path === '/login') {
...
} else {
// determine whether the user has obtained his permission roles through getInfo
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
// 登录后会走这里获取roles
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const userinfo = await store.dispatch('user/getInfo') //获取用户信息
// 调用store下module下的permission文件中的generateRoutes方法
const accessRoutes = await store.dispatch('permission/generateRoutes', userinfo) // 获取动态路由
// dynamically add accessible routes 显示获取到的侧边栏
router.addRoutes(accessRoutes) //创建路由

next({ ...to, replace: true })
} catch (error) {
...
}
}
}
}

被遗漏的BUG

描述: 登录完成之后,进入操作页面 刷新 又需要重新登录

原因: 之前这个模板是调用的getInfo接口获取、存储路由信息
页面刷新之后 再次调用接口即可获取到路由等信息

但是我们把getInfo接口废弃了 路由信息是登陆的时候获取
存在vuex中 在getInfo函数内直接使用的vuex存储的数据 并发布出去

原因就在于 vuex刷新之后 数据就清空了!所以又需要重新登录 获取路由等信息。

解决:
在login登陆接口之后获取到路由数据
通过sessionStorage / localStorage 存放在本地
然后在使用 getInfo 函数的时候 不从vuex内获取 从 sessionStorage / localStorage 获取并发送

注意:在退出登陆的时候 即调用logout接口之前要通过.removeItem()进行删除操作 不然不能正常退出登陆

事件循环相关-异步输出顺序问题

题目出处

通过这个题目 大概搞懂了 async/await 的实际意义
也可以复习下事件循环机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end"); // 相当于async2这个promise对象的一个 .then()
}

async function async2() {
console.log("async2");
}

console.log("script start");

setTimeout(function () {
console.log("settimeout");
},0);

async1();

new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});

console.log("script end");

/**
* 整体同步的执行顺序
* 1. console.log("script start") -> 输出 “ script start ”
*
* * setTimeout() 进入异步队列 *
*
* 2. async1()
* -> 输出 “ async1 start ”
* -> 进入 async2() 输出 “ async2 ”
* -> await等待async2()函数返回一个promise实例 将 console.log('async1 end') 放入异步队列
*
* 3. promise()
* -> 输出 “ promise1 ”
* -> resolve() 进入异步队列
*
* 4. console.log("script end") -> 输出 “ script end ”
*
*
* 异步队列:
* setTimeout()
* console.log('async1 end')
* resolve()
*
* 按宏 / 微任务 顺序输出
* console.log('async1 end') // 微任务 1 -> async1 end
* resolve() // 微任务 2 -> promise2
* setTimeout() // 宏任务 1 -> settimeout
*
*/

async / await

通过看起来是同步的代码来执行 async(异步) 操作。
await阻塞功能,相当于一个 .then()的功能,await后一般是跟的异步操作,加上之后 只有等他执行完之后,后面的代码才能进行
本质还是promise 属于微任务

1
2
3
4
5
6
7
8
9
10
11
12
async function asyncFunc({
    const result = await otherAsyncFunc();
    console.log(result);
}
 
// 等价于:
function asyncFunc({
    return otherAsyncFunc().then(result => {
        console.log(result);
    });
}

事件循环机制 (EventLoop)

JavaScript是单线程的 ( 所有在主线程执行 )
事件轮询机制:同步任务完成后,执行异步队列

单线程特点:

  1. 代码从上至下以此执行
  2. 同步( 会阻塞后面代码的执行 ) & 异步 ( 主线程同步任务之后触发执行 )
  3. 同步代表:alert() console.log() 赋值语句
  4. 异步代表:定时器 事件的回调( xx.onclick=function(){} ) promise

事件循环:

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个”消息队列”。只要异步操作执行完成,就到消息队列中排队
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取消息队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
  4. 主线程不断重复上面的第三步

宏任务和微任务

微任务和宏任务皆为异步任务,但是微任务的优先级高于宏任务。

options请求和axios跨域及cookie传递问题

options请求: 检测请求的接口的信息(服务器接受的方法、跨域中测实际请求是否可以被服务器所接受)

什么情况下会发送options预检请求

浏览器将CORS请求分为两类:简单请求、非简单请求

  • 简单请求(不会发送预检请求)与非简单请求(请求之前发送options)的区别

    1. 请求方法:get、post、head

    2. http请求头信息:
      accept
      accept-language、
      content-language、
      content-type、
      last-event-id

    3. content-type:
      application/x-www-form-urlencoded、
      multipart/form-data、
      text/plain

      同时满足以上两个条件时,才是简单请求,否则为非简单请求

axios跨域并获取cookie

axios详细配置文档

核心:
1.axios全局配置中添加 withCredentials: true 跨域并保存cookie
2.在请求拦截器内设置 content-type:application/json

不是很理解第二点 加了之后不是把所有请求都变成非简单请求了吗
但是不加的话就报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'

// create an axios instance
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest' // ! 可能是非必须
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})

// request interceptor 请求拦截
service.interceptors.request.use(
config => {
config.headers = {
'Content-Type': 'application/json' // 注意:设置很关键
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)

后端相应的配置:

1
2
3
response.setHeader('Access-Control-Allow-Credentials',true); // 必须加上
response.setHeader('Access-Control-Allow-Origin','http://localhost:9528'); //不能写成 * ,一定要指定地址
response.setHeader('Set-Cookie','token=cowshield'); //传递的数据

问题描述

按理说应该是可以了
但是后台说因为有options预检请求
所以请求会有两次 但是他能获取的是options那次 后面那个请求获取不到
但是options请求又没有能保存传递的数据 导致一些问题

解决方案

我觉得不严谨 但是有效
大概是 写了一个拦截器 对于options请求直接返回true 就可以进入下一个请求了

参考资料

http://www.ruanyifeng.com/blog/2016/04/cors.html
https://blog.csdn.net/qq_25551573/article/details/80690583

2019年度总结

关键词:忙碌、压抑

今年可以说是压力最大的一年  比我高考压力还大
上半年基本一直在学前端相关的东西 积累项目经验
刚好在项目实训的时候也用上了 之前一直操心的实训算是轻松顺利完成

下半年一直在准备秋招相关的东西  边面试边查漏补缺
这两个学期我和考研党一样 早出晚归 基本天天呆在教室

秋招这几个月 是我迄今为止度过的最黑暗的日子
激动,希望,失望,自我怀疑,放弃 各种感情都经历过了
但也好在自己的坚持和努力 在秋招快结束的时候稀里糊涂的找了一份对我来说还不错的工作(七月份过了体检才能正式入职,感觉还是没底)
毕设这边虽然中途出现了一个小bug,但也解决了,开题也挺顺利的 希望明年能顺利毕业

十一月底学校的事情处理完,三方流程走完就马不停蹄的找了份实习
一个人拖着行李来到长沙 
目前是我实习刚好一个月,和我想象的不太一样,
干满三个月就不想干了,感觉在浪费时间,没有我自学学的多

大学生活也差不多快结束了 
讲真这段日子没什么可以留恋的
简直是糟透了的日子
唯一教会我的是如何习惯孤独
以前连上厕所都要拉着人去的我
现在已经习惯一个人吃饭,上课,自习,逛超市。。。

有时想想也觉得自己挺可怜的
不知道从什么时候开始  我好像也成了别人眼中的异类
我也不是不合群啊 只是好像适应不了这种快节奏的交友模式
不知道怎么融入 也没有机会融入

行吧 也不奢求太多
只希望2020年 工作和学习能顺顺利利
我可能没有那么强大的内心再来一次了

CSS相关

想闲暇的时候学点CSS动画的实现方法,
希望能持续更新 见证自己的成长 ,实现的效果也能越来越复杂好看!

波纹效果

充电动画

核心原理 filter的两个属性 blur(高斯模糊)px、contrast(对比度)%、hue-rotate(色相旋转) deg
有个疑问 就是w3c里面查的contrast的单位应该是 % 这个例子里面如果给它加了单位就实现不了了 就直接改变整体的对比度
blur(px): 默认为0,数值越大越模糊
contrast(%): 0%全黑,100%图像无变化。默认为1,超过100%图像会比原来亮
hue-rotate(deg):0deg图像无变化。默认是0deg。该值虽然没有最大值,超过360deg的值相当于又绕一圈。
Tips:
背景一般选择黑色或者白色,不然会受到contrast和hue-rotate的影响
在实现渐变效果的时候 filter: contrast(20) hue-rotate(0deg); 顺序不能变

内部设置高斯模糊 外层盒子不设置 对比度
外层盒子设置 对比度
(好像是在视觉上抵消内部盒子的高斯模糊)
内部盒子相遇时
内部盒子运动+渐变颜色
(filter: contrast(20) hue-rotate(0deg); 顺序不能变!!!)

成品效果:

【第六章】代理模式

定义: 为一个对象提供一个代用品或占位符,以控制对它的访问
关键:当客户不方便直接访问一个对象或者不满足需求的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求作出一些处理之后,再把请求转交给本体对象

不使用代理模式: 客户 ——> 本体
使用代理模式: 客户 ——> 代理 ——> 本体

一个解释代理模式的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
M 直接送花给 A
*/
var Flower = function(){}
var M = {
sendFlower: function(target){
var flower = new Flower()
target.receiveFlower(flower)
}
}
var A = {
receiveFlower: function(flower){
console.log('收到花'+flower)
}
}
M.sendFlower(A)

/*
M 通过 B 送花给 A
*/
var Flower = function(){}
var M = {
sendFlower: function(target){
var flower = new Flower()
target.receiveFlower(flower)
}
}
var B = {
receiveFlower: function(flower){
A.receiveFlower(flower)
}
}
var A = {
receiveFlower: function(flower){
console.log('收到花'+flower)
}
}
M.sendFlower(B)

/*
现在我们改变故事的背景设定,假设当 A 在心情好的时候收到花,小明表白成功的几率有60%,而当 A 在心情差的时候收到花,小明表白的成功率无限趋近于 0。
小明跟 A 刚刚认识两天,还无法辨别 A 什么时候心情好。如果不合时宜地把花送给 A,花被直接扔掉的可能性很大
但是 A 的朋友 B 却很了解 A,所以小明只管把花交给 B,B 会监听 A 的心情变化,然后选择 A 心情好的时候把花转交给 A,
*/
var Flower = function () {}
var M = {
sendFlower: function (target) {
var flower = new Flower()
target.receiveFlower(flower)
}
}
var B = {
receiveFlower: function (flow) {
A.hasGoodMood(function(){
A.receiveFlower(flow)
})

}
}
var A = {
receiveFlower: function (flow) {
console.log('get flower' + flow)
},
hasGoodMood: function(fn){
setTimeout(() => {
fn()
}, 1000);
}
}

M.sendFlower(B)

保护代理和虚拟代理

代理B可以帮助A过滤掉一些请求的叫做保护代理 (通过B来控制对A的访问)
代理 B 会选择在 A 心情好时再执行 new Flower叫作虚拟代理。 (虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建)

1
2
3
4
5
6
7
8
9
var B = {
receiveFlower: function(flower){
A.listenGoodMood(function(){ //监听A的好心情
var flower = new Flower() //延迟创建flower对象
A.receiveFlower(flower)
})
}
}

虚拟代理实现图片预加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

var oImg = (function(){
var imgNode = document.createElement('img')
document.body.appendChild(imgNode)
return {
setSrc : function(src){
imgNode.src = src
}
}
})()

//代理对象
var proxyImage = (function(){
var img = new Image() //获取的img对象
img.onload = function(){
oImg.setSrc(this.src) //img.src
}
return {
setSrc: function(src){
oImg.setSrc('./loading.gif')
img.src = src
}
}
})()
proxyImage.setSrc('http://....jpg')

代理的意义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//不使用代理的方式
var oImg = (function(){
var imgNode = document.createElement('img')
document.body.appendChild(imgNode)
var img = new Image
img.onload = function(){
imgNode.src = img.src
}
return {
setSrc : function(src){
imgNode.src = './timg.jpg'
img.src = src
}
}
})()
oImg.setSrc('https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=629448732,2965351611&fm=15&gp=0.jpg')

单一职责原则:为了减少代码的耦合性,方便维护和修改

代理和本体接口的一致性

调用时的代理接口和本地接口名称一致 也通过直接返回函数

优点:

  1. 用户可以放心地请求代理,他只关心是否能得到想要的结构
  2. 在任何使用本体的地方都可以替换成使用代理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var myImage = (function(){
    var imgNode = document.createElement('img')
    document.appendChild(imgNode)
    return function(src){
    imgNode.src = src
    }
    })()

    var proxyImage = (function(){
    var img = new Image
    img.onload = function(){
    myImage( this.src )
    }
    return function(src){
    myImage('./timg.jpg')
    img.src = src
    }
    })()
    proxyImage('https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=629448732,2965351611&fm=15&gp=0.jpg')

【第五章】策略模式

策略模式

比如要实现某一个功能有多种方案可以选择。比如压缩文件的程序,既可以选择zip算法,也可以选择gzip算法

定义: 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换

  • 策略模式的优缺点
    优点:

      1. 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
      2. 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。
      3. 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
      4. 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案。

    缺点:

      1. 使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在 Context 中要好
      2. 要使用策略模式,必须了解所有的 strategy,必须了解各个 strategy 之间的不同点,这样才能选择一个合适的 strategy。
    • 使用策略模式计算奖金

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      /*
      绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖
      */

      //最初代码实现
      var calculateBonus = function(performanceLevel,salary){
      if(performanceLevel === 'S')return salary*4
      if(performanceLevel === 'A')return salary*3
      if(performanceLevel === 'B')return salary*2
      }
      caculateBonus('B',2000) //输出 4000
      /*

      缺点:
      1. calculateBonus 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的逻辑分支。
      2. calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级 C,或者想把绩效 S 的奖金系数改为 5,那我们必须深入 calculateBonus 函数的内部实现,这是违反开放-封闭原则的。
      3. 算法的复用性差,如果在程序的其他地方需要重用这些计算奖金的算法呢?我们的选择只有复制和粘贴。

      */

      //使用策略模式重构代码
      //目的: 算法的使用与算法的实现分离
      var perfromanceS = function(){}
      perfromanceS.prototype.calculate = function( salary ){
      return salary*4
      }

      var perfromanceA = function(){}
      perfromanceA.prototype.calculate = function( salary ){
      return salary*3
      }

      var perfromanceB = function(){}
      perfromanceB.prototype.calculate = function( salary ){
      return salary*2
      }

      //奖金类
      var Bonus = function(){
      this.salary = null //原始工资
      this.strategy = null //绩效等级对应的策略对象
      }
      Bonus.prototype.setSalary = function(salary){
      this.salary = salary //设置员工的原始工资
      }
      Bonus.prototype.setStrategy = function(strategy){
      this.strategy = strategy
      }
      Bonus.prototype.getBonus = function(){
      return this.strategy.calculate(this.salary) //把计算奖金的操作委托给对应的策略对象
      }
      var bonus = new Bonus();
      bonus.setSalary( 10000 );
      bonus.setStrategy( new performanceS() ); // 设置策略对象
      console.log( bonus.getBonus() ); // 输出:40000
      bonus.setStrategy( new performanceA() ); // 设置策略对象
      console.log( bonus.getBonus() ); // 输出:30000
    • JavaScript版本的策略模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      /*
      我们让 strategy 对象从各个策略类中创建而来,这是模拟一些传统面向对象语言的实现。实际上在 JavaScript 语言中,函数也是对象,所以更简单和直接的做法是把 strategy直接定义为函数:
      */
      //策略对象
      var strategies = {
      "S": function( salary ){
      return salary*4
      },
      "A": function( salary ){
      return salary*3
      },
      "B": function( salary ){
      return salary*2
      }
      }
      //操作类
      var calculateBonus = function(level,salary){
      return strategies[level](salary)
      }
      calculateBonus('S',200) //800

【第四章】单例模式

定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。在 JavaScript 开发中,单例模式的用途同样非常广泛。试想一下,当我们单击登录按钮的时候,页面中会出现一个登录浮窗,而这个登录浮窗是唯一的,无论单击多少次登录按钮,这个浮窗都只会被创建一次,那么这个登录浮窗就适合用单例模式来创建。

核心:确保只有一个实例,并提供全局访问

  1. 实现单例模式

    用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var Singleton = function(name){
    this.name = name
    this.instance = null //存放已经生成的实例对象
    }
    Singleton.prototype.getName = function(){
    console.log(this.name)
    }
    Singleton.getInstance = function(name){
    if(!this.instance){
    this.instance = new Singleton(name)
    }
    return this.instance
    }

    var a = Singleton.getInstance('doreen')
    var b = Singleton.getInstance('sherry')
    a.getName() //doreen
    b.getName() //doreen
    console.log(a === b) //true

  2. 透明的单例模式

    使用的时候要和普通类一样通过new去创建实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 利用闭包的方式 让变量存在局部
    var CreateDiv = (function(){
    var instance
    var CreateDiv = function(html){
    if(instance){
    return instance
    }
    this.html = html
    this.init()
    return instance = this
    }
    CreateDiv.prototype.init = function(){
    var div = document.createElement('div')
    div.innerHTML = this.html
    document.body.appendChild(div)
    }
    return CreateDiv;
    })()
    var a = new CreateDiv('doreen')
    var b = new CreateDiv('sherry')
    console.log(a === b) // true

  3. 用代理实现单例模式

    CreateDiv变成普通类,它跟proxySingletonCreateDiv组合起来可以达到单例模式的效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    var CreateDiv = function(html){
    this.html = html
    this.init()
    }
    CreateDiv.prototype.init = function(){
    var div = document.createElement('div')
    div.innerHTML = this.html
    document.body.appendChild(div)
    }

    //代理类 proxySingletonCreateDiv
    var proxySingletonCreateDiv = (function(){
    var instance
    return function(html){
    if(!instance){
    instance = new CreateDiv(html)
    }
    return instance
    }
    })()

    var a = new proxySingletonCreateDiv('doreen')
    var b = new proxySingletonCreateDiv('sherry')
    console.log(a===b) // true

  4. 惰性单例

    是在需要的时候才创建对象实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* 创建一个登录框  需要登录的时候才动态创建登录框*/
    var createLoginLayer = (function () {
    var div
    return function () {
    if (!div) {
    div = document.createElement('div')
    div.innerHTML = '登录框'
    div.style.display = 'none'
    document.body.appendChild(div)
    }
    return div
    }
    })()

    var loginLayer = createLoginLayer()
  5. 通用的惰性单例

    惰性单例存在的问题

    1. 违反单一职责原则,创建对象和管理单例的逻辑都放在createLoginLayer对象内部
    2. 如果下次需要创建页面中唯一的iframe,或者script标签,用来跨域请求数据,就必须如法炮制,把createLoginLayer函数几乎照抄一遍
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      var createIframe = (function(){
      var iframe
      return function(){
      if(!iframe){
      iframe = document.createElement('iframe')
      iframe.style.display = 'none'
      document.body.appendChild(iframe)
      }
      return iframe
      }
      })()

      // 把代码抽离出来
      // 公用的部分 单例逻辑
      var getSingle = function(fn){
      var result
      return function(){
      return result || (result = fn.apply(this,arguments))
      }
      }
      // 创建对象的方法
      var createLoginLayer = function(){
      var div = document.createElement('div')
      div.innerHTML = '登录框'
      div.style.display = 'none'
      document.body.appendChil(div)
      return div
      }
      var createSingleLoginLayer = getSingle(createLoginLayer)
  • Copyrights © 2019-2023 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信