三木

你是谁就遇到谁


  • 首页

  • 标签

  • 归档

设计模式学习之三 — 观察者模式

发表于 2017-07-19 |

观察者模式介绍

又叫发布-订阅模式,是js事件模式中常见的一种,比如jquery的点击事件,在我们声明点击事件后,当我们用trigger方法去触发click事件时,事件会被响应,这就是一种监听者模式,

观察者模式应用场景

某一件事中,我们声明了一个事件A,然后想监听A方法的触发。

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
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);
}
}
};
var installEvent = function(obj){
for(var i in event){
obj[i] = event[i];
}
};

var salesOffices = {};
installEvent(salesOffices);

salesOffices.listen('squareMeter88', function( price ){
console.log('价格= ' + price);
});
salesOffices.listen("squareMeter110", function(price){
console.log('价格= ' + price);
});

salesOffices.trigger('squareMeter88', 2000000);
salesOffices.trigger('squareMeter88', 3000000);

以上代码,把事件的列表放在clientList数组中,listen方法是通过键值对的方式,key为事件,value为事件需要调用的方法,把他们放进数组,trigger是通过遍历数组去找到监听的事件A,然后通过遍历其value去触发对应的方法.
clientList的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
clientList:[
{
squareMeter88: [
fn1,fn2,fn3,...
]
},
{
squareMeter100: [
fn1,fn2,fn3,...
]
},
{
要监听的事件: [
要触发的方法1,要触发的方法2,要触发的方法3,...
]
},
]

完

设计模式学习之二 — 迭代器模式

发表于 2017-07-19 |

迭代器模式介绍

js内置方法中有很多迭代方法,比如forEach,filter,map,every等,这些迭代器都是对数组中的每一项进行操作。

迭代器模式应用场景

某一件事中,有ABC三种备选方法,依次尝试ABC,如果A不行,就尝试B,如果B不行,就尝试C。按照普通的思路我们会选择用if else方法来解决,但是这样不优雅,相对其中某一项更改时就需要深入到if else方法中进行修改,于是,我们可以使用迭代器模式

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
var A = {
try {
A方法的代码
} catch (e) {
return false; // A方法行不通,返回false
}
};

var B = {
try {
B方法的代码
} catch (e) {
return false; // B方法行不通,返回false
}
};

var C = {
try {
C方法的代码
} catch (e) {
return false; // C方法行不通,返回false
}
};

var resultSolution = function(){
for( var i = 0, fn; fn = arguments[ i++ ]; ){
var theFn = fn();
if( theFn !== false ){
return theFn;
}
}
}

var getTheSolution = resultSolution(A, B, C);

这样,我们就能最后获取到是最终可行的是哪个方法,加入再增加两个方案,就直接在resultSolution(A, B, C)中增加D, E即可,而不用再去写if else;

完

设计模式学习之一 — 策略模式

发表于 2017-05-09 |

js设计模式一直是该语言的核心之一,流弊的设计模式可以让代码功能更明显,函数原理更清晰,代码更通读易懂。

阅读全文 »

性能优化之函数防抖与节流

发表于 2017-04-20 |

web端性能优化一直是一个重点和难点。今天在看了一篇关于scroll滚动优化性能的文章后,认识了函数的防抖与节流,觉得其用处蛮大的。在这里记录一下。

阅读全文 »

从输入URL,到页面显示,这中间都发生了什么?

发表于 2017-04-19 |

引言

“从输入URL,到页面显示,这中间都发生了什么?”,常刷知乎的肯定都知道这在知乎上是一道经典题,把自己能理解的部分做个记录:


过程简述

  1. 浏览器通过域名查找对应IP;
  2. 浏览器通过IP地址与服务器建立链接;
  3. 浏览器与服务器通信:浏览器发送请求,服务器处理请求;
  4. 浏览器与服务器断开链接;
  5. 页面渲染;

下面就针对以上5条进行简单介绍

1.浏览器通过域名查找对应IP:

URL的基本结构: 协议名称 :// 服务器所在域名或者IP地址 : 端口号 / 所要访问的文件路径

PS:浏览器为了保证安全性,设定了跨域保护策略, 即窗口之间的通信必须满足使用相同协议, 相同域名, 相同端口,因此深入理解URL各组成部分的含义有助于我们判断两个窗口之间是否能互相通信。

以http://www.baidu.com/index.html为例,

http表示超文本传输协议

www.baidu.com是域名,由于IP协议规定的纯数字难以记忆,所以就用域名来代替便于记忆理解,百度实际的IP地址为119.75.217.109。就像每个人都有身份证号,身份证号是唯一的,但我们不能听过身份证号来跟别人交流,所以就叫别人的名字。在这里,身份证号就是IP,域名就是人的姓名。

DNS服务器之间的访问定位还是通过IP来定位的,但是我们输入的是域名,那么就产生了DNS协议,它的作用就是把域名转化为IP,提供该协议服务的服务器就叫DNS服务器。

index.html就是我们所要访问的文件名

2.浏览器通过IP地址与服务器建立链接;

解析出了服务器的IP地址,浏览器就与服务器建立连接了。连接过程就是俗称的TCP协议“三次握手”:

1.主机向服务器发送一个建立连接的请求(您好,我想认识您);

2.服务器接到请求后发送同意连接的信号(好的,很高兴认识您);

3.主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),自此,主机与服务器两者建立了连接。

3.浏览器与服务器通信

建立连接后,主机与服务器就开始进行通信。

1.浏览器根据URL生成HTTP请求,请求中包含请求头和请求体。

2.服务器接收到请求后,根据请求的内容,将相对应的文件发送给浏览器。

4.浏览器与服务器断开链接

发送完毕后,主机与浏览器断开连接,断开连接过程是一个“四次挥手”的过程,当服务器接收到断开连接的请求时,可能仍然有数据未发送完毕,所以服务器先发送确认信号,等数据全部发送完毕再同意断开。

5.页面渲染

1.浏览器开始解析HTML文件,按照从上到下的顺序解析。

2.HTML解析器将HTML转成成DOM,构建完DOM树后,触发DomContentLoaded事件。

3.CSS解析器将CSS解析为CSSOM(层叠样式表对象模式)

4.CSSOM和DOM开始合并渲染布局,计算各个节点的位置信息;

5.布局后的渲染树显示到界面上;

以上只是自己对整个过程的简单理解,往深了讲,这个题涵盖的知识面太多太广太深。

使用七牛云存储上传照片js-sdk

发表于 2017-04-11 |

七牛云存储为个人提供10G的免费云空间,对个人来说,可以作为上传照片文件的云空间还是蛮不错的。官方提供了多种服务器语言sdk,下面就介绍下如何使用js-sdk配合php实现上传照片功能。
1、首先需要注册一个七牛账户,并获取到accessKey和secretKey,可以在空间设置中查看。

2、需要一门服务器端语言像七牛服务器通信获取token,并将token返回到前端,这里以php为例获取token:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
require_once './autoload.php';
use Qiniu\Auth;

$bucket = 'save'; //空间名,不是空间域名
$accessKey = '账户accessKey';
$secretKey = '账户secretKey';

$auth = new Auth($accessKey, $secretKey);
$upToken = $auth->uploadToken($bucket);

echo $upToken;
?>

php依赖php SDK,需要到七牛官方下载七牛php SDK并引入。初始化实例便可获取token。

3、
前端这里以formData的方式提交照片:
七牛官方规定需要提交照片文件与token值,并以form表单的方式提交:

1
2
<input name="token" id="token" type="hidden" value="">
<input id="userfile" name="file" type="file" />

token的值需要按照步骤2先获取token。提交地址是七牛提供的url,不同地区提交到不同域名,例如我的demo是提交到:链接,错误的url七牛会报错,并提醒你正确提交的地址。
详情可见demo:http://www.sanmuweb.com/qiniu/index.html

defineProperty与数据绑定

发表于 2017-03-21 |

defineProperty用来声明一个对象,与对象字面量不同的是,它可以声明内部值get(设置)和set(获取)时的方法,如:

1
2
3
4
5
6
7
8
9
10
11
var obj = {};		
Object.defineProperty(obj, 'hello', {
get: function() {
console.log('get!');
},
set: function(value) {
console.log('set!');
}
});
obj.hello = 1; //打印set
obj.hello; //打印get

由此,我们可以利用此特性实现一个简单的双向绑定:

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {};		
Object.defineProperty(obj, 'hello', {
get: function() {
console.log('get!');
},
set: function(value) {
document.querySelector(".a").value = value;
document.querySelector(".b").innerHTML = value;
}
});
document.addEventListener('keyup', function(e){
obj.hello = e.target.value;
})

###documentFragment
先来看一段代码:

1
2
3
4
<div id="app">
<input type="text" class="a">
<span class="b"></span>
</div>

1
2
3
4
5
6
7
8
9
10
function nodeToFragment(node)
{
var flag = document.createDocumentFragment();
while (node.firstChild){
flag.append(node.firstChild); //append方法会把节点从A移动到B
}
return flag;
}
var dom = nodeToFragment(document.getElementById("app"));
console.log(dom);

这里打印会把div标签里的所有标签打印出来,而html中div的标签内为空,是因为这段代码把div里需要操作的标签全都转到了documentFragment里了,因为在documentFragment里操作标签比在dom中操作要性能好很多。

###简单的通过实例进行数据绑定:

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
function compile(node, vm){		//解析赋值
var reg = /\{\{(.*)\}\}/;
if(node.nodeType === 1){
var attr = node.attributes;
for(var i = 0; i < attr.length; i++){
if(attr[i].nodeName == "v-model"){
var name = attr[i].nodeValue;
node.value = vm.data[name];
node.removeAttribute('v-model');
}
}
}
if(node.nodeType === 3){
if(reg.test(node.nodeValue)){
var name = RegExp.$1;
name = name.trim();
node.nodeValue = vm.data[name];
}
}
}
function nodeToFragment(node, vm) //把节点转移到documentFragment进行操作
{
var flag = document.createDocumentFragment();
while (node.firstChild){
compile(node.firstChild,vm);
flag.append(node.firstChild); //append方法会把节点从A移动到B
}
return flag;
}
function Vue(options)
{
this.data = options.data;
var id = options.el;
var dom = nodeToFragment(document.getElementById(id), this);
console.log(dom)
document.getElementById(id).appendChild(dom);
}
var vm = new Vue({
el:'app',
data:{
text:'hello World'
}
})

接下来再利用defineProperty进行set数据绑定,即可实现动态双向绑定:
修改代码:

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
function compile(node, vm){		//解析赋值
var reg = /\{\{(.*)\}\}/;
if(node.nodeType === 1){
var attr = node.attributes;
for(var i = 0; i < attr.length; i++){
if(attr[i].nodeName == "v-model"){
var name = attr[i].nodeValue;
node.addEventListener('input', function(e){
vm.data[name] = e.target.value;
})
node.value = vm.data[name];
node.removeAttribute('v-model');
}
}
}
if(node.nodeType === 3){
if(reg.test(node.nodeValue)){
var name = RegExp.$1;
name = name.trim();
node.nodeValue = vm.data[name];
}
}
}
function nodeToFragment(node, vm) //把节点转移到documentFragment进行操作
{
var flag = document.createDocumentFragment();
while (node.firstChild){
compile(node.firstChild,vm);
flag.append(node.firstChild); //append方法会把节点从A移动到B
}
return flag;
}
function Vue(options)
{
this.data = options.data;
var data = this.data;
observe(data, this);
var id = options.el;
var dom = nodeToFragment(document.getElementById(id), this);
console.log(dom)
document.getElementById(id).appendChild(dom);
}
function defineReactive(obj, key, val)
{
Object.defineProperty(obj, key, {
get:function(){
return val
},
set:function(newVal){
if(newVal === val) return;
val = newVal;
console.log(val);
}
})
}
function observe(obj, vm)
{
Object.keys(obj).forEach(function(key){
defineReactive(vm.data, key, vm.data[key]);
})
}
var vm = new Vue({
el:'app',
data:{
text:'hello World'
}
})

以上便可实现在输入时,同时打印出data.text,已经和vm.data.text实现了绑定

算法与数据结构JS版

发表于 2017-03-08 |

本篇博客为记录学习《数据结构与算法JavaScript语言描述》的笔。
1、递归算法,比较常见,属于比较简单类型的。

返回数字1-5的之间的整数相乘结果

1
2
3
4
5
6
7
8
function factorial(number){
if(number == 1){
return 1;
} else {
return number * factorial(number-1); //此算法核心在这一句
}
}
console.log(factorial(5)); //150

reduce迭代方法同样可以实现累加(乘)效果

1
2
3
4
5
var arr = [7, 4, 122, 20, 3333];
var newArr = arr.reduce(function(a,b){
return a+b;
});
console.log(newArr);

2、sort排序,函数中传入ab表示第一个参数和第二个参数,两者相减,返回小于0的数表示是从小到大排序

1
2
3
4
5
6
var arr = [7, 4, 122, 20, 3333];
var newArr = arr.sort(function(a,b){
return a-b;
return b-a; //从大到小排序
});
console.log(newArr);

3、创建二维数组,原理是先创建外层数组元素,再对外层元素创建内层数组元素

1
2
3
4
5
6
7
8
9
10
11
12
13
Array.matrix = function(numrows, numcols, initial){
var arr = [];
for(var i = 0; i < numrows; i++){
var col=[];
for(var j = 0; j < numcols; j++){
col[j] = initial;
}
arr[i] = col;
}
return arr;
}
var nums = Array.matrix(5,5,0);
console.log(nums)

4、求3个学生的平均成绩:

1
2
3
4
5
6
7
8
9
var grades = [[89, 77, 78], [76, 82, 81], [91,69,70]];
for(var i = 0; i < grades.length; i++){
var col = grades[i];
var total = col.reduce(function(a,b){ //累加求总分
return a+b
});
var pingjun = total / col.length;
console.log("text" + (i+1) + "成绩:" + pingjun);
}

5、快速排序算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function qSort(arr){
if(arr.length == 0){
return [];
}
var left = [];
var right = [];
var pivot = arr[0];
for(var i = 1; i < arr.length; i++){
if(arr[i] < pivot){
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return qSort(left).concat(pivot, qSort(right));
}
var a = [];
for(var i = 0; i < 100000; i++){
a[i] = Math.floor((Math.random()*100)+1)
}

书上说这种快速排序算法对大型数据排序很快,但是我经过测试,对100000个数进行排序,花了将近1000ms,而如果用sort方法,只需要40ms左右,所以看来sort还是比这个快速排序算法好用。

PHP操控mongodb命令小结

发表于 2017-02-14 |

最近学习了非关系型数据库mongodb,以json方式来存储数据,数据内容更灵活。以下是php操作mongodb常用命令,记录一下,便于以后查询。

阅读全文 »

svg实现动画效果

发表于 2017-02-13 |

web动画除了css3的帧动画外,还可以用svg来实现动画效果,例如网上的一个例子参考链接,这个动画就是用svg的animate标签来实现的,同样是通过软件绘制好图形,path标签中,d属性表示路径点坐标,然后使用animate标签来切换路径,animate标签是path标签的子级,使用 attributeName=“d”表示切换父标签path的d属性,然后values属性则等于要切换的另一个路径图形,多个图形可以使用分号”;”来区分,例如一个丑陋的图形变化参考链接
参考信息:http://www.zhangxinxu.com/wordpress/2014/08/so-powerful-svg-smil-animation/

1…345…8

三木

前端开发,web,javascript,技术博客,程序员

79 日志
17 标签
© 2023 三木
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4| 冀ICP备16017076号