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

      度过一个愉快短暂的周末,又可以开始学习了!我爱学习,学习使人进步。今天学习函数表达式,着重学习下闭包函数。

    函数表达式

       可以在定义的函数声明之前调用它,但是不能在定义函数表达式之前调用它 

    /**
     * 理解函数声明和函数表达式的区别,学习拉姆达(Lambda)表达式
     */
    functionName(a);
    aFuncton(a);//错误 function fuctionName(arg){};//函数声明 var aFunction=function (arg) {};//函数表达式(匿名函数) /* 函数声明:在执行代码之前会先读取函数声明,函数声明可以放在调用它的语句后面。
     Lambda表达式:创建匿名函数的一种方法,=>来表示
     运算符右边的表达式对运算符左边指定的参数执行操作
    */
    var x=3,
       fun=x=>{return x*x;};//lambda表达式

    递归:在函数内部调用自己

      

    /**
     * 借用arguments.callee和使用函数命名表达式实现递归
     */
    //递归函数
    function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*factorial(num-1);
        }
    };
    var anotherFactorial=factorial;
    factorial=null;
    alert(anotherFactorial(4));//出错,factorial值发生变化。
    //解决方案:1.非严格模式下
    function factorial(num){
        if(num<=1){
            return 1;
        }else{
            return num*arguments.callee(num-1);
        }
    };
    //2.命名函数表达式
    var  factorial=(function f(num){
        if(num<=1){
            return 1;
        }else {
            return num * f(num - 1);
        }
    });

     闭包:能够访问另一个函数作用域中的变量的函数。

      作用域链:本质上是一个指向变量对象的指针列表

        创建函数时,会创建一个预先包含全局变量对象的的作用域链,这个作用域链被保存在内部的[[Scope]]属性中,调用函数时创建一个执行环境,然后复制[[Scope]]属性中的对象构建起执行环境的作用域链,然后有一个函数的活动对象被创建并推入执行环境作用域链的前端。

       在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中   

    function createComparisonFunction(propertyName) {
        return function(object1,object2){
            var value1=object1[propertyName];
            var value2=object2[propertyName];
            if(value1<value2){return -1;}
            else if(value1>value2){return 1;}
            else{return 0;}
        };
    }
    //创建函数
    var compareNames=createComparisonFunction('name');
    //调用函数
    var result=compareNames({name:'赵云'},{name:'马超'});
    //解除对匿名函数的引用,以便释放内存
    compareNames=null;

      作用域链可以想象成一个列表,里面的函数创建时候内部创建它,这个列表中,最上面的

      上面的例子中,有两个作用域链,createComparisonFunction和匿名函数function(object1,object2)的作用域链分别是A和B。匿名函数从compareName()被返回后,B包含全局变量对象、createComparisonFunction函数的活动对象和匿名函数的活动对象的引用,createComparisonFunction函数执行完毕A被销毁,但是它的活动对象被B引用,虽然这样可以在全局环境中访问外部这个函数的变量了,但这个外部函数的活动对象已然存在。所以调用函数结束后要解除对匿名函数的引用。

        

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

        上面的例子中,最后result数组中每个值都是10! 因为i是a函数活动对象中的变量,每个内部函数作用域链中都有a的活动对象,所以引用的是同一个i,a函数返回后i=10。

    解决方案:使用立即执行函数

     1 function a() {
     2     var result = [];
     3     for (var i = 0; i < 10; i++) {
     4         result[i] = function (num) {
     5             return function () {
     6                 return num;
     7             }
     8         }
     9         return result;
    10     }
    11 }

      在作用域中声明的变量,可能会被其他人不小心用同名的变量给覆盖掉,作用域链的特性,用匿名函数作为一个容器,容器内部可以访问外部的变量,而外部环境不能访问容器内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,相当于命名空间。

      闭包中使用this对象

        this对象是在运行时基于函数的执行环境绑定的:全局函数,this等于window,而当函数被最为某个对象的方法调用时,this就是那个对象。由于匿名环境具有全局性,它的this通常指向window.

     1 var name='Window';
     2 var  object={
     3     name:'object',
     4     getName:function(){
     5         return function () {
     6             return this.name
     7         }
     8     }
     9 };;
    10 console.log(object.getName()) //Window

      例子中getName方返回一个匿名函数,这个匿名函数返回this.name,调用内部匿名函数的时候,你不函数在搜索this和arguments两个变量时,只会搜索到活动对象为止。

    解决方案:

    var name='Window';
    var  object={
        name:'object',
        getName:function(){
            var that=this;
            return function () {
                return that.name;
            }
        }
    };
    console.log(object.getName());

      闭包函数会导致内存泄露,在垃圾清理的引用计数模式中,如果有闭包函数作用域链中有html元素,则该元素的引用至少一直是1,因此它所占用的内存不会被回收,因此要吧该元素的赋值变量设为null;

        模仿块级作用域:

    (function(){})();//把函数声明转为函数表达式

    立即调用了一个匿名函数

    在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数容易导致命名冲突,通过创建私有作用域解决。

        私有变量

      任何函数中定义的变量,可以认为是私有变量。包括函数的参数,局部变量和函数内部定义跌其它函数。

    访问私有变量方法:1构造函数中创建闭包

      静态私有变量:利用原型。

    重点:模块模式

      对象字面量:实际是单例(只有一个实例的对象)。模块模式就是为单例创建私有变量和特权方法。

      

     1 var singleton=function(){
     2     //私有变量和私有函数
     3     var privateVaribale=10;
     4     function privateFunction(){
     5         return false;
     6     }
     7     return{
     8         publicProperty:true;
     9         publicMethod:funciont(){
    10             privateVaribale++;
    11             return privateFunction();
    12         }
    13     };
    14 }();

      这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数,有一个对象字面量作为函数的值返回,因此它的公有方法有权访问私有变量和函数。从本质上来说,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的。

      模块模式用来设计一个加载器什么的很有用,专门写一篇来分析。

      

      

  • 相关阅读:
    Atitit. visual studio vs2003 vs2005 vs2008  VS2010 vs2012 vs2015新特性 新功能.doc
    Atitit. C#.net clr 2.0  4.0新特性
    Atitit. C#.net clr 2.0  4.0新特性
    Atitit.通过null 参数 反射  动态反推方法调用
    Atitit.通过null 参数 反射  动态反推方法调用
    Atitit..net clr il指令集 以及指令分类  与指令详细说明
    Atitit..net clr il指令集 以及指令分类  与指令详细说明
    Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL
    Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL
    Atitit.跨语言反射api 兼容性提升与增强 java c#。Net  php  js
  • 原文地址:https://www.cnblogs.com/hai233/p/5969876.html
Copyright © 2011-2022 走看看