我是卡卡卡颂


  • 首页

  • 归档

  • 标签

设计模式--发布订阅模式

发表于 2017-03-13

简单的发布订阅模式的通用实现

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
//发布订阅功能
var event= {
clientList: [],
//订阅
listen: function(key,fn) {
if (!this.clientList[key]) {
this.clientList[key]=[];
}
//订阅的消息进缓存
this.clientList[key].push(fn);
},
//发布
trigger: function() {
var key = Array.prototype.shift.call(arguments),
fns=this.clientList[key];
if (!fns || fns.length===0) {
return false;
}
for (var i=0,fn;fn=fns[i++];) {
fn.apply(this,arguments);
}
},
//取消订阅
remove:function(key,fn) {
var fns=this.clientList[key];
if (!fns) {
return false;
}
//没有传入回调,表示需要取消key对应的所有回调
if (!fn) {
fns && (fns.length =0);
} else {
for (var i=0;i<fns.length;i++) {
var _fn=fns[i];
if (fn===_fn) {
fns.splice(i,1);
}
}
}
}
}
//为对象添加发布订阅
var installEvent=function(obj) {
for (var i in event) {
if (event.hasOwnProperty(i)) {
obj[i]=event[i];
}
}
}
  • 生成多个发布订阅对象会有多余的浪费。
  • 考虑到有些异步请求,有可能发布了消息,而对该消息订阅的代码还没加载出,所以应该允许先订阅再发布。(设立一个存放离线事件的堆栈)
  • 对于还没有被订阅的事件,在堆栈中保存发布的动作。等有对象来订阅事件时重新遍历发布堆栈里的事件。
  • 对于离线事件,生命周期只有一次。
  • 当订阅的事件多了也能产生命名冲突,应该允许创建命名空间。
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
//先发布后订阅
Event.trigger('click',1);
Event.listen('click',function(a) {
console.log(a);
})
//使用命名空间
Event.create('namespace1').listen('click',function(a) {
console.log(a);
})
Event.create('namespace1').trigger('click',1);
//代码实现
var Event=(function() {
var global=this,
Event,
_default='default';
Event=function() {
var _listen,
_trigger,
_remove,
_slice=[].slice,
_shift=[].shift,
_unshift=[],unshift,
namespaceCache={},
_create,
find,
each=function(ary,fn) {
for (var i=0;i<ary.length;i++) {
var cur=ary[i];
fn.call(cur,cur,i);
}
};
_listen=function(key,fn,cache) {
if (!cache[key]) {
}
}
}
})()

css学习--padding

发表于 2017-03-11
  • 对于block水平元素, 当box-sizing:border-box;或width非auto; padding大小超过宽高时影响尺寸。
  • 对于inline水平元素,垂直padding不影响尺寸,但是影响背景色(会占据空间)。当超过父级盒子高度,改元素scrollHeight改变
  • 可以用 span border 加padding实现高度可控的 分割线 a | b
  • padding不支持任何形式的负值,百分比值是相对于宽度计算的
  • inline元素默认的高度宽度细节有差异, 空inline+padding高宽也不等,会出现空白节点(strut),需要设置font-ize:0;

设计模式--代理模式

发表于 2017-03-11

代理模式分类

  1. 保护代理
    控制不同权限对象对目标对象对访问。
  2. 虚拟代理
    把一些开销很大对对象延迟到真正需要他对时候才去创建。下面用虚拟代理实现图片预加载:
    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 myImage=(function() {
    var imgNode=document.createNode('img');
    document.body.appendChild(imgNode);
    return {
    setSrc: function(src) {
    imgNode.src=src;
    }
    }
    })()
    var proxyImage=(function() {
    var img=new Image;
    img.onload=function() {
    myImage.setSrc(this.src);
    }
    return {
    setSrc: function(src) {
    myImage.setSrc('file://loading.jpg');
    img.src=src;
    }
    }
    })()
    proxyImage.setSrc('src.jpg');

设计原则

  1. 用户可以放心的请求代理,只关心能否得到想要的结果。
  2. 在任何使用本体的地方都可以替换成使用代理。

缓存代理

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
//用代理模式实现缓存
//先实现一个目标函数 这里是求阶乘
var mult=function() {
console.log('开始计算阶乘');
var a=1;
for (var i=0,cur=arguments[i];i<arguments.length;i++) {
a=a*cur;
}
console.log('输出:',a);
return a;
}
//缓存结果的代理 调用代理即可
var proxyMult=(function() {
var cache={};
return function() {
var args=Array.prototype.join.call(arguments,',');
if (args in cache) {
console.log('输出:',cache[args]);
return cache[args];
}
return (cache[args]=mult.apply(this,arguments));
}
})()
//这里我们可以更进一步,创建一个 缓存代理的工厂,就可以接受不同的运算规则了
var createProxyfactory=function(fn) {
var cache={};
return function() {
var args=Array.prototype.join.call(arguments,',');
if (args in cache) {
return cache[args];
}
return cache[args]=fn.apply(this,arguments);
}
}

设计模式--策略模式

发表于 2017-03-09

策略模式定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。至少由两部分组成:

1.一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
2.环境类context。context接受客户请求,把请求委托给一个策略类。

设计模式--惰性单例模式

发表于 2017-03-09

单例在ng里即service的实现。

创建一个通用的惰性单例模式,满足:

  1. 只有一个实例
  2. 提供全局访问
  3. 符合单一职责原则
    1
    2
    3
    4
    5
    6
    7
    //通用对象 只会返回一个实例
    var getSingle=function(fn) {
    var result;
    return function() {
    return result || (result=fn.apply(this,arguments));
    }
    }

angular源码分析5--provider实现原理

发表于 2017-03-05

原生angular中的provider和$injector主要作用是:

  1. 注册组件(controller directive service)
  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
    var Provider={
    //保存所有组件对应的工厂函数
    _providers:{},
    //注册组件
    _register: function(name,fn) {
    this._providers[name]=fn;
    },
    //要来注册service
    service: function(name,fn) {
    return this._register(name+Provider.SERVICE_SUFFIX,fn);
    },
    //要来注册directive 
    directive: function(name,fn) {
    return this._register(name+Provider.DIRECTIVE_SUFFIX,fn);
    },
    //要来注册controller
    //初始化controller缓存里保存的是工厂函数,可以多次初始化同一个controller
    controller: function(name,fn) {
    return this._register(name,function() {
    return fn;
    })
    }
    }

上面实现了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
33
34
35
36
37
38
39
40
41
42
43
44
45
var Provider = {
// ...
//缓存初始化后的组件
_cache: {},
//返回一个数组 数组为当前函数工厂函数的传参,也就是依赖组件的名称
annotate: function (fn) {
var res = fn.toString()
.replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg, '')
.match(/\((.*?)\)/);
if (res && res[1]) {
return res[1].split(',').map(function (d) {
return d.trim();
});
}
return [];
},
//接下来两个函数将互相调用,调用组件的初始化工厂函数,并循环遍历解决依赖(以及依赖的依赖)
//解决依赖的意思就是将依赖组件的名称转换成对应组件的工厂函数,传入。
//传入组件名,locals为所有本地依赖的对象集合,返回初始化后的组件
get: function(name,locals) {
//如果已有缓存的初始化工厂函数,返回他
if (this._cache[name]) {
return this._cache[name];
}
var provider=this._providers[name];
if (!provider || typeof provider !=='function') {return;}
return (this._cache[name] = this.invoke(provider,locals));
},
//用于初始化一个组件。如果该组件依赖的组件还未初始化,则会先初始化依赖的组件
invoke: function(fn,locals) {
locals= locals || {};
//通过依赖名找出依赖的工厂函数
var deps=this.annotate(fn).map(function(item) {
return locals[item] || this.get(item,locals);
},this);
return fn.apply(null,deps);
}
}

构造带记忆功能的函数

发表于 2017-03-04

构造一个带有之前结果缓存的函数,减少不必要的计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
传入 初始缓存记忆的数组memo 和计算公式formula
返回 一个管理memo存储和在需要时调用formula的函数recur
*/
var memoizer = function(memo,formula) {
var recur=function(n) {
var result=memo[n];
if (typeof result !=='function') {
result=formula(recur,n);
memo[n]=result;
}
return result;
};
return recur;
}

用我们的memoizer函数定义fibonacci函数

1
2
3
var fibonacci=memoizer([0,1],function(recur,n) {
return recur(n-1)+recur(n-2);
})

用我们的memoizer函数定义阶乘函数

1
2
3
var fibonacci=memoizer([1,1],function(recur,n) {
return n*recur(n-1);
})

DOM盒模型的各种width和height属性

发表于 2016-10-08

client系列(和内容是否溢出盒子无关)

  1. clientWidth:内容的宽度+左右padding
  2. clientHeight:内容的高度+上下padding
  3. clientLeft:左边框的宽度(border-left-width)
  4. clientTop:上边框的宽度(border-top-width)

offset系列(和内容是否溢出盒子无关)

  1. offsetWidth:clientWidth+左右边框
  2. offsetHeight:clientHeight+上下边框
  3. offsetLeft:子元素的外边框相对于父定位元素(没有则指向body)的内边框的偏移量
  4. offsetTop:子元素的外边框相对于父定位元素(没有则指向body)的内边框的偏移量
  5. offsetParent: 指向子元素的定位父元素,如果没有就指向body,body指向null。

scroll系列

  1. scrollWidth:当内容未溢出容器时等于clientWidth.当溢出时是真实宽度+左右padding
  2. scrollHeight:当内容未溢出容器时等于clientHeight。当溢出时是真实高度+上下padding
  3. scrollTop:滚动条卷去的高度
  4. scrollLeft:滚动条卷去的宽度
    注:如果设置了overflow:hidden;各浏览器结果不同。

取值问题

我们通过这13个属性获取的都是近似的整数。

浏览器的盒模型

  1. 对于浏览器而言,clientWidth/clientHeight是当前浏览器可视窗口的宽高(一屏幕的宽高)
  2. 对于浏览器而言,scrollWidth/scrollHeight是当前页面的真实宽高(所有屏加起来宽高的近似值)

浏览器盒模型属性获取/设置方法的封装

1
2
3
4
5
6
7
function win (attr,value) {
if (typeof value !=='undefined') {
return document.documentElement[attr] ||document.body[attr];
}
document.documentElement[attr]=value;
document.body[attr]=value;
}

获取某元素相对浏览器的偏移量 方法的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function offset (curEle) {
var l=curEle.offsetLeft;
var t=curEle.offsetTop;
var par=curEle.offsetParent;
//一层层定位祖先向上累加
while (par) {
if (window.navigator.userAgent.indexOf('MSIE 8.0')!==-1) {
l+=par.clientLeft;
t+=par.clientTop;
}
l+=par.offsetLeft;
t+=par.offsetTop;
par=par.offsetParent;
}
return {left:l,top:t};
}

计算后的样式值的获取

一般我们用 元素.style.样式来获取元素的样式。这样获取到的是元素的行内样式。如果想获取元素经过计算后的样式需要使用window.getComputedStyle(ele,null);
经过浏览器计算过的样式是指 只要当前标签可以在浏览器中呈现出来,那么他所有样式都是经过浏览器计算过的,哪怕那些样式你没有写也可以获取到。
window.getComputedStyle(box,null)接收两个参数。 box指要获取样式的元素,null指伪类。

获取计算后样式的 方法封装

1
2
3
4
5
6
7
8
9
function getCss (curEle,attr) {
var val=null;
try {
val=window.getComputedStyle(curEle,null)[attr];
} catch (e) {
val=curEle.currentStyle[attr];
}
return val;
}

移动端事件基础

发表于 2016-10-06

移动端事件

  • click:单击事件,不是点击事件(有300ms延迟,是为了确定是否是单击)
  • load:加载成功
  • scroll:滚动
  • blur
  • change
  • input:代替keyup keydown
  • TOUCH事件模型:处理单手指操作
  • GESTURE事件模型:处理多手指操作

TOUCH事件模型

  • touchstart
  • touchmove
  • touched
  • touchcancel

    GESTURE事件模型

  • gesturestart
  • gesturechange
  • gestureend

IE条件注释

发表于 2016-10-05
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--[if !IE]>除IE外都可识别<![endif]-->
<!--[if IE]> 所有的IE可识别 <![endif]-->
<!--[if IE 5.0]> 只有IE5.0可以识别 <![endif]-->
<!--[if IE 5]> 仅IE5.0与IE5.5可以识别 <![endif]-->
<!--[if gt IE 5.0]> IE5.0以及IE5.0以上版本都可以识别 <![endif]-->
<!--[if IE 6]> 仅IE6可识别 <![endif]-->
<!--[if lt IE 6]> IE6以及IE6以下版本可识别 <![endif]-->
<!--[if gte IE 6]> IE6以及IE6以上版本可识别 <![endif]-->
<!--[if IE 7]> 仅IE7可识别 <![endif]-->
<!--[if lt IE 7]> IE7以及IE7以下版本可识别 <![endif]-->
<!--[if gte IE 7]> IE7以及IE7以上版本可识别 <![endif]-->
123…5
BetaSu

BetaSu

一言不合撸源码

50 日志
© 2017 BetaSu
由 Hexo 强力驱动
主题 - NexT.Pisces