javascript --- Function模式
回调函数
在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模式的更多相关文章
- javascript运行模式:并发模型 与Event Loop
看了阮一峰老师的JavaScript 运行机制详解:再谈Event Loop和[朴灵评注]的文章,查阅网上相关资料,把自己对javascript运行模式和EVENT loop的理解整理下,不一定对,日 ...
- Javascript原型模式总结梳理
在大多数面向对象语言中,对象总是由类中实例化而来,类和对象的关系就像模具跟模件一样.Javascript中没有类的概念,就算ES6中引入的class也不过是一种语法糖,本质上还是利用原型实现.在原型编 ...
- JavaScript严格模式详解
转载自阮一峰的博客 Javascript 严格模式详解 作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...
- (转)深入理解JavaScript 模块模式
深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...
- JavaScript严谨模式(Strict Mode)
下面的内容翻译自It’s time to start using JavaScript strict mode,作者Nicholas C.Zakas参与了YUI框架的开发,并撰写了多本前端技术书籍,在 ...
- Javascript编程模式(JavaScript Programming Patterns)Part 1.(初级篇)
JavaScript 为网站添加状态,这些状态可能是校验或者更复杂的行为像拖拽终止功能或者是异步的请求webserver (aka Ajax). 在过去的那些年里, JavaScript librar ...
- JQuery日记6.5 Javascript异步模式(一)
理解力JQuery前实现异步队列,有必要理解javascript异步模式. Javascript异步其实并不严重格异步感,js使某些片段异步方式在将来运行,流不必等待继续向下进行. 在多线程的语言中最 ...
- 浅析JavaScript工厂模式
这里主要介绍两种工厂模式,第一种“简单工厂模式”,第二种“工厂方法模式” 简单工厂模式 1.定义 由一个工厂对象决定对象创建某一种产品对象的的实例.主要用来创建同一类对象. 2.具体需求 现在有一个登 ...
- Javascript 严格模式(strict mode)详解
Javascript 严格模式详解 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode).顾名思义,这种模式使得Ja ...
随机推荐
- Hadoop官方文档翻译——HDFS Architecture 2.7.3
HDFS Architecture HDFS Architecture(HDFS 架构) Introduction(简介) Assumptions and Goals(假设和目标) Hardware ...
- office快速制作简历
毕业的一年是由学校向社会转变的一年,面临着人生的一个重大转折--找工作.在如今信息爆炸的时代,纵使力拔山兮气盖世也难免会被遗落芳草之中而不得一展宏图.对未来的憧憬,对美好生活的向往,或多或少你需要一份 ...
- Dapper,大规模分布式系统的跟踪系统--转
原文地址:http://bigbully.github.io/Dapper-translation/ 概述 当代的互联网的服务,通常都是用复杂的.大规模分布式集群来实现的.互联网应用构建在不同的软件模 ...
- JavaWeb:Web与HTTP协议简介
JavaWeb:Web与HTTP协议简介 Web的概念 什么是Web: Web是网络上使用最广泛的分布式应用架构. 旨在共享分布在网络上的各个Web服务器中的所有互相连接的信息. 三个特征: 用HTM ...
- Image Wall - jQuery & CSS3 图片墙效果
今天我们要为您展示如何基于 jQuery 和 CSS3 创建一个整洁的图片墙效果.我们的想法是在页面上洒上一些大小不同的缩略图,并在当我们点击图片时候显示丝带,会显示一些描述,再次点击缩略图时,丝带将 ...
- URL格式
URL由三部分组成:资源类型.存放资源的主机域名.资源文件名. URL的一般语法格式为: (带方括号[]的为可选项): protocol :// hostname[:port] / path / [; ...
- Redis发布订阅实现原理
发布订阅中使用到的命令就只有三个:PUBLISH,SUBSCRIBE,PSUBSCRIBE PUBLISH 用于发布消息 SUBSCRIBE 也叫频道订阅,用于订阅某一特定的频道 PSUBSCRIBE ...
- 30天C#基础巩固------面向鸭子编程,关于string和File的练习
面向对象编程就是面向抽象的父类进行编程,具体的实现不用考虑,由子类决定.<经典的说法--面向鸭子编程> eg:鸭子的编程,<对于多态的理解> 我们都习惯把使用 ...
- Dapper学习 - Dapper的基本用法(三) - CUD
之前介绍了Dapper的查询, 存储过程, 函数的调用, 接下来要说一下Dapper的增删改, 其实Dapper的增删改, 都是同一种模式的. 我就不分开介绍了, 直接在一个例子上展现好了. var ...
- WinPhone学习笔记(一)——页面导航与页面相关
最近学一下Windows Phone(接下来简称“WinPhone”)的开发,在很久很久前稍探究一下WinPhone中对一些传感器的开发,那么现在就从头来学学WinPhone的开发.先从WinPhon ...