# 设计模式学习 **Repository Path**: tjdxwwj/design-pattern-learning ## Basic Information - **Project Name**: 设计模式学习 - **Description**: 记录一下复习设计模式过程,争取用js实现 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-06-09 - **Last Updated**: 2021-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式学习 #### 介绍 记录一下复习设计模式过程 ### 设计模式原则 - S – Single Responsibility Principle 单一职责原则 - 一个程序只做好一件事 - 如果功能过于复杂就拆分开,每个部分保持独立 - O – OpenClosed Principle 开放/封闭原则 - 对扩展开放,对修改封闭 - 增加需求时,扩展新代码,而非修改已有代码 - L – Liskov Substitution Principle 里氏替换原则 - 子类能覆盖父类 - 父类能出现的地方子类就能出现 - I – Interface Segregation Principle 接口隔离原则 - 保持接口的单一独立 - 类似单一职责原则,这里更关注接口 - D – Dependency Inversion Principle 依赖倒转原则 - 面向接口编程,依赖于抽象而不依赖于具体 - 使用方只关注接口而不关注具体类的实现 ## 《JavaScript设计模式与开发实践》学习 编程语言按照数据类型大体可以分为两类,一类是静态类型语言,另一类是动态类型语言。 静态类型语言在编译时便已确定变量的类型,而动态类型语言的变量类型要到程序运行的时 候,待变量被赋予某个值之后,才会具有某种类型。 静态类型语言的优点首先是在编译时就能发现类型不匹配的错误,编辑器可以帮助我们提前 避免程序在运行期间有可能发生的一些错误。其次,如果在程序中明确地规定了数据类型,编译器还可以针对这些信息对程序进行一些优化工作,提高程序执行速度。 动态类型语言的优点是编写的代码数量更少,看起来也更加简洁,程序员可以把精力更多地 放在业务逻辑上面。虽然不区分类型在某些情况下会让程序变得难以理解,但整体而言,代码量 越少,越专注于逻辑表达,对阅读程序是越有帮助的。 ### 多态 **多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句** ```js var renderMap = function(type){ if(type === "google"){ googleMap.show() }else if(type === "baidu"){ baiduMap.show() } } //修改 var renderMap = function(map){ if(map.show instanceof Function ){ map.show() } } ``` ### 闭包 延续局部变量的寿命 img 对象经常用于进行数据上报,如下所示: ```js var report = function(src){ var img = new Image(); img.src = src; } report( 'http://xxx.com/getUserInfo' ) ``` 但是通过查询后台的记录我们得知,因为一些低版本浏览器的实现存在 bug,在这些浏览器 下使用 report 函数进行数据上报会丢失 30%左右的数据,也就是说,report 函数并不是每一次 都成功发起了 HTTP 请求。丢失数据的原因是 img 是 report 函数中的局部变量,当 report 函数的 调用结束后,img 局部变量随即被销毁,而此时或许还没来得及发出 HTTP 请求,所以此次请求 就会丢失掉。 ```js var report = (function(){ var imgs = []; return function (Src){ var img = new Image(); imgs.push(img); img.src = src; } })() report( 'http://xxx.com/getUserInfo' ) ``` ### 高阶函数 ```js var appendDiv = function(){ for(var i = 0;i<100;i++){ var div = document.createElement('div'); div.innerHTML = i; document.body.appendChild(div); div.style.dislay = 'none' } } appendDiv() ``` 把 div.style.display = 'none'的逻辑硬编码在 appendDiv 里显然是不合理的,appendDiv 未免 有点个性化,成为了一个难以复用的函数,并不是每个人创建了节点之后就希望它们立刻被隐藏 于是我们把 div.style.display = 'none'这行代码抽出来,用回调函数的形式传入 appendDiv 方法 ```js var appendDiv = function(callback){ for(var i = 0;i<100;i++){ var div = document.createElement('div'); div.innerHTML = i; document.body.appendChild(div); if (typeof callback = 'function'){ callback(div) } } } appendDiv(function(node){ node.style.display = 'none' }) ``` ### 判断数据类型 ```js var Type = {}; for(var i = 0,type;type=['String','Array','Number'][i++];){ (function (type){ Type['is' + type] =function(obj){ return Object.prototype.toString.call(obj)==='[object '+type + ']' } })(type) } Type.isArray([]) ``` ### 高阶函数实现AOP AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些 跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后, 再通过“动态织入”的方式掺入业务逻辑模块中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。 通常,在 JavaScript 中实现 AOP,都是指把一个函数“动态织入”到另外一个函数之中,具 体的实现技术有很多,本节我们通过扩展 Function.prototype 来做到这一点。代码如下: ```js Function.prototype.before = function(beforefn){ var _self = this; return function(){ beforefn.apply(this,arguments); return _self.apply(this,arguments) } } Function.prototype.after = function(afterfn){ var _self = this; return function(){ var ret = _self.apply(this,arguments); afterfn.apply(this,arguments); return ret; } } var func = function(){ console.log(2) } func = func.before(function(){ console.log(1) }).after(function(){ console.log(3) }) func(); ``` ### uncurrying ```js Function.prototype.uncurrying = function(){ var self = this; return function(){ var obj = Array.prototype.shift.call(arguments);// 截取第一个元素 return self.apply(obj,arguments) } } ``` ```js var push = Array.prototype.push.uncurrying(); (function(){ push(arguments,4) console.log(arguments) // 输出:[1, 2, 3, 4] })(1,2,3) ``` ```js for ( var i = 0, fn, ary = [ 'push', 'shift', 'forEach' ]; fn = ary[ i++ ]; ){ Array[ fn ] = Array.prototype[ fn ].uncurrying(); }; var obj = { "length": 3, "0": 1, "1": 2, "2": 3 }; Array.push( obj, 4 ); // 向对象中添加一个元素 console.log( obj.length ); // 输出:4 var first = Array.shift( obj ); // 截取第一个元素 console.log( first ); // 输出:1 console.log( obj ); // 输出:{0: 2, 1: 3, 2: 4, length: 3} Array.forEach( obj, function( i, n ){ console.log( n ); // 分别输出:0, 1, 2 }); ``` ### 函数节流 ```js var throttle = function(fn,interval){ var _self = fn, timer, firstTime = true; return function(){ var args = arguments, _me = this; if(firstTime){ _self.apply(_me,args); return firstTime = false; } if(timer){ return false; } timer = setTimeout(function(){ clearTimeout(timer); timer = null; _self.apply(_me,args) },interval||500) }; } ``` ### 分时函数 一个例子是创建 WebQQ 的 QQ 好友列表。列表中通常会有成百上千个好友,如果一个好友 用一个节点来表示,当我们在页面中渲染这个列表的时候,可能要一次性往页面中创建成百上千 个节点。 在短时间内往页面中大量添加 DOM 节点显然也会让浏览器吃不消,我们看到的结果往往就 是浏览器的卡顿甚至假死。 ```js var timeChunk = function(ary,fn,count){ //第 1 个参数是创建节点时需要用到的数据,第 2 个参数是封装了创建节点逻辑的函数,第 3 个参数表示每一批创建的节点数量。 var obj,t; var len = ary.length; var start = function(){ for(var i= 0 ; i