HTML5-03-design.pattern

目录

1.设计模式... 2

1.1设计模式的概述... 2

1.2 23种设计模式... 2

1.3设计模式,框架,架构与工具库... 2

1.4原型与继承(回顾)... 3

2.工厂模式... 3

2.1简单工厂模式... 3

2.2(寄生)增强工厂... 4

2.3安全工厂(安全类) 4

2.4工厂方法... 5

2.5原型模式... 5

2.6单例模式... 6

2.7惰性单例... 7

2.7.1常规用法... 7

2.7.2静态变量... 8

2.8适配器模式(结构型的设计模式)... 9

2.8.1适配器模式内涵... 9

2.8.2适配器应用... 9

3. 观察者模式... 10

3.1观察者模式内涵... 10

3.2实现观察者模式... 11

4.组合模式(部分——整体模式)(结构型设计模式) 12

4.1组合模式理论... 12

4.2组合模式应用一一新闻模块... 13

5.策略模式... 15

6.命令模式... 15

6.1内涵... 15

6.2应用(canvas)... 16

7.迭代器模式... 16

8.遍历器... 17

9.委托模式... 18

9.1数据分发... 18

9.2事件委托... 18

9.2.1事件委托... 18

9.2.2jQuery中的事件委托(delegate)... 19

10.节流器... 20

10.1定义与特点... 20

10.2节流与防抖... 20

10.3实现jQuery. 21

1.设计模式

1.1设计模式的概述

什么是设计模式: 一套可以被复用的,编目分明的经验总结。

作用:让我们写的代码可复用,提高我们的代码的可维护性

1995年Erich Gamma,Richard Helm, Ralph Johnson John Vlissides合作出版了Design Patterns - Elements of Reusable Object-Oriented Software一书, 在此书中共收录了23个设计模式。

这四位作者在软件开发领域里也以他们的匿名著称Gang of Four(四人帮,简称GoF),并且是他们在此书中的协作导致了软件设计模式的突破。有时这个匿名GoF也会用于指代前面提到的那本书,第一次将设计模式提升到理论高度,并将之规范化,本书提出了23种基本设计模式,自此,在可复用面向对象软件的发展过程中,新的大量的设计模式不断出现。

我们学习的是这23中设计模式中,在js中应用很广的一些,还有js中一些特殊性, 新的模式。

1.2 23种设计模式

创建型模式:解决创建类或者实例化对象时候,产生的问题。如:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。(5)

结构型模式:解决类或者对象在组合在一起时候的问题。如:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。(7)

行为型模式:解决类或者对象之间耦合,职责关系的问题。如:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、 状态模式、 策略模式、职责链模式(责任链模式)、访问者模式。(11)

学习设计模式的目的,就是用它来解决一些实战中的问题, 我们学习要先学习这些问题,再用设计模式给出一套解决问题方案。在前端实际开发中,又引入了许许多多新的模式在后面介绍。

1.3设计模式,框架,架构与工具库

设计模式:就是可以被复用,众人知晓,编目分明的经验的总结,侧重于解决某个(些)问题的

框架:在某一些软件领域中,将公用的部分抽象提出出来形成统一的整体。 往往是一个半成品,我们需要对它们进行再次加工完成项目的开发。设计了一套思想,引导我们去实现

比较设计模式与框架:设计模式是一个单一的思想, 就是为了解决某一类问题框架是一套思想的统一,因此可以包含多个设计模式,他们在解决问题的思想上是统一的(一个框架中可以包含多个设计模式)

架构:设计的蓝图,没有具体的实现,框架是一种实现了的架构

工具库:只是一些方法的封装,每一个方法之间具有独立性,框架也是一套方法的封装,框架中的方法彼此之间是有联系的,彼此分工合作实现需求。

1.4原型与继承(回顾)

原型:是每一个函数天生拥有的属性,它的值是一个对象

特点:在类原型中的所有内容天生可以被每一个实例化对象所访问

继承:类与类之间的继承

继承的方式有:

1类式继承

2构造函数式继承

3组合式继承

4寄生式继承

5寄生组合式继承

2.工厂模式

2.1简单工厂模式

解决的问题:在创建对象的时候往往会衍生一些副作用(例如创建了全局变量),我们可以将这个创建过程封闭起来,创建完成,将结果返回,这样就可以屏蔽这些副作用对全局作用域的影响

实现:通过将创建过程封装在一个函数内,并将创建的结果返回

特点:

1避免副作用的产生

2我们看不到他的创建过程,只能看到创建的结果

3往往创建的是一个单一的产品

4我们可以简简单单对简单工厂模式的工厂方法改造实现更多的需求

//创建一个对象,要定义许多变量(污染全局作用域),为避免污染且简化创建的过程,

        //此时,可以使用简单工厂

        function factory(name, age, sex) {

            var obj = { //定义对象

                name: name,age: age,sex: sex,

intro: function () {console.log('我是' + this.name + ',性别是' + this.sex + ',今年' + this.age + '岁了');}}

            return obj  }// 返回创建的结果

var obj1 = factory('小白', 20, '男');   //简化对象的创建避免污染全局作用域

console.log(obj1);//{name: "小白", age: 20, sex: "男", intro: ƒ}

obj1.intro();//我是小白,性别是男,今年20岁了

2.2(寄生)增强工厂

作用:对类实例化的时候,对类拓展(实例化对象),而不会影响原类

特征

1在工厂内部创建类的实例化对象(寄生)

2对实例化对象进行拓展(增强)

3将实例化对象返回(工厂)

function factory(name, age, sex) {//增强型工厂

//创建

var p = new People(name, age, sex);//相当于寄生,就是新起一个灶台

//增强创建的结果

if (p.sex === '女') {

p.msg = 'hello',p.say = function (){console.log(this.msg);}}

//注意:fn的此处如果写p.msg指向的是p,而写this,this则指向People

return p;  }//返回结果

        //通过工厂创建实例

        var p1 = factory('小白', 56, '男');

        var p2 = factory('小red', 46, '女');

        var p3 = factory('小澜', 22, '女');

        // 两个有相同的行为

        p2.intro();//我是小red,性别是女,今年46岁了

        console.log(p1, p3);

// People {name: "小白", age: 56, sex: "男"}

// People {name: "小澜", age: 22, sex: "女", msg: "hello", say: ƒ}

        p2.say();//hello

        // p1.say();//没有被增强,报错

2.3安全工厂(安全类)

又叫安全类

步骤

判断当前对象是否是当前类的实例化对象

如果是

执行类的业务逻辑(对向前对象赋值)

如果不是

执行简单工厂业务逻辑(返回一个新的实例化对象)

            function People(name, age, sex) {//通过类来创建对象

            if (this instanceof People) {//判断this是否指向实例对象

this.name = name;   this.age = age;   this.sex = sex;//正常的存储数据

} else {  //this指向window时,重新实例化

                return new People(name, age, sex) }}//重新实例化

        People.prototype.intro = function () { //注意:方法一般放在原型上

console.log('我是' + this.name + ',性别是' + this.sex + ',今年' + this.age + '岁了'); }

        var p1 = new People('小白', 20, '男');  //创建实例

        var p2 = People('小红', 21, '女');

        console.log(p1);//People {name: "小白", age: 20, sex: "男"}

        console.log(p2);//undefined(没有if进行判断时)

        //判断后输出:People {name: "小红", age: 21, sex: "女"}

2.4工厂方法

作用:

用来处理多个类的创建的

本质:

对多个类的创建的封装

优势:

可以使用户不必关心多个类的创建,而将精力放在工厂方法的实现上

function Dog(name) {this.type = 'cat';this.name = name;} //创建狗

function Cat(name) {this.type = 'dog';this.name = name;} //创建猫

function Pig(name) {this.type = 'pig';this.name = name;} //创建猪

        // //创建动物//太麻烦

        // var dog = new Dog('旺财');

        // var pig = new Pig('二师兄');

        // var cat = new Cat('三师弟');

        //👇工厂方法去创建//原始函数改动只用修改/维护工厂内部的数据即可

        function animal(name, type) { //通过类型区分不同的类

            if (type === 'dog') {return new Dog(name);

            } else if (type === 'cat') {return new Cat(name);

            } else if (type === 'pig') {return new Pig(name);}}

var cat = animal('老大', 'cat');var dog = animal('老er', 'dog');

        var pig = animal('老san', 'pig');

console.log(cat, dog, pig);

        // Cat { type: "狗", name: "老大" }

        // Dog { type: "猫", name: "老er" }

        // Pig { type: "猪", name: "老san"}

2.5原型模式

定义:

类的原型指向父类的实例对象,这样,让该类创建的实例可以共享父类原型上的数据。

       就是一个类式继承(原型式继承)。

是一种创建型的设计模式

       是基于JavaScript的原型链继承原理实现的

  //定义类:实例数据(自身数据)、原型数据、静态数据

function Book(title) {this.title = title;  }//实例数据(自身数据)

        //父类原型上的//原型数据

Book.prototype.getTitle = function () {console.log(this.title);}

        //继承

        function JsBook(title, price) {

this.title = title;  this.price = price;  }  //存储其他数据

        JsBook.prototype = new Book(); //让实例对象共享父类原型属性方法

        //创建子类实例

        var js = new JsBook('js设计模式', 59);

        js.getTitle();//js设计模式

        console.log(js);// JsBook {title: "js设计模式", price: 59}

2.6单例模式

定义:

是能被实例化一次的类或者对象(只能存在一个)

特点:

只允许实例化一次的对象类。

对于十分复杂的对象类,往往可以节省资源占用

通常也被用来管理命名空间

作用:管理命名空间,管理数据、方法的存储

应用:

一些代码库中命名空间就是通过单例模式实现的管理数据的存储,例如模拟静态变量

 //放在闭包中//此种方法只实例化一次//但是不调用也会实例化一次

        var Single = (function () {

            function Demo() { //定义类

                console.log('实例化了');

                this.msg = 'hello'; } //此处省略一千亿万行数据

            Demo.prototype.getMsg = function () {//方法

                console.log(this.msg);

            }//此处省略一千亿万行方法

            //存储类的实例化对象

            var instance = new Demo();

            return function () { //暴露一个接口

                return instance; }  })()//返回s

//获取数据

        var s1 = Single();console.log(s1);//Demo {msg: "实例化了"}

        var s2 = Single();console.log(s2);//Demo {msg: "实例化了"}

2.7惰性单例

2.7.1常规用法

定义:

延迟单例类的实例化时间

作用:

如果单例类实例化开销很大,页面加载时候,有很多业务逻辑需要执行,并不需要这个单例类,此时我们可以推迟这个单例类的实例化时间

实现:

1通过闭包将惰性类封闭起来,避免外界的访问

2在闭包的返回函数中,我们判断闭包类有没有实现实例化,没有实现再去实例化,实现了直接返回实例化结果

3当调用这个闭包时候,才尝试去实例化,这样就拖延了实例化的时间点

         <script>

        //定义:类,只能被实例化一次

        //有时候,不允许外界访问

        //放在闭包中//此种方法只实例化一次//但是不调用也会实例化一次

   var Single = (function () {

     function Demo() {//定义类

                console.log('实例化了');  this.msg = 'hello';  }

            //此处省略一千亿万行数据

            //方法

Demo.prototype.getMsg = function () { console.log(this.msg); }

            //此处省略一千亿万行方法

            var instance;   //定义单例

            return function () { //暴露一个接口

           if (instance) { return instance //判断单例是是否被实例

} else {return instance = new Demo(); }}})()//重新实例化并返回

        //获取数据

        var s1 = Single();

        console.log(111, s1);//111 Demo {msg: "hello"}

        var s2 = Single();

        console.log(222, s2);//222 Demo {msg: "hello"}

2.7.2静态变量

定义:

     一旦被定义,只能被读取,无法被修改,其原理:

              js中的任何变量,任何方法,只要能够访问,都可以被修改。

                     如果这些变量不能被访问,那么我们是无法修改他们的

      定义在一个闭包(私有作用域)中的变量我们是访问不到,但是闭包方法是可以访问闭包作用域中的数据的,在这个方法中,我们只提供对数据的取值()方法,不提供赋值()方法,那么就可以实现静态变量

注意:

我们实现的静态变量有个局限性:

只能定义值类型的数据(工作中定义的静态变量也仅仅是值类型的)

 // 静态变量

        var Conf = (function () {//1.闭包作用域中存储数据,防止外部访问数据

            var _data = {

                num: 100, color: 'red',

                //8.只能存储值类型,不能存储引用类型,该类型数据会被访问与修改

                arr: [1, 2, 3],

                obj: { msg: 'hello'}};

            //2.返回一个方法

            //4.闭包函数可以访问内部的数据

            return function (key) {

               return _data[key]; }})(); //6.取值器方法获取数据

        //3.外部无法访问私有作用域的 _data

        // console.log(111, _data);//not defined

        //5.通过闭包访问

        // Conf();//{num: 100, color: "red"}

        //7.访问num,color,msg

        console.log(Conf('num'));//100

        console.log(Conf('color'));//red

        console.log(Conf('msg'));//undefined

        //10.发现可以读取引用类型的数据,查看是否可以修改数据

        Conf('arr').push(3, 54, 3)//[1, 2, 3, 3, 54, 3],可以修改数据

        Conf('obj').size = 200;//{msg: "hello", size: 200},可以修改数据

        Conf('num') === 66//100,值类型的数据不会被影响,仍然只可读取

        //9.读取引用类型的数据

        console.log(Conf('arr'));//[1, 2, 3]

        console.log(Conf('obj'));//{msg: "hello"}

2.8适配器模式(结构型的设计模式)

2.8.1适配器模式内涵

将一个类(对象)的接口(属性或者方法)适配到另一个类(对象)的接口(属性或者方法) 来解决类(对象)之间不兼容的问题,实现类(对象)之间的解耦

特点

1.结构型的设计模式

2.是用来解决接口之间不兼容的问题的

3.是对数据进行的一个拆分再封装的一个过程

4.适配器模式往往需要一些额外的开销,但是这些开销要远比修改原有业务逻辑的成本低

 //1.定义函数

        function demo(arr) {

            //第一个成员表示标题

            var h1 = document.createElement('h1');

            h1.innerHTML = arr[0];

            //第二个成员表示内容

            var p = document.createElement('p');

            p.innerHTML = arr[1];

            //第三个成员表示时间

            var span = document.createElement('span');

            span.innerHTML = arr[2];

            //上树

            app.appendChild(h1);

            app.appendChild(p);

            app.appendChild(span);

        }

  //6.由于数据变了重写方法,代价太大,所以需要适配器

        //定义适配器,将对象适配成数组,然后统一使用数组的格式

        function dataAdptor(obj) {

            return [obj.title, obj.content, obj.time]

        }

        //7.使用适配器适配的数据

        demo(dataAdptor(obj));//正常执行

2.8.2适配器应用

1.请求接口数据的适配

2.参数的适配

3.代码库的适配

创建一个“我是好人”代码库,实现了交互

领导要求换jquery,

将jquery的一些接口方法适配到原有的库的接口方法

<button>切换</button>

    <h1>内容</h1>

    <!-- 1.引用 -->

    <!-- <script src="./自用库01面向对象优化.js"></script> -->

    <!-- 3.引用jQ库,不想重新写一遍方法实现的交互,就需要适配她-->

    <script src="./jquery1.12.js"></script>

//4.如果开发时间短,没有充足的时间去重写页面,就需要定义适配器,兼容这两个库

<script> var QAQ = {

            //5.适配绑定事件方法

bindEvent: function (dom, type, fn) { $(dom).on('click', fn) },

            //6.适配修改样式方法

            css: function (dom, key, value) { $(dom).css(key, value) },

            getStyle: function (dom, key) { //7.适配获取样式方法

return $(dom).css(key)}}; </script>//注意:前面不能加return会打断函数

        // 2.老方法实现交互

<script> var h1 = document.getElementsByTagName('h1')[0];

        var btn = document.getElementsByTagName('button')[0];

        //设置交互:点击button切换h1的显隐

        QAQ.bindEvent(btn, 'click', function () {

        //切换显隐

         QAQ.css(h1, 'display', QAQ.getStyle(h1, 'display') === 'block' ? 'none' : 'block')

        }) </script>

 <!-- 注意:如果时间足够,应该重写页面,不然两个库的相同方法在一起显得很呆 -->

3. 观察者模式

3.1观察者模式内涵

1.定义:

观察者模式,又叫发布订阅者模式,又叫消息系统,又叫消息机制,又叫自定义事件

2.用途:

解决主体与观察者之间的耦合问题

3.模式:

观察者模式是一个行为型设计模式

4.特点:

1解决的是耦合问题(类与类之间,对象之间,类与对象之间,模块之间)

2对于任何一个观察者来说,其他观察者的改变不会影响自身

3对于任何一个对象来说,既可以是观察者,也可以是被观察者

如: jQuery中的观察者模式。

$.CallBacks()方法执行的结果得到一个观察者对象

观察者对象有一个方法叫add,用来订阅消息的。

观察对象有一个方法叫fire,用来发布消息。

    //1.创建一个消息相同

        var cb = $.Callbacks();

        //2.订阅消息

        cb.add('qaaq', function () {

            //👇4.Arguments(4) ["asd", 11, 22, true, callee: ƒ, Symbol(Symbol.iterator): ƒ]

            console.log(arguments);

        })

        //3.fa发布消息

        cb.fire('asd', 11, 22, true)

        //5.发布几次就执行几次

        cb.fire('asd', 11, 22, true)

        cb.fire('asd', 11, 22, true)

3.2实现观察者模式

观者者对象必须具备两个方法

on        用来注册消息

第一个参数表示消息的名称。第二个参数表示回调函数

trigger   用来触发消息

第一个参数表示消息的名称。从第二个参数开始表示传递的数据

off           用来移除消息的方法,参数同register

once   单次订阅方法,参数同register

通过闭包将接口返回,那么ontrigger对用户来说就是可访问的,闭包里面存储消息队列,对用户来说就是不可见的,因此是安全。

 <h2>消息:<span>0</span></h2>

    <ul><li>评论的消息</li></ul>

    <textarea></textarea>

    <button>提交内容</button>

    <!--  这三个模块理论上由三个人开发 -->

    <script src="./自用库.js"></script> <script>

(function () {//4.1:消息模块 A

var dom = document.getElementsByTagName('h2')[0].getElementsByTagName('span')[0];

var num = 0;//4.3

_.Observer.on('addMessage', function (msg) {//4.2订阅消息

num++;dom.innerHTML = num; }) //4.4添加信息,改变消息数量

_.Observer.on('deletMessage', function () {//3.9监听 模块B 信息的消息

num--; dom.innerHTML = num; })})()</script> <script>

(function () {//3.1:内容模块 B

var dom = document.getElementsByTagName('ul')[0];

//订阅消息               

_.Observer.on('addMessage', function (msg) { 

var li = document.createElement('li'); //3.2创建li

var text = document.createTextNode(msg);

var close = document.createElement('span');  //3.3创建删除按钮

close.innerHTML = '&times;';  //3.4设置内容

//3.5设置内容

li.appendChild(text);

li.appendChild(close);

dom.appendChild(li);  //3.6上树

close.onclick = function () { //3.7删除消息

dom.removeChild(li) //删除li

//3.8发布消息,通知模块A 的num -1

_.Observer.trigger('deletMessage') }})})()</script><script>

(function () {//2.1:评论模块 C

//2.2获取元素

var btn = document.getElementsByTagName('button')[0];

var textarea = document.getElementsByTagName('textarea')[0];

btn.onclick = function () {//2.3当用户点击按钮,要提交评论

var text = textarea.value;  //2.4获取内容

//2.5输入内容可以提交

if (text) {//2.6将内容提交到B模块(发布消息)

.Observer.trigger('addMessage', text)

textarea.value = ''; }} })() </script>  //2.7清空内容

4.组合模式(部分——整体模式)(结构型设计模式)

4.1组合模式理论

含义:

又叫部分——整体模式,将对象组装成一个树形结构来表达这个整体,不论是部分还是整体,在表现上具有一致性。 是结构型设计模式

特点:

1.是一个拆分合并过程。为我们提供清晰的组成结构

2.通过对基对象的属性方法的继承,使成员对象间的基本表现,行为统一

3.成员对象的结构简单而又单一,这给我们带来了更多的组合方式。

组合模式实现步骤:

整体拆分 -> 得到不同层级的个体

个体组装 -> 组合得到不同的整体

4.所有个体都会继承同一个基类

5.通常基类是只能被继承不会去实例化的的(包含的是所有个体共有的属性方法)

4.2组合模式应用一一新闻模块

需求

1.整个新闻模块是一个根节点

2.每一行是一个枝干节点

3.每一行有多条新闻,每一条新闻就是一个叶子节点(因此不能包含子节点)

总结

1.是一个结构型设计模式

2.本质就是一个拆分合并的过程

3.整体与个体之间具有行为的一致性

4.对个体的不同的组装,可以是整体差异化

5.组合模式使整体结构很清晰

<script src="./自用库01面向对象优化.js"></script>

    <script>

function Base() { //基类:让所有类继承

this.element = null;  //当前元素

this.children = []; }//存储子类

Base.prototype.init = function () {//定义这些行为方法

throw new Error('基类不能初始化')  } //基类不能初始化

Base.prototype.getelement = function () { // 获取当前实例对应的元素

return this.element;  }

Base.prototype.add = function (child) { //添加子元素

this.children.push(child); //存储子对象

this.element.appendChild(child.getelement());//处理dom

return this; }  //链式调用

//容器类

function Container(id, parent) {

Base.call(this); //构造函数式继承

this.id = id; //存储数据

this.parent = parent;

this.init();  }//初始化

//可以使用类式继承,也可以使用寄生式继承

// Container.prototype = new Base//类式继承

_.inherit(Container, Base);//寄生式继承

Container.prototype.init = function () {//重写方法//初始化方法

this.element = document.createElement('ul'); //创建元素

this.element.id = this.id; //添加id

this.element.className = 'container'; }//添加类

Container.prototype.show = function () {//显示容器

this.parent.appendChild(this.element);}//让容器元素上树

//每一行的类

function Item(className) {

Base.call(this) //构造函数式继承

this.className = className || 'item'//存储数据

this.init(); }//初始化

_.inherit(Item, Base);  //继承

Item.prototype.init = function () {//重写方法

this.element = document.createElement('li'); //创建元素

this.element.className = this.className; } //设置属性

//没有分类的新闻

function TitleNews(text, href) {

Base.call(this) //构造函数式继承

this.text = text; //存储数据

this.href = href;

this.init(); }//初始化

_.inherit(TitleNews, Base) //继承

TitleNews.prototype.init = function () {//重写方法//初始化方法

this.element = document.createElement('a'); //定义元素

this.element.href = this.href;  //设置属性

this.element.innerHTML = this.text; }  //设置内容

//分类新闻

function TypeNews(text, href, type) {

Base.call(this) //构造函数式继承

this.text = text; //存储数据

this.href = href;

this.type = type;

this.init();}//初始化

_.inherit(TypeNews, Base)    //继承

TypeNews.prototype.init = function () {//重写方法//初始化方法

this.element = document.createElement('a'); //创建元素

var span = document.createElement('span');

var text = document.createTextNode(this.text);

span.innerHTML = this.type + '|'; //设置内容

this.element.href = this.href;  //设置属性

this.element.appendChild(span)  //组装

this.element.appendChild(text)  }

//使用类

new Container('sport', document.body)  

    .add(new Item()

    .add(new TitleNews('中华人民共和国', 'www.baidu.com'))

    .add(new TitleNews('今天成立了', 'www.baidu.com')))

    .add( new Item()

    .add(new TitleNews('今天要当一个好人', 'www.baidu.com'))).add(new Item()

    .add(new TypeNews('明天当个坏人', '#demo', '明天')))

    .add(new Item()

    .add(new TypeNews('后天当个大好人', '#demo', '后天'))) .show(); //上树

new Container('car', document.body)   //构造一个新模块

    .add(new Item().add(new TitleNews('昨天不是个好人啊', '#demo')))

.add(new Item().add(new TypeNews('前天是个大好人啊', '#demo', '前天'))).show();

5.策略模式

定义:

将一组算法封装起来,使其彼此之间可以相互替换,封装的算法具有独立性,不会随着客户端的变化而变化。

类型:

行为型设计模式

特点:

1.创建的一系列算法,每组算法的业务逻辑可能是相同的,但是处理的过程以及结果可能不同。

2.算法是独立的,可以相互替换,解决了算法与使用者之间的耦合问题

3.算法的独立性,方便使其进行单元测试

应用:

       1.jQuery的动画算法,就是策略模式

       2.处理商品促销价格

       3.表单校验等

6.命令模式

6.1内涵

定义:

将请求与实现解耦并封装成独立的对象,从而使不同的请求对客户端实现的参数化。

类型:

行为型设计模式

特点

1.将执行的命令封装,解决命令的发起者与命令的执行者之间的耦合

2.使用者不必了解每条命令的接口是如何实现的,命令是如何执行的。

3.所有命令都被存储在命令对象上。

4.命令的使用具有一致性,多数命令在一定程度上简化了操作方法的实现,

5.对命令的封装,使得每次执行时都要调用一次命令对象,增加了系统的复杂度

6.2应用(canvas)

绘制圆的过程中遇到哪些问题?

1源生的API有时候不好用

绘制一个圆写了5行代码

绘制一个圆,只需要圆心坐标,半径,颜色就够了,其他的都多余

2如果将ctx变量放在全局环境中,有风险,可能会被人更改

3如果将ctx变量保存在闭包中,外界就无法访问了

 

strokeCircle: function (x, y, r, color) {//描边圆

ctx.beginPath();ctx.arc(x, y, r, 0, Math.PI * 2);

ctx.closePath(); ctx.strokeStyle = color; ctx.stroke();},

return { //执行方法 //暴露接口

exec: function (type) {//type:上面封装的名称,

//获取第二个参数开始,传递的参数

var args = Array.prototype.slice.call(arguments, 1);//第一个参数为type,不能再取了

_C[type] && _C[type].apply(null, args); //执行指令

return this; }}})()//链式调用

Command.exec('fillCircle', 100, 100, 50, 'green')   //绘制圆

7.迭代器模式

定义:

在不暴露对象内部结构的同时,可以顺序的访问聚合对象内部的元素。

模式:

行为型设计模式

特点;

1.可以顺序的访问聚合对象中的每个元素

2.简化循环语句,结构清晰

3.迭代器并没有移出循环语句,而是移动到迭代器中。

4.迭代器在处理一个对象时,我们只需要提供处理方法,并不关心对象内部结构。

5.解决了使用者与对象内部结构解析之间的耦合

6.为我们提供统一的操作对象接口

function Iterator(selector) { //实现一个类式jQ中的迭代器

this.selector = selector; //存储选择器

this.elements = document.querySelectorAll(selector); //获取元素

this.index = 0; }//维护一个索引值

Iterator.prototype = {

//放在对象中定义,对象的constructor属性,不再指向Iterator

//constructor属性始终指向创建自身的构造函数

constructor: Iterator, //重写构造函数

getCurrent: function (){return this.elements[this.index]},//获取元素

first: function () { //获取第一个

this.index = 0;//更改index指向

return this.getCurrent() },//返回元素

prev: function () { //获取前一个

this.index--;

if (this.index < 0) {this.index = 0; throw new Error('已经最前面了') }

return this.getCurrent(); },

next: function () {//获取后一个

this.index++;

if (this.index >= this.elements.length) { //判断边界

this.index = this.elements.length - 1;

throw new Error('已经最后面了') }

return this.getCurrent();},

//获取第几个

eq: function (index) {this.index = index; return this.getCurrent();}}

var lis = new Iterator('li'); //获取li

console.log(lis.first());console.log(lis.eq(2));

8.遍历器

含义:

遍历聚合数据。把for循环移动到方法内部。

<ul>

<li>1</li><li>2</li><li>3</li><li>4</li>

</ul>

    <!-- <script src="./jquery1.12.js"></script> -->

    <script>//遍历器:遍历聚合数据

var arr = [1, 2, 3, 4, 5];

var obj = {color: 'red', msg: 'hello', num: 12 };

//一:实现遍历器(模仿ES5)(for each)

        Array.prototype.each = function (callback) {

            //将 循环 移到方法内部

            for (var i = 0; i < this.length; i++) {

                callback(this[i], i, this)

 } }

        arr.each(function (item, index, arr) {//打印

            console.log(item, index, arr); })

function each(data, callback) {//二:实现jQ中的遍历方法(each)

/* 判断数组的四种方式(typeof只能正确判断值类型)

*      1.instanceof

*      2.constructor

*      3.Object.prototye.tostring.call(dom)*/

            if (data instanceof Array) {//遍历数组用for循环

                for (var i = 0; i < data.length; i++) {

                    callback(i, data[i]) }//执行函数

            } else {

                for (var key in data) { //遍历对象用for in 循环

                    callback(key, data[key]) }}}//执行函数

//二:遍历数据

each(arr, function (index, item) { console.log(index, item); })

//三:遍历对象

each(obj, function (key, value) {console.log(key, value);})

9.委托模式

9.1数据分发

含义:

1.多个对象接收并处理同一请求,他们将请求委托给另一个对象统一处理请求

2.解决请求与委托者之间的耦合

3.被委托者接收到请求分发给委托者去处理

应用:

请求委托:

浏览器同时并发处理请求的个数是有限的,因此对于一个页面来说,如果请求过多的话,会导致后面的请求被延迟(堵塞),我们为了加快这些请求发出,我们将这些请求合并成一个,这样当请求结束后,我们再分发这些请求的数据。

9.2事件委托

9.2.1事件委托

背景:

对于每一个元素我们都要绑定一个事件,这样对于资源开销很大(如果有1w个元素,要绑定1w个事件)

对于每一个类元素都要for循环遍历一次,开发成本很高。所以我们可以通过事件委托来解决上面的问题

含义:

所有元素的事件绑定委托给同一个父元素,根据事件冒泡捕获机制,可以在父元素绑定事件中,获取触发事件的这个元素,根据这个元素具有的某类特征(例如元素名称,元素id,元素类,元素属性等等)做不同的处理,实现事件从父元素到被委托的元素传递。

特点:

1.减少事件数量

2.预言未来元素:新增的元素也可以绑定事件

3.避免内存外泄:通常创建一个对象需要占用一些内存,这类占用是有意义的;有时候一些垃圾(已经被删除数据)还占用着内存,这部分内存对我们来说是没用的,就是外泄内存

        //在低版本的ie浏览器下会参数内存外泄问题

        //高版本浏览器垃圾回收机制是计数的

        //低版本的垃圾回收机制,是引用(指向)的

        //多个元素相互引用(指向)时,不能被垃圾回收机制回收

        //点击按钮,box显示新内容

        var btn = document.getElementById('btn');

        var box = document.getElementById('box');

        btn.onclick = function () { //绑定事件

            //box外泄,box设置了新内容

//按钮元素被删除,但由于按钮仍被绑定了点击事件,因此无法被销毁,泄露在内存中

           box.innerHTML = '新内容'} //更新box内容

        //使用事件委托,避免外泄

        var box = document.getElementById('box');

        //给盒子绑定事件

        box.onclick = function (e) {

if (e.target.tagName.toUpperCase === 'BUTTON') {//判断点击的位置

                box.innerHTML = '新内容'; } }

        //不会内存外泄,因为按钮元素没有绑定点击事件,绑定在盒子上了

9.2.2jQuery中的事件委托(delegate)

delegate:

第一个参数是被委托的元素 (子元素)

第二个参数是事件类型 (这里还可以传递自定数据)

第三个参数是事件回调函数

本质上是调用的on方法,只不过他的委托语义化更强

        // $('#box').delegate('#btn', 'click', function () {

        //     $('#box').html('新内容') })

        //还可以使用 on方法,但是语义化不强

        $('#box').on('click', '#btn', function () {

                $('#box').html('新内容') })

10.节流器

10.1定义与特点

定义:

重复的业务逻辑进行节流控制,执行最后一次操作并取消其他操作, 以提高性能。

特点

1.延迟:通过计时器延迟程序的执行

2.异步:通过计时器,使程序异步执行,避免开销大的程序造成的堵塞

条件

3.程序可控:即是否可以继续执行

4.异步执行:即程序是否可以异步执行

10.2节流与防抖

节流器:

触发操作,让程序延迟执行

清除操作,清除还没有执行的操作

两种节流模式

按操作节流:

在规定时间内,重复执行的时候,取消前一次执行,执行当前的操作

这种节流操作有的人也叫防抖。例如:对返回顶部的优化。

按时间节流:在规定时间内,只允许执行一次。

例如:对icon浮层的优化。

        var btn = document.getElementById('back');

        //显隐返回顶部按钮

        function goTopBtn() { console.log(11111,this,arguments);

            //判断距离顶部300px,显示顶部按钮,否则隐藏

            if (window.scrollY > 300) {

                btn.style.display = 'block'

            } else { btn.style.display = 'none'}}

        window.onscroll = function (e) {

//监听页面滚动

             // goTopBtn();//返回顶部方法

throttle(goTopBtn, {

                time: 1000,

                context: { color: 'red' },

                args: [1, 2, 3]

            })

 /**

         * 优化节流器(防抖器)

         * @fn      执行的函数

         * @data    传递的数据,为true时 清空定时器,(结束)

         **/

        function throttle(fn, data) {

            clearTimeout(fn.__timebar)  //清除

            data = data || {};//定义data默认值//如果data不是true再启动

var params = {  time: data.time || 200, //定义时间默认值

                context: data.context || null,

                args: data.args};

        if (data !== true) {fn.__timebar = setTimeout(function () {

//执行方法

fn.apply(params.context, params.args) }, params.time) }}//启动

10.3实现jQuery

jQuery本身是一个工厂 方法

jQuery中,内置了一个寄生类,寄生在原型.上。

jQuery中,始终返回this即可实现链式调用

jQuery中,通过extend可以为jQuery添加静态方法以及原型方法

 

    <script>

        // //实现jQuery

        // //jQuery本身是一个工厂方法

        // function jQuery(selector) {

        //     //返回实例

        //     return new init(selector);

        // }

        // //工厂的原型

        // jQuery.prototype = {

        //     //更正构造函数

        //     constructor: jQuery,

        //     //把真正的类放在工厂方法的原型对象上

        //     //每次访问init都要写:jQuery.prototype.init太麻烦了

        //     //所以,取别名jQuery.fn代表原型对象

        //     init: init

        // }

        // //真正的jQuery类

        // function init() {

        // }

        //jQuery本身是一个工厂方法

        function jQuery(selector) {

            //返回实例

            // return new init(selector);

            //重新访问init

            return new jQuery.fn.init(selector) }

        //工厂的原型

        //这样访问jQuery.fn相对于访问jQuery.prototype.init

        jQuery.fn = jQuery.prototype = {

            //更正构造函数

            constructor: jQuery,

            //把真正的类放在工厂方法的原型对象上

            //每次访问init都要写:jQuery.prototype.init太麻烦了

            //所以,取别名jQuery.fn代表原型对象

            // init: init,

            version: '1.0',

            demo() { console.log('demoo'); },

            //拓展一些方法,更像数组

            push: Array.prototype.push,

            splice: Array.prototype.splice, }

        //真正的jQuery类

        jQuery.fn.init = function (selector) {

            //根据选择器,将元素获取

            var dom = document.querySelectorAll(selector);

            this.selector = selector;  //存储选择器

            this.length = 0; //定义长度

            //将获取的元素,存储在自身

            for (var i = 0; i < dom.length; i++) {

                //每存储一个成员,更改长度一次

                this[i] = dom[i];

                this.length++;}}

        // 让init类的实例,具有jQuery原型上的方法

        jQuery.fn.init.prototype = jQuery.prototype;

        jQuery.fn.init.prototype = jQuery.fn;

        //jQuery中又定义了拓展方法

        jQuery.extend = jQuery.fn.extend = function (obj) {

            //解析对象

            for (var key in obj) {

                this[key] = obj[key] } }

        // //拓展的静态属性

        // jQuery.extend({

        //     color: 'red', num: 989 })

        //拓展原型属性

        jQuery.fn.extend({

            css: function (key, value) {

                if (typeof key === 'string') { //如果key是字符串,直接赋值

                    //给每一个元素设置样式

                    for (var i = 0; i < this.length; i++) {

                        this[i].style[key] = value; }

                } else { //遍历对象设置样式

                    for (var k in key) {

                        this.css(k, key[k]) } }

                return this; }, //链式调用

            attr: function (key, value) { //设置属性方法

                //给每一个成员设置属性

                for (var i = 0; i < this.length; i++) {

                    this[i][key] = value; } } })

        //对jQuery起别名

        var $ = jQuery;

        $('.app').css('color', 'red').css({

            fontSize: '50px',

            backgroundColor: 'pink'

        }).attr('title', 'hellooo')

        console.log($('.app'));

    </script>

  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值