欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

js 函数表达式

程序员文章站 2022-03-18 19:15:27
...

一、函数表达式 和 函数声明 的区别

  • 函数的创建有两种方式:函数声明 和 函数表达式。
  • 函数声明具有函数声明提升,函数表达式不具有函数声明提升。 

(1)、函数声明

函数声明有一个重要的特点就是 函数声明提升——在执行代码之前会先读取函数声明,这就意味着可以把函数声明放在调用它的语句之后。

fn();                     // hello
function fn(){
    alert("hello");
}

(2)、函数表达式

函数表达式就是常规的变量赋值——创建一个函数(一般为匿名函数,当然也有命名函数),并将它赋值给一个变量,使用前必须先赋值,不存在函数声明提升。

// sayHello();                      // error

var sayHello = function(){
    alert("hello");
};

sayHello();                         // hello

 

二、递归

递归函数,是在一个函数内部,通过名字调用自身的情况下形成的

举个栗子:这是一个经典的递归实现阶乘的函数

function factorial(num){
    if(num <= 1){
        return 1;
    } else {
        return num * factorial(num - 1);
    }
}

递归有时会面临一个问题:如上代码,在函数有名字,而且名字以后也不会变得情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 仅仅耦合在了一起。为了消除这种紧密的耦合现象,就可以用 argument.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 * factorial(num - 1);
    }
});

上述代码,创建了一个名为 f() 的命名函数表达式,然后将她赋值给变量 factorial。即便把函数赋值给了另一个变量,函数的名字 f 仍然有效,所以递归调用照样能正确完成。

 

三、闭包

1、函数被调用时都发生了什么?

当某个函数被调用时,会创建一个执行环境及其相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,……直至作为作用域链终点的全局执行环境。在函数执行的过程中,为了读取和写入变量的值,就需要在作用域链中查找变量。

后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,而像函数中的局部环境的变量对象,则只能在函数执行的过程中存在。

function compare(value1, value2){
    if(value1 < value2){
        return -1;
    } else if(value1 > value2){
        return 1;
    } else {
        return 0;
    }
}

var result = compare(5, 10);

js 函数表达式

 一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局执行环境的变量对象。但是,闭包的情况例外。

2、闭包

  • 闭包是指有权访问另一个函数作用域中的变量的函数。
  • 创建闭包的常见方式,就是在一个函数内部创建另一个函数。

在一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域中。

例如:

function createComparisionFunction(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 compare = createComparisionFunction("name");
var result = compare({ name: "marry" }, { name: "lily" });
console.log(result);                                                // 1

上述代码,在匿名函数从  createComparisionFunction() 函数中被返回后,它的作用域链被初始化为包含 createComparisionFunction() 函数的活动对象 和 全局变量对象。这样,匿名函数就能访问在 createComparisionFunction() 函数中定义的所有变量了。

更为重要的是,在 createComparisionFunction() 函数执行完毕后,其执行环境的作用域链会被销毁,但它的活动对象仍会留在内存中,知道匿名函数被手动销毁后,createComparisionFunction() 函数的活动对象才会被销毁。

function createComparisionFunction(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 = createComparisionFunction("name");

// 调用函数
var result = compare({ name: "marry" }, { name: "lily" });

// 解除匿名函数的引用(以便释放内存)
compareNames = null;

 

3、闭包与变量

4、闭包与 this 对象

5、闭包与内存泄漏

 

四、模仿块级作用域

 

五、私有变量

1、静态私有变量

2、模块模式

3、增强的模块模式

 

相关标签: JavaScript