JavaScript——函数表达式

定义函数的方式

有两种,一种是函数声明,一种是函数表达式

函数声明

  • 它的语法是function fun(arg0,arg1,arg2){//函数体}
  • name属性:可以访问到函数的名字。alert(fun.name);//"fun"
  • 函数声明有一个重要特征:函数声明提升
    • 意思是执行代码之前会先读取函数声明,意味着可以把函数声明放在调用它的语句后面
sayhi();
function sayhi(){
    alert("hi");
}

函数表达式

  • 语法var fun=function(arg0,arg1,arg2){//函数体};
  • 这种情况下创建的函数叫做匿名函数(拉姆达函数),匿名函数的name属性是空字符串。
  • 函数表达式在使用前必须先赋值。

递归

  • 本质:自己调用自己
  • 在编写递归函数时,如果显式的调用函数名(指针),会因为这个指针可能被重置了而出现错误。为了解除这种耦合性,可以使用arguments.callee代替函数名,这是一个指向正在执行的函数的指针。
  • 在严格模式下,使用命名函数表达式来达成同上安全的效果。
    var factorial=function fun(n){
        if(n<=1)
            return 1;
        eles
            return n*fun(n-1);
    };
    alert(factorial(4));//24
    alert(fun(4));//error:fun is not defined

闭包

闭包指有权访问外部函数作用域中的变量的函数

  • 创建闭包的方式
    在一个函数内部创建并返回另一个函数。
  • 需要注意一点,在函数内部定义变量时,要用var,否则就是定义了一个全局变量。

  • 先讲一下函数的作用域链
    假设有个函数fun(value);在函数fun(value);被创建的时候,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用fun(value)函数时,会为函数创建一个执行环境,然后复制[[Scope]]属性中的对象构建起执行环境的作用域链,之后,又有一个活动对象(arguments,value)被创建并被推入执行环境作用域链的前端。所以fun函数的执行环境包含两个变量对象:本地活动对象和全局变量对象,当在一个函数内部调用一个函数时,内部函数的执行环境包含内部活动对象,外函数对象,全局变量对象,以此可以得到作用域链。

  • 闭包
    因为创建闭包就是在一个函数内部创建另一个函数,所以闭包的作用域链包含本地活动对象、包含它的函数的对象、全局变量对象,所以,当在f1内部定义且返回f2,而f2中可能会引用f1的变量。当返回f2时,f1已经执行完毕,其执行环境的作用域链已经被销毁,但它的活动对象还被包含在f2中,所以不会报错,直到f2执行完毕被销毁后,或者清空f2(f2=null),f1的活动对象才会被销毁。
  • 常见的闭包的模式:
    var x=function A(a){
        return function B(b){...a...};//B是闭包
    }
    var y=a(b);//y实际上就是闭包B函数
  • 闭包的缺点
    由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过度使用闭包可能会导致内存占用过多。

闭包与变量

  • 小心:闭包会取得包含函数中任何变量的最后一个值。比如书中的这个例子:
    function fun(){
        var result=new Array();
        for(var i=0;i<result.length;i++){
            result[i]=funtion(){
                return i;
            };
        }
        return result;
    }
    var f=fun();
    for(var i=0;i<result.length;i++){
        console.log(f[i]());
    }
  • 这个例子中返回的result数组中的值都是10,为什么呢,因为result[i]=…;这一句代码中的function并没有执行,只是定义了,没有调用。所以当return result;时,i=10,所以之后的代码执行时,result的每个值都是10。要调用得需要有()。改正后的代码如下:
    function fun(){
        var result=new Array();
        for(var i=0;i<result.length;i++){
            result[i]=function(n){
                return function(){
                    return n;
                };
            }(i);//定义了一个匿名函数,这样result[i]都有变量i的一个副本。
        }
        return result;
    }
    var f=fun();
    for(var i=0;i<result.length;i++){
        console.log(f[i]());
    }

关于this对象

  • this对象是在运行时基于函数的执行环境绑定的。
  • 在全局函数中,this等于window;而当函数被作为某个对象的方法调用时,this等于那个对象。
  • 调用分为:方法调用和函数调用,方法调用时,this指向对象,函数调用时,就用到了之前学到的作用域链。
  • 会遇到的问题:匿名函数有时没有取得其包含作用域(或外部作用域)的this对象,解决办法就是,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。arguments对象同样如此。因为内部函数在搜索这两个变量时,只会搜索到其活动对象为止。

内存泄漏

如果闭包的作用域链中保存着一个HTML元素,那么该元素将无法被销毁。为了防止在匿名函数中循环引用,可在匿名函数之后将元素赋为null。

模仿块级作用域

  • 语法:(function(){//这里是块级作用域})();
  • 无论在什么地方,只要临时需要一些变量,就可以使用此方法。
  • 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。
(function(){
    var now=new Date();
    if(now.getMouth()==0&&now.getDate()==1){
        alert("happy new year");
    }
})();
  • 优点:这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

私有变量

  • 任何在函数中定义的变量,都可以认为是私有变量。
  • 利用闭包可以访问私有变量。
  • 把有权访问私有变量和私有函数的公有方法称为特权方法
  • 两种创建特权方法的方式:

    • 构造函数中定义特权方法:

      • 模式:

        function Object(){
            //定义私有变量和私有函数
            ...
            //特权方法
            this.publicMethod=function(){
                //访问私有变量和函数,可以进行修改等操作
                ...
            };
        }
      • 在创建了Object实例后,除了使用publicMethod()这一个途径外,没有任何办法可以直接访问私有变量和函数。

      • 利用私有和特权成员,可以隐藏那些不应该被直接修改的数据。
      • 缺点:必须使用构造函数模式来达到这个目的,针对每个实例都会创建同样一组新方法。
    • 在私有作用域中定义私有变量或函数。

      • 模式:

        (function (){
            //私有变量和私有函数
            ...
            //构造函数,创建全局变量
            ...
            ///共有/特权方法
            Object.prototype.publicMethod=function(){
                //访问私有变量和函数,可以进行修改等操作
            };//是在原型上定义的
        })();
      • 以这种方式创建静态私有变量会因为使用原型而增加代码复用,但每个实例都没有自己的私有变量。

模块模式

  • 作用:为单例创建私有变量和特权方法。
  • 语法形式:
var singleton=function(){
    //私有变量和私有函数
    ...
    //特权/共有方法和属性
    return{
        ...
    };
}();
  • 如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,就可以使用模块模式。

增强的模块模式

  • 增强的内容:在返回对象之前加入了对其增强的代码。
  • 适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。
  • 语法:
var singleton=function(){
    //私有变量和私有函数
    ...
    //创建对象
    var object=new CustomType();
    //添加特权/共有属性和方法
    object.publicProperty=true;
    ...
    //返回这个对象
    return object;
}();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值