回调函数

在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. 转载--How to Install VMware Tools on CentOS 6.3

    源地址:http://www.ehowstuff.com/how-to-install-vmware-tools-on-centos-6-3/ VMware Tools is a group of u ...

  2. JS实现弹出层对话框

    点击按钮后,弹出层对话框,可交互,点击关闭后才关闭掉对话框. 效果图: 源码: <!doctype html> <html> <head> <meta cha ...

  3. MAC Objective-C 开发经典书籍推荐

    MAC Objective-C 开发经典书籍推荐 闻道有先后,术业有专攻,这句话放到计算机科学领域的理解可能每个人都会不同. 有些人选择一个操作系统,一个体系的编程语言,作一个领域的开发. 有些人选择 ...

  4. html选中图片时,在页面回写图片

    我们经常会遇到这种情况,就是上传一个图片,但是点击一个图片的时候,最好是可以在浏览器预览这个图片,不然用户还以为没有选择图片呢,但是浏览器的安全机制却阻止了这个问题,就是当访问的是网络上的网站的时候, ...

  5. 实现jquery.ajax及原生的XMLHttpRequest跨域调用WCF服务的方法

    关于ajax跨域调用WCF服务的方法很多,经过我反复的代码测试,认为如下方法是最为简便的,当然也不能说别人的方法是错误的,下面就来上代码,WCF服务定义还是延用上次的,如: namespace Wcf ...

  6. 网页颜色分辨测试小游戏的js化辨别及优化

    相信大家都玩过一种网页游戏,通俗的可以叫颜色分辨测试游戏(这个名字对不对我也不知道,不要在意这些细节),也就是下面截图这个玩意,一看就明白:细细把玩过一段时间,作为一个一百米开外男女不分的弱视青年,每 ...

  7. git入门到熟练使用

    最近以为接触ios开发,所以对git也产生了一点兴趣.所以在网上搜索资料开始学习,但大部分都是没用的copy的文章,有一个还不错的,推荐给大家 http://www.liaoxuefeng.com/w ...

  8. SQL--子查询

    什么是子查询 子查询:顾名思义,在一个查询中,有另外一个查询,这个查询就叫做,主查询的子查询. [把一个查询的结果,在另一个查询中使用就叫子查询.(将一个查询语句,作为一个结果集,供其他SQL语句使用 ...

  9. Mac下Vim配置语法高亮

    设置终端的字体颜色 如图,打开终端然后,选择偏好设置,再选择描述文件,再窗口左侧可以选择系统配置好的,或者你也可以自定义,最后别忘了把你的配置设置成默认就行 Vim语法高亮设置 只需要找到vimrc配 ...

  10. 使用html和css的一些经验

    1.注释须知:html中注释不能这样写: <div></div><!--------这是错误写法-------> <div></div>&l ...