JavaScript 模式(四)

函数模式


1.回调模式

【基本模式】

  var func = function(callback){
  
      //TODO
      
      //检查回调函数是否可用
      if(typeof callback !== "function"){
         callback = false;
      }

      //TODO
      //运行回调函数
      if(callback){
         callback(value);
      }
  }

回调模式下,回调函数作为参数可以是一个已有的函数,也可以是匿名函数

【作用域】

在一般方法下,回调函数可以正常运行,但对于对象方法,很可能出现对象方法内调用自身属性,造成this指向不明,其原因是作用域改变:

  //创建对象和对象方法,对象方法调用其属性值
  var objectName = {
      propName: value,
      methodName: function(){
         var other = this.propName;
      }
  }

  //使用对象方法作为回调函数
  func(objectName.methodName);//函数调用时出现问题,因为this.propName在相应的作用域内不存在

解决方法一:回调函数参数不仅包含对象方法,也包含该对象,通过绑定限定作用域:

    //更改外层函数的使用方式
    var func = function(callback,obj_callback){
  
      //TODO
      
      //检查回调函数是否可用
      if(typeof callback !== "function"){
         callback = false;
      }

      //TODO
      //运行回调函数
      if(callback){
         callback.call(obj_callback,value);
      }
  }

解决方法二:将对象方法的方法名作为参数,按照方法一的模式进行

      var func = function(callback,obj_callback){
  
      //TODO
      
      //检查回调函数赋值参数是否为函数名
      if(typeof callback === "string"){
         callback = obj_callback[callback];//回调函数从对象方法中获得
      }

      //TODO
      //运行回调函数
      if(typeof callback === "function"){
         callback.call(obj_callback,value);
      }

【超时用法】

   setTimeout(callback,timeout);

2.返回函数

将函数作为一个返回值,函数获得返回值后,可以继续调用:

   var outer = function(){
       //在函数声明的作用域中,创建变量,这些变量只能在该域内使用
       var params = value;
      
       //返回一个函数
       return function(){
          //TODO 可以在函数中处理内部参数,对外保持隐蔽
       }
   }

   //调用
   var First = outer(); //此时Fisrt即为一个可调用的函数
   var Second = First(); //继续调用

3.自定义函数(惰性函数定义)

当函数需要初始化工作,且只需要进行一次时,可以使用该模式:

   var selFunc = function(){
      
        //TODO 做一些需要初始化的工作
        
        //注意名称不能改变,因为需要对函数进行重定义
        selFunc = function(){
           //TODO 该函数真正要实现的功能
        };
   }
   
   //使用时
   selFunc(); //第一次进行初始化工作
   selFunc(); //执行真正的功能

【说明】

  • 重定义时,必须要使用相同名称,否则真实功能无法发生
  • 在重定义后,该函数之前添加的属性和方法全部丢失

4.即时函数

函数在定义后立即执行的一种模式,包括以下部分:

  • 需要使用函数表达式,不能使用函数声明
  • 在函数主体后添加“()”,表示立即执行
  • 将整个函数包装在括号中
  (function(){
     //TODO
  })();

【使用】
对于在一些场景中(如页面加载),一些操作只需要进行一次不需要重复使用,没有必要声明函数,如果直接操作,会污染全局域,此时使用即时函数创建沙箱

【参数和返回值】

对于含有参数的函数,直接将参数赋值即可,一般可以将全局域本身赋值给函数,就可以调用其中的属性

  (function(region){
     //TODO 使用this中的属性,如DOM元素
  })(this);

返回值可以直接从即时函数获取,返回值或者返回函数均可:

  var result = (function(){
     //TODO
  })();

【预设值】
可以利用即时函数的特点,直接返回一些预设值,且用户只读不可更改

  var getPreValue = (function(){
     //获取或计算预设值
     var preValue = {
            propName = value
         };
     //通过闭包的方式返回预设值 
     return function(){
        return preValue;
     };

  })();

  //使用
  var preValue = getPreValue();
  preValue.propName //可以使用预设值

【属性不变量】
可以利用即时函数给对象的属性赋值,在对象创建时进行赋值后,该属性保持不变

  //定义字面量对象
  var objectName = {
     //定义属性不变量
     constProp: (function(){
         //利用即时函数,直接返回处理结果
         var privateField = value;
         return privateField; 
    })()
  }; 

  //使用
  objectName.constProp //直接使用属性不变量

【变种:即时对象初始化】

    //创建匿名对象
   ({
      //这里定义的属性为对象内的属性,不会污染全局域
       propName: value,
       methodName: function(){
       },
       //定义初始化任务
       init: function(){
         //TODO 初始化任务
       }
    }).init(); //直接调用初始化方法

【说明】

  • 该模式可以更有效的组织代码结构
  • 如果要获取该对象,可以在init()中返回this
  • 该模式,在js压缩器工具中,一般不会被压缩

【变种:初始化分支(加载时分支)】

初始化时,确定一个条件,该条件在整个程序运行中不会改变,则只需确定一次即可(常见的浏览器嗅探功能,平台确认,函数是否兼容等)

   var mainObj = ({
  
      //需要确认的属性和方法
      propName: null,
      methodName: null,
      
      init: function(){
         //TODO 根据条件给相关属性和方法进行赋值或者定义
         this.propName = value;
         methodName = function(){
         };
      }

   }).init();

   //使用
   mainObj.propName;
   mainObj.methodName();

6.备忘模式(memoization)

给函数添加自定义属性,利用该属性缓存函数的返回结果,在下一次相同计算时,无需再做大量计算,直接返回缓存中的结果

   var funcMem = function(param){
       //一般需要将输入参数进行转换,生成一个键值
       var key = param;
       if(! funcMem.cache[key]){
          var result = {};
          //TODO 计算result值
         funcMem.cache[key] = result;
       }
       return funcMem.cache[key]; //返回结果
   };
   funcMem.cache = {};//为函数创建自定义属性

【说明】

  • 创建键值时,一般使用序列化,将输入参数转换为json字符串key = JSON.stringfy(Array.prototype.slice.call(arguments))

7.配置模式

函数在需要传递大量参数的情况下,不要直接列在函数参数中,而是传入一个配置对象

  //创建配置对象
  var conf = {
     param_1:value_1,
     param_2:value_2
  };
  
  //传入参数
  func(conf);

【优势】

  • 无需记住所有参数和顺序
  • 可以安全忽略可选参数
  • 易于阅读和维护,易于添加和删除参数
    【劣势】
  • 需要记住参数名称
  • 参数属性无法被压缩器压缩

8.柯里化(Curry)

【函数的apply和call】

函数不仅可以描述为调用,还可以描述为应用

   //原型:Function.prototype.apply()
   //使用: 
   func.apply(region,["param1","param2"]);

【说明】

  • region:表示函数中this所指向的作用域,如果赋值为null则为全局作用域,对于对象方法,必须要赋对象值
  • 参数数组:输入的参数数组,即生成函数的arguments对象
  • call函数为apply的语法糖,即只有一个输入参数时,效率更高:
   func.call(region,"param");

【柯里化过程】

使函数理解并处理部分应用的过程
通用实现方法:

   function currify(fn){
      var slice = Array.prototype.slice, //获取数组的原型方法slice()
          stored_args = slice(arguments, 1); //获取参数中第二个至最后的参数
        //返回利用剩余参数构建的新函数
        return function(){
           var new_args = slice.call(arguments), //获取新函数中所有参数
               args = stored_args.concat(new_args); //并将新参数和原有剩余参数合并在一起,组成新的参数数组
           return fn.apply(null,args); //返回原函数调用
        }
   }

【使用场景】
如果调用同一个函数,且传递的参数绝大多数都是相同的,则该函数可以用于柯里化

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容