首页 > 代码库 > JavaScript高级 函数表达式 《JavaScript高级程序设计(第三版)》

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



来自为知笔记(Wiz)



JavaScript高级 函数表达式 《JavaScript高级程序设计(第三版)》