zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    SmartBear正式发布LoadComplete
    CGI, mod_perl, PHP, JSP性能比较
    LoadRunner测试Socket
    性能测试基本功 什么是软件性能
    性能测试基本功 Centos5.5下安装LAMP
    如何聘用优秀的性能测试工程师?
    Spotlight For Oracle使用说明
    SmartBear推出新产品LoadComplete
    LNMP安装
    测试网站性能的在线服务(性能测试云)
  • 原文地址:https://www.cnblogs.com/maplejan/p/3140004.html
Copyright © 2011-2022 走看看