JavaScript的函数

很随意的总结,几乎是代码片段,纯粹当个人笔记。

 

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

何时使用柯里化?
当发现同一个函数,被调用多次,并且每次传递的参数绝大多数是相同的,这个时候应该用柯里化。

 

以上。

参考:《JavaScript设计》

 

 

本文作者:Maple Jan

本文链接:http://www.cnblogs.com/maplejan/archive/2013/06/17/3140004.html

 

 

 

posted @ 2013-06-17 13:42  Maple Jan  阅读(352)  评论(0编辑  收藏  举报