定义函数的方法主要有三种:
其中,经常使用的是函数声明和函数表达式的函数定义方法,这两种方法有着很微妙的区别和联系,而且这两种方法的使用也容易混淆,所以这篇文章主要总结下这两种函数定义方法的相关知识点,当然本文的主题依然是关于函数提前的。
function functionName(arg1, arg2, ...){
}
函数表达式的典型格式:
var variable=function(arg1, arg2, ...){
}
包含名称(括弧,函数名)的函数表达式:
var variable=function functionName(arg1, arg2, ...){
}
像上面的带有名称的函数表达式可以用来递归:
var variable=function functionName(x){
if(x<=1)
return 1;
else
return x*functionName(x);
}
声明提前是函数声明和函数表达式的一个重要区别,对于我们进一步理解这两种函数定义方法有着重要的意义。
变量在声明它们的脚本或函数中都是有定义的,变量声明语句会被提前到脚本或函数的顶部。但是,变量初始化的操作还是在原来var语句的位置执行,在声明语句之前变量的值是undefined。
上面的结论中可以总结出三个简单的点:
看一个例子。
var aerchi='aerchi';
function aerchiToShow(){
debugger;
console.log(aerchi); //aerchi
}
aerchiToShow();// 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);
};
//VM3235:1 Uncaught TypeError: showM is not a function
at :1:10
var showM = function(str){
console.log(str);
};
//只能在函数表达式定义之后被调用
var bb = showM("call after function");
对于函数表达式,变量赋值是不会提前的,即function(arg1, arg2, ...){}
是不会提前的,所以函数定义并没有被执行,所以函数表达式不能像函数声明那样进行函数声明提前。
没有名称,在解释器经过它们时执行一次。示例如下:
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 fn(){
console.log("函数声明");
})();
//情况4
//语法错误,匿名函数属于函数表达式,未执行赋值操作,不能被调用
function(){
console.log("函数表达式");
}();
//Uncaught SyntaxError: Function statements require a function name
//情况5
//输出
(function(str){
console.log(str);
})("函数表达式");
经典的立即执行函数样式
(function(a){ console.log(a); })(123); //123
(function(a){ console.log(a); }(1234)); 1234
!function(a){ console.log(a); }(12345); //12345
+function(a){ console.log(a); }(123456); //123456
-function(a){ console.log(a); }(1234567); //1234567
//而加上(),!,+,-等符号可以执行,是因为加上这些符号就可以告诉JavaScript引擎这不是函数声明了
注意:
只有函数表达式才能实现立即执行,匿名函数也是函数表达式为何不能立即执行呢,因为匿名函数开始的function会被JavaScript引擎识别为函数声明的开始,所以加上括号也不会被执行,而加上(),!,+,-等符号可以执行,是因为加上这些符号就可以告诉JavaScript引擎这不是函数声明了。
函数声明、 函数表达式的执行顺序
函数声明:
1 |
function 函数名称 (参数:可选){ |
函数表达式:
1 |
function 函数名称(可选)(参数:可选){ |
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');
}
//下面的 getName 由于与最上的相同,故被重新赋新值
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