JS中比较常见的两种声明函数的方式如下:
//方式1:函数表达式
var aaa = function () {//匿名函数表达式
};
var bbb = function f() {//命名函数表达式
};
//方式2:函数声明
function ccc() {
}
两者的区别如下:
区别1:函数声明可以提前被解析出来,而表达式不可以。
解析器会先读取函数声明,并使其在执行任何代码之前可以访问,在任何地方调用都不会有问题;
而函数表达式则必须等到解析器执行到它所在的代码行才会真正被执行,提前调用会报错。
aaa(); //会报错--aaa is not a function
ccc(); //可以调用
var aaa = function(){};
function ccc(){}
区别2:函数表达式可以直接后面加括号执行,而函数声明是不可以。
var aaa = function () {
}();//可以直接执行
function ccc() {
}()//会报错
值得注意的是:当同时使用这两种方式声明同一个函数名,最终执行的是函数表达式声明的函数。
// 函数表达式
var f = function () {
console.log(1);
}
// 直接声明
function f() {
console.log(2);
}
f();//控制台打印1
其实还有下面两种创建函数的方式:
方式1:自执行函数
自执行函数严格来说也叫函数表达式,它主要用于创建一个新的作用域,在此作用域内声明的变量,不会和其它作用域内的变量冲突或混淆,大多是以匿名函数方式存在,且立即自动执行。
举例如下:
//可用来传参
(function (x, y) {
console.log(x + y);
})(2, 3);
//带返回值
var sum = (function (x, y) {
return x + y;
})(2, 3);
console.log(sum);
~function () {
var name = '~';
console.log(name);
}();
!function () {
var name = '!';
console.log(name);
}();
;(function () {
var name = ';';
console.log(name);
})();
-function () {
var name = '-';
console.log(name);
}();
//逗号运算符
1, function () {
var name = ',';
console.log(name);
}();
//异或
1 ^ function () {
var name = '^';
console.log(name);
}();
//比较运算符
1 > function () {
var name = '>';
console.log(name);
}();
~+-!(function () {
var name = '~+-!';
console.log(name);
})();
~!(function () {
var name = '~!';
console.log(name);
})();
(function () {
var name = 'call';
console.log(name);
}).call();
(function () {
var name = 'apply';
console.log(name);
}).apply();
方式2:函数构造法,参数必须加引号
var sum3 = new Function('n1', 'n2', 'return n1+n2');
console.log(sum3(2, 3));//5
从技术角度讲,这是一个函数表达式。一般不推荐用这种方法定义函数,因为这种语法会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。
下面再看一个例子:
var name = 'hello';
function fun() {
var name = 'world';
return new Function('return name');//不能获取局部变量
}
console.log(fun()());//控制台打印hello
Function()构造函数每次执行时都会解析函数主体,并创建一个新的函数对象,所以当在一个循环或频繁执行的函数中调用Function()构造函数效率是非常低的。而函数字面量却不是每次遇到都会重新编译的,用Function()构造函数创建一个函数时并不遵循典型的作用域,它一直把它当作是顶级函数来执行。