最近我开始维护别人的JavaScript代码。我正在修复错误,添加功能,并尝试整理代码并使其更加一致。
之前的开发人员使用了两种声明函数的方式,我无法确定是否有什么原因。
这两种方式是:
var functionOne = function() {
// Some code
};
而且,
function functionTwo() {
// Some code
}
为什么要使用这两种不同的方法,各自有哪些优缺点?是否有一种方法可以做到另一种方法无法实现的事情?
最近我开始维护别人的JavaScript代码。我正在修复错误,添加功能,并尝试整理代码并使其更加一致。
之前的开发人员使用了两种声明函数的方式,我无法确定是否有什么原因。
这两种方式是:
var functionOne = function() {
// Some code
};
而且,
function functionTwo() {
// Some code
}
为什么要使用这两种不同的方法,各自有哪些优缺点?是否有一种方法可以做到另一种方法无法实现的事情?
第一个函数(function doSomething(x))应该是对象表示法的一部分。
第二个函数(var doSomething = function(x){ alert(x);}
)只是创建了一个匿名函数并将其赋值给变量doSomething
。所以调用doSomething()将调用该函数。
您可能想知道什么是函数声明和函数表达式。
函数声明定义了一个命名的函数变量,而不需要变量赋值。函数声明作为独立的构造出现,不能嵌套在非函数块内。
function foo() {
return 3;
}
ECMA 5(13.0)将语法定义为
function Identifier( FormalParameterListopt ){ FunctionBody }
在上述条件下,函数名在其范围以及其父级的范围内可见(否则它将无法访问)。
而在函数表达式中
函数表达式将函数定义为较大表达式语法的一部分(通常是变量赋值)。通过函数表达式定义的函数可以是具有名称或匿名的。函数表达式不应以“function”开头。
// Anonymous function expression
var a = function() {
return 3;
}
// Named function expression
var a = function foo() {
return 3;
}
// Self-invoking function expression
(function foo() {
alert("hello!");
})();
ECMA 5(13.0)将语法定义为
function Identifieropt ( FormalParameterListopt ) { FunctionBody }
下面是它们的区别:
函数声明可以放在代码的任何位置。即使在调用函数之前,函数定义出现在代码中,函数声明也会在页面上的任何其他代码开始执行之前被提交到内存中或以某种方式提升,从而被执行。
请看下面的函数:
function outerFunction() {
function foo() {
return 1;
}
return foo();
function foo() {
return 2;
}
}
alert(outerFunction()); // Displays 2
这是因为在执行期间,它看起来像:
function foo() { // The first function declaration is moved to top
return 1;
}
function foo() { // The second function declaration is moved to top
return 2;
}
function outerFunction() {
return foo();
}
alert(outerFunction()); //So executing from top to bottom,
//the last foo() returns 2 which gets displayed
如果在调用函数表达式之前未定义它,将导致错误。此外,在函数表达式中,函数定义本身不会像函数声明那样被移动到顶部或者提交到内存中。但是,我们分配给函数的变量会被提升,而且被赋值为undefined。
使用函数表达式的相同函数:
function outerFunction() {
var foo = function() {
return 1;
}
return foo();
var foo = function() {
return 2;
}
}
alert(outerFunction()); // Displays 1
这是因为在执行过程中,它看起来像:
function outerFunction() {
var foo = undefined;
var foo = undefined;
foo = function() {
return 1;
};
return foo ();
foo = function() { // This function expression is not reachable
return 2;
};
}
alert(outerFunction()); // Displays 1
在非函数块(如 if)中编写函数声明是不安全的,因为它们将无法访问。
if (test) {
function x() { doSomething(); }
}
像下面的命名函数表达式,在Internet Explorer9之前的版本中可能无法正常工作。
var today = function today() {return new Date()}
如果您使用这些函数来创建对象,则会得到:
var objectOne = new functionOne();
console.log(objectOne.__proto__); // prints "Object {}" because constructor is an anonymous function
var objectTwo = new functionTwo();
console.log(objectTwo.__proto__); // prints "functionTwo {}" because constructor is a named function
第一个函数语法是匿名函数表达式:
var functionOne = function() {
// do something...
};
第二种是函数声明:
function functionTwo () {
// do something...
}
两者之间的主要区别在于函数名称,因为匿名函数没有名称可供调用。
匿名函数声明快捷方便,许多库和工具倾向于鼓励这种惯用代码风格。然而,匿名函数也有一些缺点:
可读性: 匿名函数省略了一个名称,可能导致代码难以阅读。
调试: 栈跟踪中没有匿名函数的名称,这可能会使调试更加困难。
自我引用: 如果函数需要引用自身,例如递归,该怎么办。
为您的函数表达式提供名称可以很好地解决所有这些缺点,并且没有任何实质性的不利影响。最佳实践是始终为您的函数表达式命名:
setTimeout(function timeHandler() { // <-- look, a name here!
console.log("I've waited 1 second");
}, 1000);
(function IIFE(str) { // <-- look, always name IIFEs!
console.log(str); // "Hello!"
})('Hello!');
对于分配给变量的函数,命名函数在这种情况下不太常见,可能会导致混淆,在这种情况下,箭头函数可能是更好的选择。
Function declaration:
function fn(){
console.log("Hello");
}
fn();
This is very basic, self-explanatory, used in many languages and standard across C family of languages. We declared a function defined it and executed it by calling it.
What you should be knowing is that functions are actually objects in JavaScript; internally we have created an object for above function and given it a name called fn or the reference to the object is stored in fn. Functions are objects in JavaScript; an instance of function is actually an object instance.
Function expression:
var fn=function(){
console.log("Hello");
}
fn();
JavaScript has first-class functions, that is, create a function and assign it to a variable just like you create a string or number and assign it to a variable. Here, the fn variable is assigned to a function. The reason for this concept is functions are objects in JavaScript; fn is pointing to the object instance of the above function. We have initialized a function and assigned it to a variable. It's not executing the function and assigning the result.
参考资料:JavaScript函数声明语法:var fn = function() {} vs function fn() {}
考虑到“堆栈跟踪中显示命名函数”的论点,现代JavaScript引擎实际上能够很好地表示匿名函数。
截至本文撰写时,V8、SpiderMonkey、Chakra和Nitro总是按其名称引用命名函数。如果匿名函数有标识符,则它们几乎总是按其标识符引用。
SpiderMonkey可以找出从其他函数返回的匿名函数的名称。而其他引擎则无法做到这一点。
如果您真的非常希望您的迭代器和成功回调在跟踪中显示,您也可以给它们取个名字...
[].forEach(function iterator() {});
但大部分情况下,这并不值得太过紧张。
'use strict';
var a = function () {
throw new Error();
},
b = function b() {
throw new Error();
},
c = function d() {
throw new Error();
},
e = {
f: a,
g: b,
h: c,
i: function () {
throw new Error();
},
j: function j() {
throw new Error();
},
k: function l() {
throw new Error();
}
},
m = (function () {
return function () {
throw new Error();
};
}()),
n = (function () {
return function n() {
throw new Error();
};
}()),
o = (function () {
return function p() {
throw new Error();
};
}());
console.log([a, b, c].concat(Object.keys(e).reduce(function (values, key) {
return values.concat(e[key]);
}, [])).concat([m, n, o]).reduce(function (logs, func) {
try {
func();
} catch (error) {
return logs.concat('func.name: ' + func.name + '\n' +
'Trace:\n' +
error.stack);
// Need to manually log the error object in Nitro.
}
}, []).join('\n\n'));
func.name:
Trace:
Error
at a (http://localhost:8000/test.js:4:11)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: b
Trace:
Error
at b (http://localhost:8000/test.js:7:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: d
Trace:
Error
at d (http://localhost:8000/test.js:10:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at a (http://localhost:8000/test.js:4:11)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: b
Trace:
Error
at b (http://localhost:8000/test.js:7:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: d
Trace:
Error
at d (http://localhost:8000/test.js:10:15)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at e.i (http://localhost:8000/test.js:17:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: j
Trace:
Error
at j (http://localhost:8000/test.js:20:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: l
Trace:
Error
at l (http://localhost:8000/test.js:23:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name:
Trace:
Error
at http://localhost:8000/test.js:28:19
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: n
Trace:
Error
at n (http://localhost:8000/test.js:33:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27
func.name: p
Trace:
Error
at p (http://localhost:8000/test.js:38:19)
at http://localhost:8000/test.js:47:9
at Array.reduce (native)
at http://localhost:8000/test.js:44:27 test.js:42
func.name:
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
a@http://localhost:8000/test.js:4:5
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: b
Trace:
b@http://localhost:8000/test.js:7:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: d
Trace:
d@http://localhost:8000/test.js:10:9
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
e.i@http://localhost:8000/test.js:17:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: j
Trace:
j@http://localhost:8000/test.js:20:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: l
Trace:
l@http://localhost:8000/test.js:23:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name:
Trace:
m</<@http://localhost:8000/test.js:28:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: n
Trace:
n@http://localhost:8000/test.js:33:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: p
Trace:
p@http://localhost:8000/test.js:38:13
@http://localhost:8000/test.js:47:9
@http://localhost:8000/test.js:54:1
func.name: undefined
Trace:
Error
at a (http://localhost:8000/test.js:4:5)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at b (http://localhost:8000/test.js:7:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at d (http://localhost:8000/test.js:10:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at a (http://localhost:8000/test.js:4:5)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at b (http://localhost:8000/test.js:7:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at d (http://localhost:8000/test.js:10:9)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at e.i (http://localhost:8000/test.js:17:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at j (http://localhost:8000/test.js:20:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at l (http://localhost:8000/test.js:23:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at Anonymous function (http://localhost:8000/test.js:28:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at n (http://localhost:8000/test.js:33:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name: undefined
Trace:
Error
at p (http://localhost:8000/test.js:38:13)
at Anonymous function (http://localhost:8000/test.js:47:9)
at Global code (http://localhost:8000/test.js:42:1)
func.name:
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
a@http://localhost:8000/test.js:4:22
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: b
Trace:
b@http://localhost:8000/test.js:7:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: d
Trace:
d@http://localhost:8000/test.js:10:26
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
i@http://localhost:8000/test.js:17:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: j
Trace:
j@http://localhost:8000/test.js:20:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: l
Trace:
l@http://localhost:8000/test.js:23:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name:
Trace:
http://localhost:8000/test.js:28:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: n
Trace:
n@http://localhost:8000/test.js:33:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
func.name: p
Trace:
p@http://localhost:8000/test.js:38:30
http://localhost:8000/test.js:47:13
reduce@[native code]
global code@http://localhost:8000/test.js:44:33
两者都是定义函数的不同方式。不同之处在于浏览器如何解释它们并将它们加载到执行上下文中。
第一种情况是函数表达式,它只有在解释器达到那行代码时才会加载。所以如果你像下面这样做,你会得到一个错误,显示functionOne不是一个函数。
functionOne();
var functionOne = function() {
// Some code
};
原因是在第一行没有给functionOne分配任何值,因此它是未定义的。我们试图将其作为函数调用,因此出现了错误。
在第二行,我们将匿名函数的引用赋给functionOne。
第二种情况是函数声明,在执行任何代码之前加载。因此,如果您像以下方式那样操作,您将不会收到任何错误,因为声明在代码执行之前加载。
functionOne();
function functionOne() {
// Some code
}
var functionOne = function() {
// Some code
};
函数表达式定义了一个函数作为更大表达式语法的一部分(通常是变量赋值)。通过函数表达式定义的函数可以具有名称或匿名。函数表达式不能以“function”开头(因此在下面的自调用示例周围使用括号)。
将一个变量赋值给一个函数意味着没有提升,我们知道 JavaScript 中的函数可以提升,也就是说它们可以在声明之前被调用,而变量需要在访问它们之前声明,因此在这种情况下我们无法在其声明之前访问该函数,同时这可能是你编写函数的一种方式,对于返回另一个函数的函数,这种声明方式可能是有意义的,在 ECMA6 及以上版本中,您可以将 this 分配给箭头函数,这可以用于调用匿名函数,此声明方式也是创建 JavaScript 构造函数的更好方式。
2. 函数声明
function functionTwo() {
// Some code
}
这只是声明函数的两种可能方式,而在第二种方式中,您可以在声明之前使用该函数。