函数模式
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); //返回原函数调用
}
}
【使用场景】
如果调用同一个函数,且传递的参数绝大多数都是相同的,则该函数可以用于柯里化