回调函数

在javascript中,当一个函数A作为另外一个函数B的其中一个参数时,则称A函数为回调函数,即A可以在函数B的运行周期内执行(开始,中间,结束)。

举例来说,有一个函数用于生成node.

var complexComutation = function(){
    // 内部处理,并返回一个node
}

又有一个findNodes函数声明用于查找所有节点,然后通过callback回调进行执行代码。

var findNodes = function(callback){
    var nodes = [];
    var node = complexComputation();
    // 如果回调函数可用,则执行她
    if(typeof callback === 'function'){
        callback(node);
    }
    nodes.push(node);
    return nodes;
}

关于callback的定义,我们可以事先定义好来用:

// 定义callback
var hide = function(node){
    node.style.display = 'node';
}
var hiddenNodes = findNodes(hide);

也可以在调用的时候使用匿名定义,如下:

// 使用匿名定义callback
var blockNodes = findNodes(function(node){
     node.style.display = 'block';
})

我们平时用的最多的,估计就数jQuery的ajax方法的调用了,通过在done/faild上定义callback,以便在ajax调用成功或者失败的时候做进一步处理,代码如下(本代码基于jquery1.8版):

var menId = $('ul.nav').first().attr('id');
var request = $.ajax({
    url: 'script.php',
    type: 'post',
    data: {id: menuId},
    dataType: 'html'
})
// 调用成功时的回调处理
request.done(function(msg){
    $('#log').html(msg);
})
// 调用失败时的回调处理
request.fail(function(jqXHR, textStatus){
    alert('Request failed:' + textStatus);
});

配置对象

如果一个函数(或方法)的参数只有一个,并且参数为对象字面量,我们则称这种模式为配置对象模式。如下所述:

var her = {
    username: 'gaohan',
    first: 'gao',
    last: 'han'
};
addPerson(her);

则在addPerson函数内部,就可以随意使用her对象内的值了,一般用于初始化工作,例如jquery里的ajaxSetup也就是这种方式来实现的:

// 事先设置好初始值
$.ajaxSetup({
    url: '/xmlhttp/',
    global: false,
    type: 'POST'
})
// 然后再调用
$.ajax({ data:myData });

另外,很多jquery的插件也有这种形式的传参,只不过也可以不传,不传的时候则就使用默认值了。

返回函数

返回函数,则是指一个函数的返回值为另一个函数,或者根据特定的条件灵活创建的新函数,示例如下:

var setup = function(){
    console.log(1);
    return function(){
        console.log(2);
    };
};
// 调用setup函数

her();
// 或者直接调用也可以;
setup()(); // 2

或者我们可以利用闭包的特性,在函数中加入一个计数器,用来计算函数被调用的次数:

var setup = function(){
    var count = 0;
    return function(){
        retunr ++count;
    };
};
var next = serup();
next();
next();
next(); 

Currying

Currying是函数式编程的一个特性,将多个参数的处理转化成单个参数的处理,类似链式调用。

下面来举一个简单的例子:

function add(x, y){
    var oldx = x, oldy = y;
    if(typeof oldy === 'undefined'){
        return function (newy){
             return oldx + newy;
        }
    }
    return x + y ;
}

这样的话,调用方式就有很多种了,比如:

// 测试
typeof add(5); // 'function'
add(3)(4);
// 也可以这样调用
var add2000 = add(2000);
add2000(10); 

接下来我们定义一个,比较常用的currying函数:

// 第一个参数为要应用的function, 第二个参数是需要传入的最少参数个数
function curry(func, minArgs) {
    if(minArgs == undefined) {
         minArgs = 1;
    }
    function funcWithArgsFrozen(frozenargs){
         return function () {
              // 优化处理,如果调用时没有参数,返回该函数本身
              var ages = Array.prototype.slice.call(arguments);
              var newArgs = frozenargs.concat(args);
              if(newArgs.length >= minArgs) {
                   return func.apply(this, newArgs);
              }else{
                   return funcWithArgsFrozen(newArgs);
              }
         }
    }
    return funcWithArgsFrozen([]);
}

这样,我们就可以随意定义我们的业务行为了,比如定义加法:

var plus = curry(function(){
    var result = 0;
    for(var i=0; i<arguments.length; ++i){
        result += arguments[i];
    }
    return result;
}, 2)

使用方法多种多样。

plus(3, 2) // 正常调用
plus(3) // 偏应用
plus(3) (2) // 完整应用(返回5)
plus() (3) () () (2) // 返回5
plus(3, 2, 4, 5) // 可以接受多个参数
plus(3) (2, 3, 5) // 同理

JavaScript里的Function有很多特殊的功效,可以利用闭包以及arguments参数特性实现很多不同的技巧,下一篇我们将继续介绍利用Function进行初始化的技巧。

立即执行的函数

// 声明完函数以后,立即执行该函数
(function (){
     console.log('watch out');
}());
// 这种方式声明的函数,也可以立即执行
!function(){
    console.log('watch out!');
}();
// 如下的方式也都可以哦
~function(){ /* do someing*/ }();
-function(){ /* do someing*/ }();
+function(){ /* do someing*/ }();

立即执行的对象初始化

该模式的意思是指在声明一个对象(并非函数)的时候、立即执行对象里的某一个方法进行初始化工作,通常该模式可以用在执行一次性的代码上:

({
    // 这里你可以定义常量,并设置其它值
    maxwidth:  600,
    maxheight: 400,
    // 当然也可以定义方法
    gimmMax: function(){
        return this.maxwidth + 'x' + this.maxheight;
    },
    init: function(){
        console.log(this.gimmeMax());
        // 更多代码......
    }
}).init(); // 这样就能初始化喽

分支初始化

分支初始化是指在初始化的时候,根据不同的条件(场景)初始化不同的代码,也就是所谓的条件语句赋值。之前我们在处理的时候,常常使用类似下面的代码:

var utils = {
    addListener: function(el, type, fn){
        if(typeof window.addEventListener === 'function'){
             el.addEventListener(type, fn, false);
        }else if(typeof document.attachEvent !== 'undefined'){
             el.attachEvent('on' + type, fn);
        }else{
             el['on' + type] = fn;
        }
    },
    removeListener: function(el, type, fn){
        // 神马之类的......
    }
}

我们来改进一下,首先我们要定义两个接口,一个用来add事件句柄,一个用来remove事件句柄,代码如下:

var utils = {
    addListener: null,
    removeListener: null
};

实现代码如下:

if (typeof window.addEventListener === 'function') {
    utils.addListener = function (el, type, fn) {
        el.addEventListener(type, fn, false);
    };
} else if (typeof document.attachEvent !== 'undefined') { // IE
    utils.addListener = function (el, type, fn) {
        el.attachEvent('on' + type, fn);
    };
    utils.removeListener = function (el, type, fn) {
        el.detachEvent('on' + type, fn);
    };
} else { // 其它旧浏览器
    utils.addListener = function (el, type, fn) {
        el['on' + type] = fn;
    };
    utils.removeListener = function (el, type, fn) {
        el['on' + type] = null;
    };
}

用起来,是不是就很方便了?代码也优雅多了。

自声明函数

一般是在函数内部,重写重名函数代码,比如:

var her = function(){
    alert('Boo!');
    her = function(){
        alert('Double Boo!!');
    }
}

这种代码,非常容易使人迷惑,我们先来看看例子的执行结果:

// 1.添加新属性
scareMe.prototype = 'Anna';
// 2. scareMe赋予一个新值
var prank = scareMe;
// 3. 作为一个方法调用
var spooky = {
    boo: scareMe
}
// 使用新变量名称进行调用
prank(); // 'Boo!'
prank(); // 'Boo!'
console.log(spooky.boo.property); // 'properly'

通过执行结果,可以发现,将定于的函数赋值与新变量(或内部方法),代码并不执行重载的scareMe代码,而如下例子则正好相反:

// 使用自声明函数
scareMe(); // Double boo!
scareMe(); // Double boo!
console.log(scareMe.property); // undefined

大家使用这种模式时,一定要非常小心才行,否则实际结果很可能和你期望的结果不一样,当然你也可以利用这个特殊做一些特殊的操作。

内存优化

该模式主要是利用函数的属性特性来避免大量的重复计算。通常代码形式如下:

var myFunc = function (param) {
    if (!myFunc.cache[param]) {
        var result = {};
        // ... 复杂操作 ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
};

// cache 存储
myFunc.cache = {};

但是上述代码有个问题,如果传入的参数是toString或者其它类似Object拥有的一些公用方法的话,就会出现问题,这时候就需要使用传说中的hasOwnProperty方法了,代码如下:

var myFunc = function (param) {
    if (!myFunc.cache.hasOwnProperty(param)) {
        var result = {};
        // ... 复杂操作 ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
};

// cache 存储
myFunc.cache = {};

或者如果你传入的参数是多个的话,可以将这些参数通过JSON的stringify方法生产一个cachekey值进行存储,代码如下:

var myFunc = function () {
    var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
        result;
    if (!myFunc.cache[cachekey]) {
        result = {};
        // ... 复杂操作 ...
        myFunc.cache[cachekey] = result;
    }
    return myFunc.cache[cachekey];
};

// cache 存储
myFunc.cache = {};

或者多个参数的话,也可以利用arguments.callee特性:

var myFunc = function (param) {
    var f = arguments.callee,
        result;
    if (!f.cache[param]) {
        result = {};
        // ... 复杂操作 ...
        f.cache[param] = result;
    }
    return f.cache[param];
};

// cache 存储
myFunc.cache = {};
总结

javascript --- Function模式的更多相关文章

  1. javascript运行模式:并发模型 与Event Loop

    看了阮一峰老师的JavaScript 运行机制详解:再谈Event Loop和[朴灵评注]的文章,查阅网上相关资料,把自己对javascript运行模式和EVENT loop的理解整理下,不一定对,日 ...

  2. Javascript原型模式总结梳理

    在大多数面向对象语言中,对象总是由类中实例化而来,类和对象的关系就像模具跟模件一样.Javascript中没有类的概念,就算ES6中引入的class也不过是一种语法糖,本质上还是利用原型实现.在原型编 ...

  3. JavaScript严格模式详解

    转载自阮一峰的博客 Javascript 严格模式详解   作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...

  4. (转)深入理解JavaScript 模块模式

    深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...

  5. JavaScript严谨模式(Strict Mode)

    下面的内容翻译自It’s time to start using JavaScript strict mode,作者Nicholas C.Zakas参与了YUI框架的开发,并撰写了多本前端技术书籍,在 ...

  6. Javascript编程模式(JavaScript Programming Patterns)Part 1.(初级篇)

    JavaScript 为网站添加状态,这些状态可能是校验或者更复杂的行为像拖拽终止功能或者是异步的请求webserver (aka Ajax). 在过去的那些年里, JavaScript librar ...

  7. JQuery日记6.5 Javascript异步模式(一)

    理解力JQuery前实现异步队列,有必要理解javascript异步模式. Javascript异步其实并不严重格异步感,js使某些片段异步方式在将来运行,流不必等待继续向下进行. 在多线程的语言中最 ...

  8. 浅析JavaScript工厂模式

    这里主要介绍两种工厂模式,第一种“简单工厂模式”,第二种“工厂方法模式” 简单工厂模式 1.定义 由一个工厂对象决定对象创建某一种产品对象的的实例.主要用来创建同一类对象. 2.具体需求 现在有一个登 ...

  9. Javascript 严格模式(strict mode)详解

    Javascript 严格模式详解   一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode).顾名思义,这种模式使得Ja ...

随机推荐

  1. Js杂谈-DOM

    前言 对jQuery的依赖.导致js的原生方法的淡忘,如果是封装自己的库,那势必要用到js的许多原生方法.从Jquery强大的dom处理开始,我们开始回顾javascript那些古老而坚挺的DOM方法 ...

  2. 自制jquery可编辑的下拉框

    昨天看到QQ登录的时候,可以选择以前登录过的账户,这个东西也可以在网站登录的时候用到,所以我就想做一个这样的插件:在网上查了很多,没有找到合适自己的,所以决定自动制作一个. 原理就是一个textbox ...

  3. web前端学习笔记(CSS盒子的定位)

    相对定位 使用相对定位的盒子的位置常以标准流的排版方式为基础,然后使盒子相对于它在原本的标准位置偏移指定的距离.相对定位的盒子仍在标准流中,它后面的盒子仍以标准流方式对待它.      使用relat ...

  4. Elasticsearch之_default_—— 为索引添加默认映射

    前篇说过,ES可以自动为文档设定索引.但是问题也来了——如果默认设置的索引不是我们想要的,该怎么办呢? 要知道ES这种搜索引擎都是以Index为实际的分区,Index里面包含了不同的类型,不同的类型是 ...

  5. SQL Server中的事务日志管理(7/9):处理日志过度增长

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

  6. SQL Server代理(1/12):配置和概况

    SQL Server代理是所有实时数据库的核心.代理有很多不明显的用法,因此系统的知识,对于开发人员还是DBA都是有用的.这系列文章会通俗介绍它的很多用法. SQL Server代理是SQL Serv ...

  7. Asp.net 高性能 Sqlite ORM 框架之 sqliteSugar

    一.介简 easyliter框架的升级版本,并且正式命名为SqliteSugar框架,另外Sugar系列还有 MySql和MsSql版本,Oracle版本待开发中(因为客户端太大一直在忧郁当中) 用S ...

  8. Windows 8.1 系统ISO镜像下载或自Win8应用商店升级方法

    1)下载 -- 面向全体用户的Win8.1预览版ISO镜像下载(28日开放): 安装时请输入微软官方提供给大家的产品密钥:NTTX3-RV7VB-T7X7F-WQYYY-9Y92F. 64位简体中文版 ...

  9. 无刷新提交表单(非Ajax实现)

    HTML代码: <iframe id="fra" name="frm" style="display: none;"></ ...

  10. 简单的ASP.NET MVC发布

    学习这样久的ASP.NET MVC,但一直没有实现过发布MVC程序.今天来试试. 分两个部分进行,先是第一部分,Visual Studio的publish:创建一个带有实例的ASP.NET MVC: ...