在开发中,许多开发模式也就不知不觉被我们运用到了,设计模式一书可以加深我们对这些概念的理解,或者说让我们头脑里有这种概念。
对《javascript设计模式与开发实践》一书的设计模式稍微做了点总结:

  • 单例模式
  • 惰性模式
  • 策略模式
  • 代理模式
    • 保护代理
    • 虚拟代理
    • 缓存代理
  • 迭代器模式

①单例模式
名称代表用法,指保证一个类只有一个实例(原理:用一个变量标志当前某个对象是否创建过,如果创建过则返回之前创建过的对象)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Human = function(name,age){
this.name = name;
this.age = age;
this.obj = null;
};
Human.Stupid = function(name,age){
if(!this.obj){
this.obj = new Human(name,age);
};
return this.obj;
};
var people1 = Human.stupid('烧饼');
var people2 = Human.stupid('Touko');
console.log(people1===people2); //true

衍生出一个知识点:惰性单例 ,只在需要时才创建对象实例,并且只创建唯一一个。

②策略模式
生活中我们要到达一个地方有很多种方法,比如去广州,可以搭大巴,也可以搭高铁的啦。要实现某个功能有多种方式可选择(原理:定义一系列的功能并把他们封装起来,使他们可以相互替换)。
使用策略模式定义算法要把算法和使用算法分离开来。稍微对书中的实例进行修改,可能比书中具有灵活性一点点点。WoW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var strategies = {  //用于计算奖金内容的函数
"S":function(salary){
return salary*4;
},
"A":function(salary){
return salary*3;
},
"B":function(salary){
return salary*2;
}
};
var calculateBonus = function(lavel,salary,num){
if(num !== undefined){ //如果有传入第三参
strategies[lavel] = function(salary){
return salary*num;
};
};
return strategies[lavel](salary);
};
console.log(calculateBonus('S',2000)); //8000
console.log(calculateBonus('C',3000,6)); //8000

③代理模式
代码模式可是说成中介,假如我现在有事不方便出门,我让我弟弟代替我出门买东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var yomtaaa={
shoppingn:function(target){
var money = 100;
target.payment(money);
}
};
var B = {
payment:function(money){
seller.gathering(money);
}
};
var seller = {
gathering:function(money){
console.log('收到钱为'+money);
}
};
yomtaaa.shoppingn(B);

有待更新….

事件冒泡:即由文档中嵌套层次最深的元素接收,逐级向上传播(FF、chrome、opera、safari以及ie9以上都会一直传播至window,而ie9以下的则只冒泡到html)。
事件捕获:即由不太具体的元素最先接收事件,而最具体的元素安排在最后,逐级向下传播。
事件流的3个阶段:事件捕获阶段、目标阶段、事件冒泡阶段,虽然规定由document最先接收,但实际上都从window开始。在addEventListener中的this始终指的都是调用他的元素。attachEvent事件处理程序中调用的this始终指向window。
addEventListener把第三个参数设置为true,让他在事件捕获阶段触发事件处理程序,相反的,如果为false则在事件冒泡阶段触发,从而达到和事件冒泡一致的效果。
为了兼容IE提供了attachEvent(),这个方法只针对IE,因为IE8没有事件流,所以用这个方法添加的事件处理程序都被添加到冒泡阶段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
oDiv1.addEventListener('click',function(){
console.log('父元素冒泡');
},false);
oDiv1.addEventListener('click',function(){
console.log('父元素捕获');
},true);
oDiv2.addEventListener('click',function(){
console.log('子元素捕获');
},true);
oDiv2.addEventListener('click',function(){
console.log('子元素冒泡');
},false);
oDiv3.addEventListener('click',function(){
console.log('孙元素捕获');
},true);
oDiv3.addEventListener('click',function(){
console.log('孙元素冒泡');
},false);


下面是控制台的输出,一切都显得那么自然。

1
2
3
4
5
6
父元素捕获
子元素捕获
孙元素捕获
孙元素冒泡
子元素冒泡
父元素冒泡

当我们把孙元素的事件处理程序交换位置后….

1
2
3
4
5
6
oDiv3.addEventListener('click',function(){
console.log('孙元素冒泡');
},false);
oDiv3.addEventListener('click',function(){
console.log('孙元素捕获');
},true);

控制台的输出如下

1
2
3
4
5
6
父元素捕获
子元素捕获
孙元素冒泡
孙元素捕获
子元素冒泡
父元素冒泡

如果你给代码最深处再嵌套一个div,你会发现冒泡和捕获位置错乱总是出现在最深一层。
还记得事件流包括3个阶段吗?事件捕获阶段、目标阶段、事件冒泡阶段。
点击孙元素时处于目标阶段然后是冒泡,冒泡之后才触发捕获(这跟事件顺序有关),接着再往上冒,这一点可以用event的eventPhase属性去验证。

在一所学校读了将近6年,只能说是无趣。一个习惯随波逐流的人在一个没有半点学习氛围的学校,
可想而知,唯有选择堕落盲目过着每一天,年少无知,并不清楚知识对于一个人的重要性。好在中间认识了形形色色的人,他们也是很有魅力的,他们的优点吸引了我,我们成为了好友。
靡不有初,鲜克有终。
渐渐你会发现丢了很多东西,例如时间、情感、友谊。
这几年的时间,生活也是发生了很大的改变,就我个人而言,这些改变是我不能接受的,但是已经适应了。生活大概就是不断的去接受新事物并且习惯他。
每个人都会在一段时间内去清楚自己喜爱的事物是什么,并且为之改变。
高二我才想认真读书,但是发现以前落下的东西太多了,一直都有在努力补救,竭尽全力,才明白做作业和学习是多痛苦的事,但是为了自己想要的也只能选择努力。
群里的人虽然好像关系很好,但是之间仍然缺乏感情,我们不会互相倾诉内心,每个人都不够了解对方。
近期得知班上的一位好友(简称老lo)要离开学校去追求自己的未来,才感觉到时间差不多到了,又要分别,三年又三年。
告别不可怕,最可怕的是今后失去了联系,这是我最不想看到的。
遇见一些人,然后再与他们告别。

除了IE,其余主流浏览器都支持DOM2的范围,然而IE用其特有的方式实现了范围特性(但IE9+支持)!

Document类型中定义了createRange(),这个方法属于document对象,首先检测浏览器是否支持他:

1
2
var supportRange=document.implementation.hasFeature('Range','2.0');
var supportRange2=(typeof document.createRange=='function');



这是初步创建范围后在控制台输出的,拿其中较为关键的属性来说

1.startContainer与endContainer他们通常是指向相同元素(前者为选区中第一个元素的父节点,后者为选区中最后一个元素的父节点)

2.startOffset通常等于父节点的childNodes的第一个子节点的索引,在兼容DOM的浏览器中空格算为一个文本节点。
要使用范围选择文档中的一部分,可以使用selectNode()和selectNodeContents(),他们都接收一个参数,即范围的起点,前者选择自身与子,后者只选择子。
为了更精确更方便地控制范围引入了一些更为方便的方法,如下:

1
2
3
4
setStartBefore(refNode) =>  将范围设置在refNode之前,即refNode是范围开始的第一个子节点,所以startContainer就是refNode的父节点
setStartAfter(refNode) => 将范围设置在refNode之后,即refNode的下一个同辈节点才是选区的第一个子节点,所以refNode不在范围之内
setEndBefore(refNode) => 将范围的终点设置在refNode之前,即refNode的上一个同辈节点是选区的最后一个子节点,所以refNode不在范围之内
setEndAfter(refNode) => 将范围的终点设置在refNode之后,即refNode是选区的最后一个子节点,所以refNode在范围之内

复杂操作:

1
2
setStart(node, offset) => 设置起点的位置,node是对startContainer的引用,偏移则是startOffset
setEnd(node, offset => 设置结束点的位置,node是对endContainer的引用,偏移则是startOffset



这段代码最后剩下Herld 虽然把范围内的文本删了,但是DOM结构仍然不会变。

与deleteContents()类似的方法有extractContents(),但后者这个方法会返回选区的内容,因此可以利用他来插入到页面的其他位置。

cloneContents()这个方法,如果只是要创建一个范围的副本而不去删除或移除范围的内容可以用他,语法名称代表用法,即克隆内容。
插入DOM范围中的内容:使用insertNode()方法可以在范围选区开始处插入一个节点。


在使用完范围之后,最好调用detach()方法,清理范围,回收内存,一旦分离了范围就不能再使用了。

1
2
range.detach();     // 从文档中分离
range=null; // 解除引用

/===========接下来说IE8及更低版本中的DOM范围=============/

准确来说IE高版本以及低版本都支持文本范围,是IE独有的特性,其他浏览器不支持,语法名称代表用法,所以主要用于文本。


选择某一区域用方法findText(),他会找到第一次出现的文本,然而他第二个参数如果为负值就会找最后一个匹配成功的文本,正值就会找到第一个匹配成功的文本,这也就说明了图中为什么找到hELLO而不是另外两个的原因了!

当中的变量found只要在匹配到文本时就会返回true,匹配不区分大小写在某些操作上也许会带来方便,可以用属性text,来获取范围选中的文本。

DOM范围有selectNode()而IE中有=>moveToElementText() =>这个方法接收一个DOM元素,并选择该元素的所有文本,包括HTML标签。如果在范围选区中包含HTML标签可以用属性htmlText读取范围全部内容。

属性text可以读取文本也可以修改文本,如下:



可见hELLO的部分已经被替代成hello,但标签没有被替代,如果想把标签替换掉可以用pasteHTML(),例如:range1.pasteHTML(‘hello‘);
但是上述做法会引起冲突,不提倡!

IE中使用方法duplicate()复制文本范围,如同cloneContents(),新创建的范围带有原范围相同的属性。例:var range=range.duplicate();

数组方法里的函数一般接收3个参数:数组元素、索引、数组本身;数组的方法都不会影响他本身。
①forEach()遍历数组,无返回值

1
2
3
var data=[1,2,3,4,5];
var sum=0;
sum=data.forEach(function(v){sum+=v;});

②map()遍历数组,返回新数组不改变原数组

1
2
3
var data=[1,2,3,4,5,,];
var j=0;
j=data.map(function(v,i){return v * v}); //虽然在输出j的时候最后一个没定义的没显示,但是他仍然存在,这里的length值是6。

③filter()遍历数组,传递的函数是用来逻辑判断的,返回true或false,判断成立则添加到返回的数组中,他会跳过稀疏数组中缺少的元素,返回到数组始终是稠密的

1
2
3
var data=[1,2,3,4,5,undefined,undefined,,0,9];
var f=0
f=data.filter(function(v){return v>0;}); //[1,2,3,4,5,9]

④every()遍历数组、逻辑判断,返回true或false,这个方法相当于“针对所有”,全部判断成立才返回true,一个不成立终止判断返回false。

1
2
3
var data=[1,2,3,4,5,0];
var j=0;
j=data.every(function(v,i){return v>0;});

⑤some()遍历数组、逻辑判断,返回true或false,这个方法相当于“存在或至少一个”,一个判断成立终止判断返回true,全部都不成立终止判断返回false。

1
2
3
var data=[-1,-2,-3,-4,-5,0];
var j=0;
j=data.some(function(v){return v>0;});

⑥reduce()遍历数组、将数组元素进行组合(如加减乘除)生成单个值;接收两个参数1.执行的函数 2.初始化函数的值

1
2
3
var data=[1,2,3,4,5,0];
var j=0;
j=data.reduce(function(x,y){return x+y;},2); \/\/17

注意:在使用空数组时并且省略第二个参数,也就是上面例子中第二个参数(2)才会导致类型错误。另外,如果数组只有一个元素,就只是简单返回原来的值

⑦reduceRight()用途和reduce差不多都是操作两个元素加减乘除、也可指定可选参(初始值),只是他是从右到左处理数组(从高索引到低索引)

1
2
3
4
var data=[4,5];
var j=0;
j=data.reduceRight(function(x,y){return x-y});
console.log(j); //输出1 如果把x和y互换位置会得到-1 这是因为高索引先作为参数传入

⑧indexOf和lastIndexOf()搜索数组中具有给定值的元素,返回找到的元素的索引,如果没有找到返回-1。前者的从头开始搜索,后者则相反。和其他方法不同。他还接收第二个参数,指定数组的索引(从哪开始搜索)

1
2
3
var data=[4,5,9,5,15,14,12];
var j=data.lastIndexOf(6)
console.log(j); //未找到,返回-1

transform是css3新增加的, 能够对元素进行移动、缩放、转动、拉长或拉伸,首先我们来说说rotate()。
①rotate()是2D的转换方法,他接收一个参数,deg(度),可以传入任意度数,默认是顺时针旋转,传入负数则是逆时针旋转。
transform的旋转是根据中心点进行旋转的,这一点可以用transform-origin来改变。注意,无论你如何旋转offsetWidth、offsetHeight、offsetLeft、offsetTop始终不变,始终是默认不旋转时的值。
②移动的translate()

  • 我们可以把他划分为三种情况:translate(x,y) => 水平垂直两个方向同时移动
    translateX(x)      =>     他只有一个参数,即水平移动
    translateY(y)      =>     他只有一个参数,即垂直移动
    
    应该有部分的人会认为这和定位差不多,都是用来控制元素位置的,但是实际上他和定位相比有很大的出入,有积极的影响也有消极的影响。
    translate()的计算是相对于父元素,除非脱离普通流。

    设置了transform:translate(10px,0px)让他往左便宜10像素,再来看看下面的代码。

    之后给他加了绝对定位,意料之中,他脱离文本流,但是他的left值会在translate的基础上再偏移10像素。
    他可能相对于定位来说会比较灵活,但是之后我又取消了绝对定位,给他加了一段js,局限性就显而易见了。
    translate的偏移量没有算在offsetLeft里,既然没有算在offsetLeft里,那会对我们的操作有一定的影响。
    ③scale()放大缩小,默认,宽度以中心点向左右两边扩大或缩小,高度则向上下两边扩大或缩小。

    可以看到在扩大缩小时一般会覆盖其他元素。注意,无论你如何扩大或缩小offsetWidth、offsetHeight、offsetLeft、offsetTop始终不变,始终是默认不变时的值。。
    ④skew()翻转
    transform:skew(30deg,30deg);表面上看这个方法挺好理解,也就是沿着x轴与y轴各旋转30度,但是后来显示的图形又让我很不理解。

    这还看不出什么,如果改为60deg+就很离谱了,之所以会产生变形的,这是因为我给元素限定的高度是固定的,所以他都会保持着固定的高度,同时为了保持倾斜,只能拉长本身。

    这是skewY(),因为给了固定宽度,所以只能垂直拉伸,宽度固定。