很随意的总结,几乎是代码片段,纯粹当个人笔记。
JavaScript中,定义Function对象有两种方法。
函数声明(function declaration)
1 function fn() { 2 // 代码 3 };
函数表达式(function expression)
var fn = function () { // 代码 };
函数作回调(callback)时的作用域
其实回调并不是传递一次性的匿名函数或全局函数,而是对象的方法。
看下面的代码,输出结果和我们所想的不一致。
1 var obj = {}; 2 obj.color = "red"; 3 obj.getColor = function () { 4 return { 5 _this: this, 6 color: this.color 7 } 8 }; 9 10 function printColor(callback) { 11 console.log(callback()); 12 } 13 14 printColor(obj.getColor); // console print {_this: Window, color: undefined}
要解决这个问题,可以利用call()或者apply():
1 function printColor(callback, obj) { 2 console.log(callback.call(obj)); 3 } 4 5 printColor(obj.getColor, obj); // console print {_this: Object, color: "red"}
但这种写法不够直观,我们换一个思路试试:
1 function printColor(key, obj) { 2 console.log(obj[key]()); 3 } 4 5 printColor("getColor", obj); // console print {_this: Object, color: "red"}
返回函数
所谓的返回函数,其实就是:函数执行完后返回一个匿名函数。
如下:
1 function fnA() { 2 alert(1); 3 return function () { 4 alert(2); 5 }; 6 } 7 8 var fnB = fnA(); // alert 1 9 fnB(); // alert 2
实际应用中,可创建一个闭包,用来存储私有数据:
1 function setup() { 2 var count = 0; 3 return function () { 4 return ++count; 5 }; 6 } 7 8 var next = setup(); 9 next(); // return 1 10 next(); // return 2 11 next(); // return 3
个人觉得更好的用法是,结合即时函数:
1 var next = (function () { 2 var count = 0; 3 return function () { 4 return ++count; 5 }; 6 })(); 7 8 next(); // return 1 9 next(); // return 2 10 next(); // return 3
如果结合了即时函数,就可以做条件预加载(Conditional Advance Loading):·
1 var addEvent = (function (W) { 2 if (W.addEventListener) { 3 return function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (W.attachEvent) { 7 return function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 return function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 } 15 })(window);
关于返回函数,最大的价值还是在于创建闭包。如果要存储私有数据,闭包固然十分好用。
但如果像上述代码,并不需要存储私有数据,返回函数就显得有点多余。
但我们依然可以条件预加载:
1 var addEvent = function () {}; 2 if (window.addEventListener) { 3 addEvent = function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (window.attachEvent) { 7 addEvent = function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 addEvent = function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 }
只是,这些写貌似没那么炫了...
如果只有一个判断条件,建议使用三元运算符,更直观更简单:
1 var addEvent = window.addEventListener ? function (target, type, handler) { 2 target.addEventListener(type, handler, false); 3 } : function (target, type, handler) { 4 target.attachEvent("on" + type, handler); 5 };
自定义函数
所谓的自定义函数,可以理解为:在函数内部覆盖(重新定义)函数自身。
1 function fnA() { 2 alert(1); 3 fnA = function () { 4 alert(2); 5 }; 6 } 7 fnA(); // alert 1 8 fnA(); // alert 2
利用自定义函数的特点,我们可以对上面的addEvent进行优化:
1 var addEvent = function(target, type, handler) { 2 if (target.addEventListener) { 3 addEvent = function (target, type, handler) { 4 target.addEventListener(type, handler, false); 5 }; 6 } else if (target.attachEvent) { 7 addEvent = function (target, type, handler) { 8 target.attachEvent("on" + type, handler); 9 }; 10 } else { 11 addEvent = function (target, type, handler) { 12 target["on" + type] = handler; 13 }; 14 } 15 addEvent(target, type, handler); 16 };
这种写法又叫:延迟加载(Lazy Loading) 或 惰性函数定义(Lazy Function Definition)。
当函数执行的那一刻才会进行“初始化”,重新去定义函数。
即时函数(immediate function)
什么是即时函数?顾名思义,就是立刻执行的函数。
下面三种都是即时函数的写法:
1 (function () { 2 alert(1); 3 }()); 4 5 (function () { 6 alert(2); 7 })(); 8 9 var fn = function() { 10 alert(3); 11 }();
网页开发中,各种事件是最难抽象的,因为业务逻辑层出不穷。
看下面代码:
1 // code... 2 var $body = $("body"), 3 MESSAGE = "Hello!"; 4 5 $body.on("click", function() { 6 console.log(MESSAGE); 7 }); 8 // code...
这是一段十分常见的事件绑定的代码。
就这样看十分清晰,但如果把它摆在n段事件绑定的代码之中,估计就会让人抓狂了。
单单避免变量名冲突这一点,就十分蛋疼了。
利用即时函数让代码模块化:
1 // code... 2 (function () { 3 var $body = $("body"), 4 MESSAGE = "Hello!"; 5 6 $body.on("click", function() { 7 console.log(MESSAGE); 8 }); 9 })(); 10 // code...
即时对象初始化(immediate object initialization)
如果你不喜欢即时函数的方式,也可以换一个形式:
1 // code... 2 ({ 3 $body: $("body"), 4 MESSAGE: "Hello!", 5 bindClick: function() { 6 var msg = this.MESSAGE; 7 this.$body.on("click", function() { 8 console.log(msg); 9 }); 10 }, 11 bindEvent: function() { 12 this.bindClick(); 13 } 14 }).bindEvent(); 15 // code...
这种做法,有一个比较大的缺陷是,大部分压缩器都不会对其进行“变量名压缩”。
目前只有 Google Closure Compile 的“高级模式”才能对其进行“变量名压缩”。
柯里化(Currying)
在纯粹的函数式编程语言中,函数并不描述为被调用(called或invoked),而是描述为应用(applied)。
1 alert(1); // 调用函数 2 alert.apply(null, [1]); // 应用函数
柯里化例子:
1 function currying(fn) { 2 var slice = Array.prototype.slice, 3 storedArgs = slice.call(arguments, 1); 4 return function () { 5 var newArgs = slice.call(arguments), 6 args = storedArgs.concat(newArgs); 7 return fn.apply(null, args); 8 }; 9 } 10 11 function add(x, y) { 12 return x + y; 13 } 14 15 add(5, 3); // return 8 16 var add10 = currying(add, 10); 17 add10(11); // return 21
何时使用柯里化?
当发现同一个函数,被调用多次,并且每次传递的参数绝大多数是相同的,这个时候应该用柯里化。
以上。
本文作者:Maple Jan
本文链接:http://www.cnblogs.com/maplejan/archive/2013/06/17/3140004.html