[关闭]
@lizlalala 2017-01-29T22:37:50.000000Z 字数 7148 阅读 1153

javascript设计模式(一)

设计模式 javascript 读书笔记


概述

动态语言类型与鸭子类型

javascript是动态类型语言,只有在运行时才会知道变量类型,缺点是运行时可能出现与类型有关的错误,
好处是,针对接口编程,不关心它isA,只关心它hasA,比如,有length,有slice等等的就可以当数组用。(相当于,可能一只会鸭子叫的鸡也会被认为是鸭子...)

多态

多态的思想其实是把“做什么”跟“谁去做”分离开来,需要找出各个对象中共有的“做什么”的部分。多态性的根本作用在于把过程化的条件分支语句转化为对象的多态性
javascript由于不用类型检查,与生俱来有多态性。很多设计模式就有多态性的内涵在里面,而javascript的高阶函数也可以用来实现这些设计模式。

  1. var makeSound = function(animal){
  2. animal.sound();
  3. }
  4. //这样就可以调用
  5. // makeSound(new Duck())
  6. var Duck = function(){}
  7. Duck.prototype.sound = function(){
  8. console.log("嘎嘎嘎");
  9. }

封装

包括封装数据、封装方法、封装类型、封装变化。其中
(1)封装数据(访问权限)
1. let限定作用域
2. 闭包
3. symbol 伪私有属性

(2)封装变化
《设计模式》一书中共归纳总结了23种设计模式。从意图上区分,这23种设计模式分别被划分为创建型模式、结构型模式和行为型模式。
拿创建型模式来说,要创建一个对象,是一种抽象行为,而具体创建什么对象则是可以变化的,创建型模式的目的就是封装创建对象的变化。而结构型模式封装的是对象之间的组合关系。行为型模式封装的是对象的行为变化。

原型系统

javascript遵守原型编程的基本原则:

this

this的若干种场景,在楼主的某篇blog里面已经说过了。
比较好的代码片段

  1. document.getElementById = (
  2. return function(func){
  3. return func.apply(document,arguments)
  4. }
  5. )(document.getElementById);
  6. //实现bind
  7. Function.prototype.bind = function(context){
  8. var self = this; //保存当前函数
  9. return function(){
  10. self.apply(context,arguments);
  11. }
  12. }

闭包和高阶函数

用处:
1. 封装变量
2. 延续局部变量的寿命

  1. var mult = (function(){
  2. var privateParam = {};
  3. return function(){
  4. privateParam[key] = someValue;
  5. return privateParam
  6. }
  7. })
  8. mult()

命令模式是把请求封装成对象,从而分离请求的发起者和请求的接受者(执行者)之间的耦合关系·用闭包可以事先往命令对象中植入命令的接收者。
虽然闭包使得局部变量可以长期被引用,一直生存下去,但一般来说它不会导致内存泄露,要回收的话只需要设置null即可。与内存有关的地方是比较容易形成循环引用,如果闭包的作用域链中保存着一些DOM节点,那就可能造成内存泄露,但这是由于IE浏览器中BOM和DOM是使用C++用COM对象的凡是实现的,它的垃圾回收机制是引用计数,循环引用会导致无法被回收,所以内存泄漏。
经典闭包

  1. for(var i=0;i<nodes.length;i++){
  2. (function(i){
  3. node[i].onclick = function(){
  4. console.log(i);
  5. }
  6. })(i)
  7. }

高阶函数

高阶函数是至少满足以下条件的函数:
参数为函数
返回值为函数

还有一段很不错的代码片段:

  1. function isType(type){
  2. return function(obj){
  3. return Object.prototype.toString.call(obj)===`[object ${type}]`;
  4. }
  5. }
  6. var isArray = isType('Array')
  7. isArray([1,2,4])

高阶函数的其他场景还有:

  1. function createXHR(){
  2. iftypeof XMLHttpRequest != 'undefined'){
  3. createXHR = function(){
  4. new XMLHttpRequest();
  5. }
  6. }else if(typeof ActiveObject != 'undefined'){
  7. createXHR = function(){
  8. ...
  9. }
  10. }else ...
  11. return createXHR();
  12. }

设计模式

1. 单例模式

js中单例模式的核心是:确保只有一个实例,供全局访问。

  1. //单例
  2. var Singleton = (function(){
  3. var param;
  4. return function(initialValue){
  5. return param || (param = initialValue);
  6. }
  7. })();

全局变量不是单例,但常被用作单例,但会造成命名空间的污染,有以下几种方式可以降低污染:

1.1 惰性单例

使用时才创建,而不是一运行/加载页面就创建。比如登录窗口,通常的做法是一开始就隐藏,使用时display:block。但很可能一直用不到。改进的方法是onclick的时候创建,但是维持的需要保证是同一个变量,就可以用之前的闭包来进行判断。

  1. //惰性单例
  2. var generateLoginBox = (function(){
  3. var div;
  4. function createDiv(){
  5. var div = document.createElement('div');
  6. div.innerHTML = "登录窗口";
  7. document.body.appendChild(div);
  8. return div;
  9. }
  10. return function(){
  11. return div || (div = createDiv());
  12. }
  13. })();
  14. document.getElementById("loginBtn").addEventListener("click",function(){
  15. var div = generateLoginBox();
  16. div.style.display = 'block';
  17. }

但实际上上述代码中管理单例的跟创建对象的进行了耦合(即这个单例可以用来创建很多类型的单例,不光是div,还可能是其他的image等等)。因此需要进行拆分

  1. function createDiv(){
  2. var div = document.createElement('div');
  3. div.innerHTML = "登录窗口";
  4. document.body.appendChild(div);
  5. return div;
  6. }
  7. //朴素的改进,一步生成结果
  8. var generateSingle = (function(){
  9. var result;
  10. return function(fn){
  11. return result || (result = fn.apply(this,arguments));
  12. }
  13. })();
  14. document.getElementById("loginBtn").addEventListener("click",function(){
  15. var div = generateSingle(createDiv);
  16. div.style.display = 'block';
  17. }

method2:生成函数的函数

  1. //管理单例的函数!
  2. var generateSingle = function(fn){
  3. var result;
  4. return function(){
  5. return result || (result = fn.apply(this,arguments));
  6. }
  7. }
  8. document.getElementById("loginBtn").addEventListener("click",function(){
  9. var createSingleDiv = generateSingle(createDiv);
  10. var div = createSingleDiv();
  11. div.style.display = 'block';
  12. }

类似的场景还包括对dom元素绑定事件,实际上只要在最开始创建的时候绑一次就好。

【summary】
全局+命名空间-》单例-》惰性单例(管理单例的代码与创建对象的会耦合)-》带有代理的惰性单例


策略模式

js中策略模式的核心是:定义一系列可以互相替代的算法,把他们分别封装起来。在js中表现为一个strategy对象

程序需要包括两部分,一个是接受用户请求并进行分配的总调度部分,一个是各策略计算部分。如

  1. //调度部分Context
  2. var calculateBonus = function(salary,level){
  3. return strategies[level](salary);
  4. }
  5. //策略计算部分
  6. var strategies = {
  7. 'A': function(salary){
  8. return salary*3;
  9. },
  10. 'B': function(salary){
  11. return salary*2;
  12. },
  13. 'C': function(salary){
  14. return salary*1;
  15. }
  16. }

还有其他栗子如,缓动动画和表单校验:

  1. var strategies = {
  2. noEmptyName:function(name,errMsg){
  3. if(!name) return errMsg;
  4. },
  5. minLength: function(value,length,errMsg){
  6. if(value.length<length) return errMsg;
  7. },
  8. isMobile: function(phoneNum,reg,errMsg){
  9. if(!reg.test(phoneNum)) return errMsg;
  10. }
  11. }
  12. function Validator(){
  13. this.validateQueue = [];
  14. }
  15. /**
  16. * [validator.add()]
  17. * @param {[type]} item [description]
  18. * @param {[type]} rules [description]
  19. */
  20. Validator.prototype.add = function(item,rules){
  21. var ruleFuncs = rules.map(function(rule){
  22. var strategyArr = rule.strategy.split(':');
  23. var strategy = strategyArr.shift();
  24. strategyArr.unshift(item);
  25. strategyArr.push(rule.errorMsg);
  26. return function(){
  27. return strategies[strategy].apply(null,strategyArr);
  28. };
  29. })
  30. this.validateQueue = this.validateQueue.concat(ruleFuncs);
  31. }
  32. Validator.prototype.start = function(){
  33. return this.validateQueue.every(function(fn){
  34. var errMsg = fn();
  35. if(errMsg) {
  36. console.log(errMsg);
  37. return false;
  38. }
  39. return true;
  40. })
  41. }
  42. var validate = function(){
  43. var validator = new Validator();
  44. var form = {
  45. username: 'luchen'
  46. }
  47. validator.add(form.username,
  48. [{
  49. strategy: 'noEmptyName',
  50. errorMsg: '用户名不能为空'
  51. },
  52. {
  53. strategy: 'minLength:7',
  54. errorMsg: '用户名长度不能小于7位'
  55. }]);
  56. var result = validator.start();
  57. }
  58. validate();

【summary】策略模式考虑用对象简便分配策略,来代替switch及耦合,case还有 计算奖金、缓动动画和表单校验三个例子。


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注