zoukankan      html  css  js  c++  java
  • JS函数表达式

    函数表达式是定义函数的一种方式,另一种是之前提到的函数声明。

    //函数声明
    function functionName(args){
        //函数体
    }
    
    
    //函数表达式
    var functionName = function(args){
        //函数体
    };

     函数声明和函数表达式之间的区别,主要是函数声明提升,意思是在执行代码之前会读取函数声明。

    没有名字的函数表达式也叫匿名函数。

    一、递归

    递归是一个函数通过名字调用自身。

    因为函数名可能会发生改变,如果函数内部调用自身的语句仍使用原来的名字,就会发生错误,在这种情况下,使用arguments.callee可以解决这个问题。arguments.callee是一个指向正在执行的函数的指针。

    function factorial(num){
        if (num <= 1){
            return 1;
        } else {
            return num * arguments.callee(num-1);
        }
    }

    除了使用arguments.callee,还可以使用命名函数表达式来达成同样的结果。

    var factorial = (funtion f(num) {
        if num (num <= 1){
            return 1;
        } else {
            return num * f(num-1);
        }
    });

    二、闭包

    闭包是指有权访问另一个函数作用域中的变量的函数。

    关于作用域:当某个函数被调用时,会创建一个执行环境及对应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象。作用域链就是由一组有序(按调用顺序由内到外)的活动对象的引用组成。在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。   作用域链本质上是一个指向变量对象的指针列表,他只引用但不包含实际对象。  一般来讲,当函数执行完毕后,局部活动对象会被销毁,内存中仅保存全局作用域。但是闭包的情况又有所不同。

    创建闭包必须维护额外的作用域:匿名函数作为返回值在函数内部定义,如果在外部通过函数表达式引用并调用这个匿名函数,就形成了闭包。在函数执行后,因为匿名函数仍被引用,所以匿名函数的活动对象和作用域链依然存在,这导致匿名函数所在的函数的活动对象仍然处于被引用状态,留在内存中。  可以通过解除对匿名函数的引用(设置引用值为null)来解决,这样就释放了内存。

    1. 闭包与变量

    闭包保存的是整个变量对象,而不是某个特殊变量。所以对于具有同一变量名的值,闭包只会取得最后一个值。关于这个问题,下面的例子可以清晰地说明:

    function createFunctions(){
        var result = new Array();
        
        for (var i=0; i < 10; i++){
            result[i] = function(){
                return i;
            };
        }
        
        return result;
    }

    这个函数会返回一个函数数组。表面上看,似乎数组内每个函数都应该返回自己的索引值。但实际上,每个函数都返回10,因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以他们引用的都是用一个变量i=10。这个问题可以通过创建另一个匿名函数解决。

    function createFunctions(){
        var result = new Array();
        
        for (var i=0; i < 10; i++){
            result[i] = function(num){
                return function(){
                    return num;
                };
            }(i);
        }
        
        return result;
    }

    每循环一次,外层的匿名函数就执行一次,将索引值传入当前位置的函数里(虽然都叫num,但是每个num都是按值传递,都只是一个副本)。

    2. 关于this对象

    每个函数在被调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。

    this对象是在运行时基于函数的执行环境绑定的。虽然匿名函数在某个函数内定义,但一般匿名函数都是在全局函数中执行,所以其this对象通常指向window。

    如果想访问外部作用域的this对象,可以先在外部函数中将this的值保存在一个闭包能访问到的变量里。

    3. 内存泄漏

    如果闭包创建了一个循环引用,就会导致无法减少变量的引用数,因此占用的内存就永远不会回收。

    三、 模仿块级作用域

    JS中只有执行环境(函数作用域)的概念,而没有块级作用域的概念。所以如果想实现块级作用域,就要通过匿名函数来模仿。具体步骤是先定义一个函数,然后立即调用它。这样即可以执行其中的代码,又不会在内存中留下对该函数的引用。结果就是函数内部的所有变量都会立即被销毁。

    因为函数声明后面不能跟圆括号,所以我们选择使用函数表达式的方式。代码如下:

    (function(){
        //这里是块级作用域
    })();

    四、私有变量

    JS中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量(私有变量)。

    任何在函数中定义的变量,都是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

    特权方法是有权访问私有变量和私有函数的公有方法。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法,基本模式如下:

    function MyObject(){
    
        //私有变量和私有函数
    
    
        //特权方法
        this.func = function (){
        //有权访问私有变量和函数
        };
    }

    但是这种方式针对每个实例都会创建同样一组新方法,而是用私有静态变量来实现特权方法可以避免这个问题。

    1. 静态私有变量

    在私有作用域中定义私有变量和函数,并省略var构造全局函数及下及相应的特权方法。

    (function(){
    
        var name = "";
        
        Person = function(value){                
            name = value;                
        };
        
        Person.prototype.getName = function(){
            return name;
        };
        
        Person.prototype.setName = function (value){
            name = value;
        };
    })();

    2. 模块模式

    前面的模式是用于为自定义类型创建私有变量和特权方法的。模块模式则视为单例创建私有变量和特权方法。单例,指只有一个实例的对象。

    在私有作用域中定义私有变量和函数,并返回公有变量和特权方法,由单例接收。

    function BaseComponent(){
    }
    
    function OtherComponent(){
    }
    
    var application = function(){
    
        //private variables and functions
        var components = new Array();
    
        //initialization
        components.push(new BaseComponent());
    
        //public interface
        return {
            getComponentCount : function(){
                return components.length;
            },
    
            registerComponent : function(component){
                if (typeof component == "object"){
                    components.push(component);
                }
            }
        };
    }();

    3. 增强的模块模式

    和模块模式类似,不同的是显式创建要返回的对象,再返回之前可以添加某些属性和方法对其加以增强。这种模式适合那些单例必须是某种类型的实例的情况。

    function BaseComponent(){
    }
    
    function OtherComponent(){
    }
    
    var application = function(){
    
        //private variables and functions
        var components = new Array();
    
        //initialization
        components.push(new BaseComponent());
    
        //create a local copy of application
        var app = new BaseComponent();
    
        //public interface
        app.getComponentCount = function(){
            return components.length;
        };
    
        app.registerComponent = function(component){
            if (typeof component == "object"){
                components.push(component);
            }
        };
    
        //return it
        return app;
    }();
  • 相关阅读:
    easyui datetimebox 日期控件绑定双击日期选择时间
    js 中call和apply的应用
    js中数组的合并和对象的合并
    flex也可以让背景透明
    收集了一些as的面试题,给HR准备的
    [转]PureMVC的十个小提示
    12个Flex常用功能代码
    43个热门Flex和ActionScript 3.0 APIs,技巧和工具[转]
    转载+原创PureMVC 实例讲解
    PureMVC使用时的注意事项
  • 原文地址:https://www.cnblogs.com/wangxinwen/p/9599979.html
Copyright © 2011-2022 走看看