JS函数声明、 函数表达式与立即执行函数的理解与执行顺序

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/aerchi/article/details/79805301

定义函数的方法

定义函数的方法主要有三种:

  1. 函数声明(Function Declaration)
  2. 函数表达式Function Expression)
  3. new Function构造函数

其中,经常使用的是函数声明和函数表达式的函数定义方法,这两种方法有着很微妙的区别和联系,而且这两种方法的使用也容易混淆,所以这篇文章主要总结下这两种函数定义方法的相关知识点,当然本文的主题依然是关于函数提前的。

函数声明的典型格式:

function functionName(arg1, arg2, ...){
    <!-- function body -->
}

函数表达式

  • 函数表达式的典型格式:

    var  variable=function(arg1, arg2, ...){
                <!-- function body -->
    }
  • 包含名称(括弧,函数名)的函数表达式:

    var  variable=function functionName(arg1, arg2, ...){
            <!-- function body -->
    }

    像上面的带有名称的函数表达式可以用来递归:

    var  variable=function functionName(x){
            if(x<=1)
                return 1;
            else
                return x*functionName(x);
    }

声明提前

声明提前是函数声明和函数表达式的一个重要区别,对于我们进一步理解这两种函数定义方法有着重要的意义。

变量在声明它们的脚本或函数中都是有定义的,变量声明语句会被提前到脚本或函数的顶部。但是,变量初始化的操作还是在原来var语句的位置执行,在声明语句之前变量的值是undefined。

上面的结论中可以总结出三个简单的点:

  1. 变量声明会提前到函数的顶部;
  2. 只是声明被提前,初始化不提前,初始化还在原来初始化的位置进行初始化;
  3. 在声明之前变量的值是undefined。

看一个例子。
var aerchi='aerchi';
function aerchiToShow(){
	debugger;
    console.log(aerchi); //aerchi
}
aerchiToShow();
17:04:18.000 VM1744:4 aerchi
上面代码正确的输出结果是:

输出aerchi
再看下面的例子,

var aerchi='aerchi';
function aerchiToShow(){
	debugger;
    console.log(aerchi); // undefined
    var aerchi='aerliho';
    console.log(aerchi); //aerliho
}
aerchiToShow();
// undefined
// aerliho
上面代码正确的输出结果是:
先输出undefined,然后输出aerliho

注:

在声明之前变量的值是undefined,只是声明被提前,初始化并未提前,初始化还在原来初始化的位置进行初始化.


函数声明

   函数声明创建将来代码调用的函数。函数可以在声明之前的位置被调用。示例如下:   

//可以在声明之前的位置被调用  
var size=show("call before function");    
function show(str){  
   console.log(str);  
};  
//可以在声明之后的位置被调用  
var size2=show("call after function");  


函数表达式

  将函数放在本该表达式待的位置,这称为函数表达式。在函数表达式中,经常使用匿名函数。示例如下:     

//会报错,变量aa 还未保存对函数的引用,函数调用必须在函数表达式之后 
var aa = showM("call before function");    
var showM = function(str){  
   console.log(str);  
};
var showM = function(str){  
   console.log(str);  
};  
//只能在函数表达式定义之后被调用
var bb = showM("call after function");  


函数表达式相当于是
是把函数对象赋值给一个变量

 对于函数表达式,变量赋值是不会提前的,即function(arg1, arg2, ...){<!-- function body -->}是不会提前的,所以函数定义并没有被执行,所以函数表达式不能像函数声明那样进行函数声明提前。


立即执行函数(IIFE)

   没有名称,在解释器经过它们时执行一次。示例如下:   

var showM = (function(str){  
   console.log(str);  
})("call me from function");
16:38:45.542 VM1255:2 call me from function
16:38:45.556 undefined
16:39:30.972 (function(str){  
   console.log(str);  
}("call me from function 2"));
16:39:30.971 VM1257:2 call me from function 2
16:39:30.994 undefined
16:39:44.131 !function(str){  
   console.log(str);  
}("call me from function 2");
16:39:44.131 VM1259:2 call me from function 2
16:39:44.145 true


            //情况1  
            //结果会被输出  
            var fn=function(){  
                console.log("函数表达式赋值给一个变量");  
            }();  

            //情况2  
            //结果不会被输出,JavaScript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用  
            function fn(){  
                console.log("函数声明");  
            }();  

            //情况3  
            //语法错误,匿名函数属于函数表达式,未执行赋值操作,不能被调用  
            function(){  
                console.log("函数表达式");  
            }();  

经典的立即执行函数样式

 (function(a){  
                console.log(a);  
            })(123);  
            (function(a){  
                console.log(a);  
            }(1234));  
            !function(a){  
                console.log(a);  
            }(12345);  
            +function(a){  
                console.log(a);  
            }(123456);  
            -function(a){  
                console.log(a);  
            }(1234567);  
注意:

只有函数表达式才能实现立即执行,匿名函数也是函数表达式为何不能立即执行呢,因为匿名函数开始的function会被JavaScript引擎识别为函数声明的开始,所以加上括号也不会被执行,而加上(),!,+,-等符号可以执行,是因为加上这些符号就可以告诉JavaScript引擎这不是函数声明了。


函数声明、 函数表达式的执行顺序

函数声明:

1function 函数名称 (参数:可选){ 函数体 }

函数表达式:

1function 函数名称(可选)(参数:可选){ 函数体 }

function foo(){} // 声明,因为它是程序的一部分
var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分

new function bar(){}; // 表达式,因为它是new表达式

(function(){
	function bar(){} // 声明,因为它是函数体的一部分
})();
function foo(){} // 函数声明
(function foo(){}); // 函数表达式:包含在分组操作符内
  
try {
	(var x = 5); // 分组操作符,只能包含表达式而不能包含语句:这里的var就是语句
} catch(err) {
	// SyntaxError
}

来看一个函数声明、 函数表达式的执行顺序的例子

var getName = function () { 
    console.log('var getName 1');
};
function getName() { 
    console.log('function getName 1');
}
var getName = function () { 
    console.log('var getName 2'); //此函数表达式会被执行
};
function getName() { 
    console.log('function getName 2');
}
getName();

上述代码的执行结果是:var getName 2。原因是这样的,var声明的变量和函数声明function都会被提升,但是函数声明的提升的级别是比var要高的.

另一个示例:

var currying = function(fn) {
    // fn 指官员消化老婆的手段
    var args = [].slice.call(arguments, 1);
    // args 指的是那个合法老婆
    return function() {
        // 已经有的老婆和新搞定的老婆们合成一体,方便控制
        var newArgs = args.concat([].slice.call(arguments));
        // 这些老婆们用 fn 这个手段消化利用,完成韦小宝前辈的壮举并返回
        return fn.apply(null, newArgs);
    };
};

// 下为官员如何搞定7个老婆的测试
// 获得合法老婆
var getWife = currying(function() {
    var allWife = [].slice.call(arguments);
    // allwife 就是所有的老婆的,包括暗渡陈仓进来的老婆
    console.log(allWife.join(";"));
}, "合法老婆");

// 获得其他6个老婆
getWife("大老婆","小老婆","俏老婆","刁蛮老婆","乖老婆","送上门老婆");
//合法老婆;大老婆;小老婆;俏老婆;刁蛮老婆;乖老婆;送上门老婆

// 换一批老婆
getWife("超越韦小宝的老婆");
//合法老婆;超越韦小宝的老婆

本文地址: https://blog.csdn.net/aerchi/article/details/79805301


展开阅读全文

没有更多推荐了,返回首页