首页 > 编程语言 > 详细

JavaScript - 函数

时间:2015-03-22 15:01:30      阅读:377      评论:0      收藏:0      [点我收藏+]

1. 函数声明提升

    就是在执行代码之前会先读取函数声明,也就表示可以把函数声明放在调用语句后面。

    两种定义函数的方式:

    1)函数声明:

    function functionName(arg0, arg1, arg2) {
        //函数体
    }

    2)函数表达式:

    var functionName = function(arg0, arg1, arg2){
        //函数体
    };

    第二种方式创建了一个匿名函数,并将其赋给变量functionName,因此,使用前如果未赋值就会出错,就像这样:

    sayHi(); //错误:函数还不存在
    var sayHi = function(){
        alert("Hi!");
    };

2. 递归的问题

    function fun1(){
       alert(‘fun1‘);
    }

    var fun2 = fun1;

    fun1 = null;

    fun2(); // "fun1"

    正常来说,这段代码不会有问题,但是递归函数中就会出问题。

function factorial(num){
    if (num <= 1){
        return 1;
    } else {
        return num * factorial(num-1);
    }
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错!

    factorial = null之后不再是函数了,但是原定义中依然使用了factorial(),因此出错。

    arguments.callee是一个指向正在执行函数的指针,因此可修改为:

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);
    }
});

 

3. 作用域链

    当某个函数被调用时,会创建一个执行环境及相应的作用域链,以保证对执行环境有权访问的所有变量和函数的有序访问。

    然后,使用arguments和其他命名参数的值来初始化函数的活动兑现。

    作用域链的前端,是当前执行代码所在环境的变量对象,作用域链的下一个变量对象来自外部环境,如此一直延续到全局执行环境。

    例:

function compare(value1, value2){
    if (value1 < value2){
        return -1;
    } else if (value1 > value2){
        return 1;
    } else {
        return 0;
    }
}
var result = compare(5, 10);

    上面在全局作用域中调用了compare()函数,调用compare()时,会创建一个包含arguments、value1和value2的活动对象。全局执行环境的变量对象(包含result和compare)在compare()执行环境的作用域中处于第二位,如下:

    技术分享

    后台的每个执行环境都有一个变量对象(表示变量的对象)。全局环境的变量对象始终存在,而compare()函数这样的局部环境的变量对象,则只存在于函数执行的过程中。

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

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

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

    此例中compare()的执行环境的作用域链中包含两个变量对象:本地活动对象和全局变量对象。

    作用域链本质上是一个指向变量对象的指针列表,只引用但不实际包含变量对象。

    无论什么时候在函数中访问一个变量,就会从作用域链中搜索具有相应名字的变量。一般来讲,函数执行完毕后,局部活动对象就会被销毁,内存中仅包含全局执行环境的变量对象。但,闭包属于二般情况。

 

4. 闭包

    闭包就是有权访问另一个函数作用域中的变量的函数,是个函数。

    一般创建闭包,就是在一个函数内部创建另一个函数。这样:

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;
        }
    };
}

    上面在createComparisonFunction()内部创建了一个匿名函数,匿名函数内部访问了外部函数中的变量propertyName(var value1 = object1[propertyName];)。该匿名函数被返回后无论在哪儿被调用都可以访问变量 propertyName。

    下面代码执行时作用域链是这样的:

    var compare = createComparisonFunction("name");
    var result = compare({ name: "Nicholas" }, { name: "Greg" });

    技术分享

    在createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁,但其活动对象会留在内存中不被销毁,因为匿名函数的作用域链仍在引用这个活动对象。直到匿名函数被销毁后,createComparisonFunction()的活动对象才被销毁,例如:

    //创建函数
    var compareNames = createComparisonFunction("name");
    //调用函数
    var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
    //解除对匿名函数的引用(以便释放内存),就等于通知垃圾回收例程将其清除
    compareNames = null;

4.1 闭包与变量

    闭包保存的是整个变量对象,因此闭包只能取得包含函数中任何变量的最后一个值。

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

    执行createFunctions()后返回的函数数组,每个的执行结果都是10。因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的是同一个变量i。当createFunctions()函数返回后,i = 10,所以每个函数内部i的值也都是10。

    若要每个函数返回自己的索引,可以这么做:

function createFunctions(){
    var result = new Array();
    for (var i=0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    return result;
}

    上面对于数组每项都立即执行了一个匿名函数,在调用每个匿名函数时,传入了变量i,由于参数按值传递,因此变量i的当前值会复制给参数num。而匿名函数内部,又返回了一个访问num的闭包,而result数组中每个函数都有自己num变量的一个副本,因此就可以返回各自索引了。

4.2 关于this对象

    匿名函数的执行环境具有全局性,因此其this对象通常指向window。

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)

    几种特殊的情况:

var name = "The Window";
var object = {
    name : "My Object",
    getName: function(){
        return this.name;
    }
};
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下

5. 模仿块级作用域

    js并没有块级作用域的概念,像这样:

function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
    alert(i); //计数
}

    模仿块级作用域的匿名函数的语法如下:

    (function(){
         //这里是块级作用域
    })();

    上面其实是一个函数表达式,函数表达式的后面可以跟圆括号。而函数声明的后面不可以跟圆括号,这样:

    function(){
        //这里是块级作用域
    }(); //出错!

    因此,让上面的变量i私有可以这样做:

function outputNumbers(count){
    (function () {
        for (var i=0; i < count; i++){
            alert(i);
        }    
    })();
    alert(i); //导致一个错误!
}

    这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

JavaScript - 函数

原文:http://www.cnblogs.com/sduzhangxin/p/4356013.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!