函数表达式

1.定义函数的方式

    函数声明和函数表达式 

    函数声明

/**
 * 函数声明(重要特征:函数声明提升)
 */
demo('函数声明');  //由于函数声明提升 不会报错
function demo(str) {
    console.log(str);
}

    函数表达式

/**
     * 函数表达式
     * @param str
     */
//    demo('函数表达式');  //报错
    var demo = function (str) {
        console.log(str);
    }
    demo('函数表达式');

        两者的区别:

            (1)函数声明有函数声明提升,无论在将函数声明放在调用前还是调用后,都不会报错;

            (2) 函数表达式没有函数声明提升,必须赋值后才能调用,否则会报错

 /**
     * 由于函数声明提升,导致函数表达式的函数把函数声明的函数所覆盖
     */
    demo();  //我是函数声明
    var demo = function () {
        console.log('我是函数表达式');
    };
    function demo() {
        console.log('我是函数声明');
    }
//    var demo = function () {
//        console.log('我是函数表达式');
//    };
    demo();  //我是函数表达式

        两种函数的调用,函数表达式必须在函数表达式完整后才能调用,异步的在函数表达式之前调用,虽然在之前调用,但是还是表达式完整之后调用的 。

console.log(fun1+'-----');
    console.log(fun2+'-----');  // 不好报错,var声明的由变量提升,这里访问只是没有值而已   打印  undefined-----
//    console.log(fun3+'------');  // es6 变量声明有块级作用域,不存在变量提升, 所以 fun3为没有 not defined, 这里就会报错
    console.log('--------------');
    fun1();
//    fun2();  // fun2由于var声明的变量提升,默认值为undefined,不是function
//    fun3();  //fun3  not defined
//    fun4(fun2,fun3);   //也不行
    console.log('--------同步的情况下,在fun1中调用fun2fun3----------');
    function fun1() {
        console.log('我是函数声明,同步的情况下,在fun1中调用fun2fun3');
//        fun2();  // fun2由于var声明的变量提升,默认值为undefined,不是function
//        fun3();  //fun3  not defined
        console.log('----------------------');
    }
    function fun4(f1,f2) {
        console.log('我是函数声明,传参的情况下,在fun1中调用fun2fun3');
        f1();
        f2();
    }
    setTimeout(function () {
        console.log('---------异步的情况下在fun4中调用 fun2,fun3---------------');
        fun3();
        fun2();
        fun5();
    });

    var fun2 = function () {
        console.log('我是函数表达式');
    };

    let fun3 = function () {
        console.log('我是es6声明函数表达式');
    };

    let fun5 = ()=>{
        console.log('我是箭头函数');
    };

    console.log('--------在函数表达式之后调用------------');
    fun1();
    fun2();
    fun3();


2. 递归

    (1)递归函数是在一个函数通过名字调用自身的情况下构成的。

/**
 * 递归
 */
function demo(num) {
    if(num <= 1){
        return 1;
    }else {
        return num*demo(num-1);
    }
}

function demo(num) {
    if(num <= 1){
        return 1;
    }else {
        return num*demo(num-1);
    }
}
var anotherDemo = demo;
demo = null;
console.log(anotherDemo(5));  //出错

函数表达式

    (2)在非严格模式下,可以用arguments.callee来实现函数的递归调用,但在严格模式下由于不能通过脚本来访问arguments.callee则会导致错误。

            arguments.callee是一个指向正在执行的函数的指针

/**
 * 递归---arguments.callee实现
 */
function demo(num) {
    if(num <= 1){
        return 1;
    }else {
        return num*arguments.callee(num-1);
    }
}
console.log(demo(5));
/**
 * 递归---arguments.callee实现,在严格模式下会报错
 */
function demo(num) {
    'use strict';
    if(num <= 1){
        return 1;
    }else {
        return num*arguments.callee(num-1);
    }
}
console.log(demo(5));

3.闭包

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

/**
 * 闭包
 * @param property
 * @returns {Function}
 */
function demo(property) {
    return function (obj_1,obj_2) {
        var v_1 = obj_1[property];
        var v_2 = obj_2[property];
        return v_1+'---'+v_2;
    }
}
var com = demo('name');
console.log(com({name : 'zhangsan'},{name : 'lisi'}));  //zhangsan---lisi
    3.1 闭包与变量

        闭包只能取得包含函数中任何变量的最后一个值。

/**
 * 闭包只能取得包含函数中任何变量的最后一个值
 * @returns {Array}
 */
function demo() {
    var result = [];
    for(var i = 0;i < 10;i++){
        result[i] = function () {
            return i;
        }
    }
    return result;
}
var arr = demo();
for(var j = 0;j<arr.length;j++){
    console.log(arr[j]());  //全是10
}
/**
 * 运用匿名函数强制让闭包的行为表现为:数组的每个函数都有一个自己的num副本
 * @returns {Array}
 */
function demo() {
    var result = [];
    for(var i = 0;i < 10;i++){
        result[i] = (function (num) {  //为每一个函创建一个自己的num副本
            return function () {
                return num;
            };
        })(i);
    }
    return result;
}
var arr = demo();
for(var j = 0;j<arr.length;j++){
    console.log(arr[j]());  // 0-9
}
    3.2 内存泄漏

        闭包会导致访问的变量永远存在内存中,不能自行回收,所以会导致内粗泄漏。


4. 模仿块级作用域域

    js没有块级作用域的概念。js也从来不对多次声明的同一个变量做出反应,只是会执行后面的初始化的值。

function demo(name) {
    for(var i = 0;i<5;i++){
        console.log(i);  // 0-4
    }
    console.log('--------------');
    for(var i ;i<10;i++){
        console.log(i);  // 5-9
    }
    console.log('--------------');
    for(var i =0;i<5;i++){
        console.log(i);  // 0-4
    }
}
demo();

    解决方法:模仿模块作用域 -------私有作用域的匿名函数

    语法:

       (function(){

        })()

function demo(name) {
    (function () {
        for(var i = 0;i<5;i++){
            console.log(i);  // 0-4
        }
    })();
    console.log('--------------');
    (function () {
        for(var i ;i<10;i++){
            console.log(i);  // 没有输出
        }
    })();
    console.log('--------------');
    (function () {
        for(var i =0;i<5;i++){
            console.log(i);  // 0-4
        }
    })();
    console.log(i);  //出错   i is not defined
}
demo();