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来递归自身,以防止函数名变化。
     
    当在函数内部定义其他函数时就创建了闭包。闭包有权访问 包含函数 的 内部的所有变量,原理:
    在后台执行环境中,闭包的作用域链包含它自身的作用域、包含函数的作用域、全局作用域。
    通常,函数的作用域及其变量都会在函数执行结束了被销毁。
    但是,当函数返回一个闭包时,这个函数的作用域会在内存中一直保存到闭包不存在为止。
     
    使用闭包可以在对象中创建私有变量:
    可以使用构造函数模式、原型模式、来实现自定义类型的特权方法;也可以使用模块模式、增强模式的模块模式来实现单利的特权方法。
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     





  • 相关阅读:
    Linnia学习记录
    漫漫考研路
    ENS的学习记录
    KnockoutJS 3.X API 第四章 数据绑定(4) 控制流with绑定
    KnockoutJS 3.X API 第四章 数据绑定(3) 控制流if绑定和ifnot绑定
    KnockoutJS 3.X API 第四章 数据绑定(2) 控制流foreach绑定
    KnockoutJS 3.X API 第四章 数据绑定(1) 文本及样式绑定
    KnockoutJS 3.X API 第三章 计算监控属性(5) 参考手册
    KnockoutJS 3.X API 第三章 计算监控属性(4)Pure computed observables
    KnockoutJS 3.X API 第三章 计算监控属性(3) KO如何实现依赖追踪
  • 原文地址:https://www.cnblogs.com/jarl/p/5962617.html
Copyright © 2011-2022 走看看