zoukankan      html  css  js  c++  java
  • JavaScript高级 函数表达式 《JavaScript高级程序设计(第三版)》

    • 函数表达式的特征
    • 使用函数实现递归
    • 使用闭包定义私有变量
    前面我们说到定义函数有两种方式:函数声明、函数表达式。
    两者的区别在于函数声明提升,前者在执行之前的上下文环境中直接被赋值,而后者不会。
    一、递归
    递归函数是一个函数通过名字调用自身的情况下构成的。
         function factorial(num){
             if(num<1){
                 return 1;
             }else{
                 return num * arguments.callee(num-1);
             }
         }
         alert(factorial(10));  
    二、闭包
    闭包的核心概念就是指有权访问另一个函数作用域中变量的函数。
    创建闭包的常见方式主要就是在一个函数内部创建另一个函数。
         function createCompareFunction(proprtyName){
             return function(obj1,obj2){
                 if(obj1[proprtyName]>obj2[proprtyName]){
                     return 1;    
                 }else if(obj1[proprtyName]<obj2[proprtyName]){
                     return -1;
                 }else{
                     return 0;
                 }
             }
         }
         var o1 = {name : 'zjh'};
         var o2 = {name : 'azz'};
         var f = createCompareFunction('name');
         alert(f(o2,o1)); //-1  
    内部匿名函数中调用的proprtyName就是另一个函数作用域中的变量。当f被赋值后,外层函数的作用域链被销毁,但是他的活动对象仍然在内存中,因为它的活动对象被返回的匿名函数引用了。
    所以闭包会占用很多内存,要在必要的时候使用。
    2.1闭包与变量
    作用域链的这种机制也会有一定的副作用:
         function createFunction(){
             var array = new Array();
             for(var i = 0 ; i < 10 ; i++){
                 array[i] = function(){
                     return i;
                 }
             }
              i = 20;
             return array;
         }
         var a = createFunction();
         alert(a[6]())//20  
    应该是6,这是却是20。原因就是数组中的每一项都是function(){return i},而此时的i的值是createFunction执行完的20。
         function createFunction(){
             var array = new Array();
             for(var i = 0 ; i < 10 ; i++){
                 array[i] = function(num){
                     return num;
                 }(i)
             }
              i = 20;
             return array;
         }
         var a = createFunction();
         alert(a[6])//20  
    利用了函数参数按值传递的特性。
    2.2关于this对象
    this对象时在运行时基于函数执行环境绑定的。在全局函数中,this等于window,而当函数作为某个对象的方法调用时,this等该对象。
    匿名函数执行环境具有全局性。因此this通常指向window。
         var k = 'window';
         var o = {
             k : 'object',
             getName : function(){
                 return function(){
                     return this.k;
                 }
             }
         }
         alert(o.getName()())  
    2.3内存泄漏
    在IE浏览器中,因为dom对象和js对象存在浏览器的不同地方,如果闭包的作用域链中保存着一个HTML元素,那么该元素就无法被销毁。
    function createF(){
        var ele = document.getElementById('aa');
        ele.onclick = function(){
            alert(ele.value);
        }
    }  
    避免泄漏:
    function createF(){
        var ele = document.getElementById('aa');
        var k = ele.value;
        ele.onclick = function(){
            alert(k);
        }
        ele = null;
    }  
    三、模仿块级作用域
    JS中没有块级作用域,如果要使用需要在函数中定义来模仿。
    (function(){
        for(var i =0 ; i<10 ; i++){
            var k = 10;
        }
    })()
    alert(i) //错误  
    因为i在块级作用域执行完后 就销毁了。
    只要临时需要一些变量,可以使用私有作用域。这种技术经常用在全局作用域中的外部函数,可以限制向全局作用域中添加过多的变量和函数。
    也能有效的减少闭包占用内存的问题。因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域了。
    四、私有变量
    JS中没有私有成员的概念,所以属性都是公有的,但是有一个私有概念,那就是在任何函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。
    这些私有变量包括:函数的参数,局部变量,函数内部定义的其它函数。
    function add(num1,num2){
        var a = '1500';
    }  
    在这个函数中,有三个私有变量,num1,num2,a。我们可以在函数内部访问他们,或者通过函数内部的闭包通过作用域来访问他们。那么我们利用这一点就可以创建用于访问私有变量的特权方法
    有两种方式可以创建特权方法:
    4.1私有变量
    第一种:利用闭包
    function Person(name){
        this.setName = function(value){
            name = value;
        }
        this.getName = function(){
            return name;
        }
    }
    var p = new Person();
    p.setName('zjh');
    alert(p.getName());  
    所以,利用私有成员和特权成员可以隐藏那些不该被直接修改的数据。
    缺点:是必须使用构造函数模式来达到这个目的。前面提到过,构造函数模式的缺点是:每次实例化一个对象,都会创建一组同样的新方法。
    4.2静态私有变量
    在私有作用域中定义私有变量或者函数,也可以创建特权方法。
        var privateVariable = 10;
        function privateFuntion(){
            return false;
        }
        MyObject = function(){};
        MyObject.prototype.publicMethod = function(){
            privateVariable++;
            return privateFuntion();
        }
        var o = new  MyObject();
        alert(o.publicMethod());  
    这个模式在定义构造函数时没有使用函数声明,而是使用了函数表达式。函数表达式只能创建局部函数,所以我们不使用关键字var 来定义了MyObject ,未经过初始化声明的变量总是会创建一个全局变量。(在严格模式下会错误)
    这个模式与在构造函数中定义特权方法的区别在于:私有变量和函数是→实例共享的,由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法作为一个闭包,总是保存着对包含作用域的引用。
    (function(){
        var name = '';
        Person = function(value){
            name = value;
        }
        Person.prototype.setName = function(value){
            name = value;
        }
        Person.prototype.getName = function(){
            return name;
        }
    })()
    var p = new Person('zjh');
    alert(p.getName());//zjh
    var pp = new Person('zzz');
    alert(pp.getName());//zzz
    alert(p.getName());//zzz  
    可见这种方式创建的是静态私有变量。
    4.3模块模式
    之前的模式是为自定义类型添加私有变量。而这种模块模式是为单例模式的对象添加私有属性。
    方法如下:
    var singleton = function(){
        var privateVar = 10;
        function privateFun(){
            return false;
        }
        return {
            publicVar : true,
            publicMethod : function(){
                privateVar++;
                return privateFun();
            }
        }
    }()
    alert(singleton.publicMethod());  
    如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以用到这种模块方式。
    4.3增强的模块模式
    function Person(){}
    var singleton = function(){
        var privateVar = 10;
        function privateFun(){
            return false;
        }
        var o = new Person();
        o.publicVar = true;
        o.publicMethod = function(){
            privateVar++;
            return privateFun();
        }    
        return o;
    }()
    alert(singleton.publicMethod());  
    这种模式 在匿名函数中创建了一个指定类型的对象,用来返回。
    五、小结
    在JS中,函数表达式是一种非常有用的技术。使用函数表达式无须命名,从而实现动态编程。
    特点:
    函数表达式不一定有名字,没有名字的函数表达式叫做匿名函数。
    在无法确定如何引用函数的情况下,递归函数变得比较复杂。
    递归函数中应该用arguments.callee来递归自身,以防止函数名变化。
     
    当在函数内部定义其他函数时就创建了闭包。闭包有权访问 包含函数 的 内部的所有变量,原理:
    在后台执行环境中,闭包的作用域链包含它自身的作用域、包含函数的作用域、全局作用域。
    通常,函数的作用域及其变量都会在函数执行结束了被销毁。
    但是,当函数返回一个闭包时,这个函数的作用域会在内存中一直保存到闭包不存在为止。
     
    使用闭包可以在对象中创建私有变量:
    可以使用构造函数模式、原型模式、来实现自定义类型的特权方法;也可以使用模块模式、增强模式的模块模式来实现单利的特权方法。
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     





  • 相关阅读:
    算法笔记4
    算法笔记3
    SQLServer 两种分页方式
    Git 使用笔记
    Docker MongoDB 笔记
    Abp vNext 修改Identity模块用户管理界面的坑
    CentOS安装MariaDB
    Visio修改页边距
    在 PowerDesigner 导入Excel中的表结构
    Uploadify上传失败,提示 2156 SecurityError Error #2156 null的解决办法
  • 原文地址:https://www.cnblogs.com/jarl/p/5962617.html
Copyright © 2011-2022 走看看