【js高程第7章】 — 函数表达式

一、递归

function factorial(num){

    if(num <= 1){

        return 1;

    }else{

        return num * factorial(num-1);

    }

}

【缺陷】

var anotherFactorial = factorial;

factorial = null;

alert(anotherFactorial(4)); //出错!

anotherFactorial函数内部调用了factorial,此时factorial=null,造成错误。

【改进】

function factorial(num){   

    if(num <= 1){       

        return 1;    

    }else{       

        return num * arguments.callee(num-1);  

  }

arguments.callee是一个指向正在执行的函数的指针。严格模式不可使用。

【严格模式下】

var factorial = ( function f(num){

            if(num <= 1){       

                return 1;    

            }else{       

                return num * f(num-1);    

            }

    });


二、闭包

1.基础

定义:有权访问另一个函数作用域中的变量的函数。

【关于作用域链】

每个执行环境都有一个表示变量的对象——变量对象。

全局环境的变量对象始终存在。

创建函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链,保存在内部的[[Scope]]属性中。

当,调用函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象,构建起执行环境的作用域链。

此后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。

作用域链通常包括几个变量对象:各个函数的活动对象全局变量对象

本质:一个指向变量对象的指针列表。

eg:

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: "Nicholas" }, { name: "Greg" });

//解除对匿名函数的引用(以便释放内存)

compareNames = null;


【js高程第7章】 — 函数表达式_第1张图片
作用域链图示

【注意】

当 createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中。

【闭包使用建议】

由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。

建议只在绝对必要时,再考虑使用闭包。


2.闭包使用要注意的问题

(1)for循环的问题

作用域链机制的副作用:闭包只能取得包含函数中任何变量的最后一个值。

function createFunctions(){

    var result = new Array();

    for(var i = 0; i < 10; i++){

        result[i] = function(){

            return i;

        };

    }

    return result;

}

【结果】

var result = createFunctions();

result = [f,f,f,f,...,f]; //一个包含10个函数对象的数组,每个函数都是function(){ return i; }

【解析】

当createFunctions执行完毕,返回result时,createFunctions作用域中的变量i = 10。

由于闭包中的变量i来自外层函数createFunctions,所以,当遍历result数组,执行每个函数,返回的都是10。

【造成错误的原因】

数组中的10个函数的1级作用域,指向了同一个活动对象(createFunctions的活动对象)。

【改进,期望每个函数返回自己对应的索引值】

function createFunctions(){

    var result = new Array();

    for(var i = 0; i < 10; i++){    

        result[i] = function(num){

            return function(){

                return num;

            }

        }(i);

    }

}

【结果】

result = [f,f,f,f...,f]; //一个包含10个函数对象的数组,每个函数都是function(){ return num; }

【解析】

执行result中的每个函数,发现当前0级作用域不存在num变量,于是去1级作用域找。

每个函数的1级作用域,指向10个不同的活动对象,它们的num值分别为0 — 9(在for循环的时候被赋值)。

因此,最终result数组中每个函数的返回值分别为0 — 9。

【注意】

函数的参数变量,相当于在其作用域中声明的变量。

即:num相当于在外层匿名函数中,被var num = i;

(2)关于this

匿名函数中的this指向window。

(3)内存泄漏(闭包造成)

内存泄漏的概念(个人理解):使用过的变量在该被回收的时候,没有被回收,占用了本该被释放的内存,就叫内存泄漏。

function assignHandler(){

    var element = document.getElementById("someElement");

    element.onclick = function(){

        alert(element.id);

    };

}

闭包保存了对assignHandler活动对象的引用,造成DOM元素element无法销毁。

解决

function assignHandler(){

    var element = document.getElementById("someElement");

    var id = element.id;

    element.onclick = function(){

        alert(id);

    };

    element = null;

}

【注意】

闭包会引用包含函数的整个活动对象,所以,引用了变量id,assignHandler的活动对象就不会被销毁。防止内存泄漏,要手动element = null,解除对DOM对象的引用,回收其占用的内存。


三、私有变量

【创建私有变量的两种方式】

1.类似构造函数模式的私有变量使用方法

function MyObject(){

    //私有变量和私有函数

    var privateVariable = 10;

    function privateFunction(){

        return false;

    }

    //特权方法

    this.publicMethod = function (){

        privateVariable++;

        return privateFunction();

    };

}

MyObject的实例,只能通过publicMethod访问其内部私有变量。

function Person(name){

    this.getName = function(){

        return name;

    };

    this.setName = function (value) {

        name = value;

    };

}

var person = new Person("Nicholas");

alert(person.getName()); //"Nicholas"

person.setName("Greg");

alert(person.getName()); //"Greg"

【缺点】

同构造函数模式,每个实例都会创建同样一组新方法,缺少复用性。

2.静态私有变量(类似于原型模式的私有变量使用方法)

(function(){

    //私有变量和私有函数

    var privateVariable = 10;

    function privateFunction(){

        return false;

    }

    //构造函数(全局变量)

    MyObject = function(){

    };

    //公有/特权方法

    MyObject.prototype.publicMethod = function(){

        privateVariable++;

        return privateFunction();

    };

})();

MyObject的实例可以通过原型方法publicMethod访问匿名函数的私有变量。

(function(){

    var name = "";

    Person = function(value){

        name = value;

    };

    Person.prototype.getName = function(){

        return name;

    };

    Person.prototype.setName = function (value){

        name = value;

    };

})();

var person1 = new Person("Nicholas");

alert(person1.getName()); //"Nicholas"

person1.setName("Greg");

alert(person1.getName()); //"Greg"

var person2 = new Person("Michael");

alert(person1.getName()); //"Michael" 【缺点】

alert(person2.getName()); //"Michael"

【缺点】

私有变量name,是静态的、由所有实例共享的属性。

因为所有实例的setName、getName方法,都有相同的1级作用域,即声明name的那个匿名函数。


【模块模式】

1.单例对象

var singleton = {

    name : value,

    method : function () {

        //这里是方法的代码

    }

};

2.模块模式通过为单例添加私有变量和特权方法能够使其得到增强

var singleton = function(){

    //私有变量和私有函数

    var privateVariable = 10;

    function privateFunction(){

        return false;

    }

    //特权/公有方法和属性

    return {

        publicProperty: true,

        publicMethod : function(){        

            privateVariable++;

            return privateFunction();

        }    

    };

}();

【适用场景】

需要对单例进行某些初始化,同时又需要维护其私有变量。


【增强的模块模式】

var singleton = function(){

    //私有变量和私有函数

    var privateVariable = 10;

    function privateFunction(){

        return false;

    }

    //创建对象

    var object = new CustomType();

    //添加特权/公有属性和方法

    object.publicProperty = true;

    object.publicMethod = function(){

        privateVariable++;

        return privateFunction();

    };

    //返回这个对象

    return object;

}();

【适用场景】

单例必须是某种类型的实例,同时还需要添加一些属性、方法对其加以增强的情况。

你可能感兴趣的:(【js高程第7章】 — 函数表达式)