掌握函数对JavaScript程序猿来说是个基本功,因为JavaScript的函数有很多用法;
接下来,我们会学习不同的方法去定义函数,你会学到函数表达式和函数声明;
你会看到局部作用域(local scope)和变量提升(hoisting)是如何工作的;
接着我们会学习一些模式去帮助我们改善我们的API(更好的编程接口),代码初始化过程和性能。
让我们深入探讨函数,首先从回顾和澄清非常重要的基础知识开始。
所以可能出现的情况,函数A是一个对象,有属性和方法,其中一个恰巧是函数B,B接受一个函数C作为参数,当执行结束后,返回另一个函数D;
乍看之下,需要去记住很多函数。但当你属性了函数的各种用途,你就会欣赏函数提供的能力,灵活性和表达性;
一般,当你思考JavaScript函数时,可以想象成一个对象,但唯一一个特殊的功能就是这个对象是可调用的,意味着它能被执行;
为了让函数是对象这个事实更明显一些,我们可以看new Function()构造方法的用法:
// antipattern // for demo purposes only var add = new Function('a, b', 'return a + b'); add(1, 2); // returns 3这段代码中,add()是一个对象是毫无疑问的,因为它是由构造方法创建的。
虽然使用Function()构造函数不是一个好注意(和Eval()一样糟糕),因为代码作为一个字符串传入并解析;
写起来和读起来也非常不方便,你不得不去转义引号,如果你想为了更好看,在函数中适当缩进代码就要格外小心;
函数第二个重要的功能就是函数提供作用域,在JavaScript中,没有大括号的局部作用域,换言之,大括号不创建作用域,只有函数作用域;
在函数中,任何使用var定义的变量都是局部变量,在函数外是不可见的;
说大括号不创建局部作用域的意思是如何你在if或者while循环中使用var定义了一个变量,并不意味着这个变量是if或者for的局部变量;
它是外围函数(wrapping function)的局部变量,如果没有外围函数,它会成为全局变量,我们以前说过要尽可能的减少全局变量;
所以函数是控制变量作用域不可缺少的因素。
接下来,让我们花一些时间去讨论一下用来定义函数的术语,因为在我们讨论模式的时候,使用准确,一致的名称和代码一样重要;
看下面这段代码:
// named function expression var add = function add(a, b) { return a + b; };这段代码通过命名的函数表达式(named function expression)展现了一个函数;
如何你省略了函数表达式的名字,你会得到一个未命名的函数表达式(unnamed function expression),被简称为函数表达式(function expression)或者更常见 的叫法匿名函数(anonymous function);
例子:
// function expression, a.k.a. anonymous function var add = function (a, b) { return a + b; };所以函数表达式(function expression)范围更广,命名的函数表达式(named function expression)是函数表达式的一种特殊情况(定义了可选的名称);
当你省略第二个add,以未命名的函数表达式结束,这不会影响函数的定义和使用;
唯一的不同之处就是函数的name属性会是一个空字符串或者是undefined(取决于具体的实现),name属性不是语言的扩展(不是ECMA标准的一部分)但是在很多环境中广泛使用;
如果你保留第二个add,那么add.name属性将会包含字符串"add";
最后,还有函数声明(function declarations):
function foo() { // function body goes here }在语法上,命名的函数表达式和函数声明看起来非常像,不同的地方就是是否将函数表达式的结果赋值给了一个变量;
有些时候,除了查看上下文(context)没有其它办法说明函数声和命名的函数表达式的区别,我们会在以后详细说明;
还有一个语法的区别就是结尾的分号,函数声明不需要分号,但函数表达式需要;
你应该始终使用这个分号,即使自动分号插入机制可能会为你做这件事。
还有一个术语函数字面量(function literal)也经常使用,但它可能意味着未命名函数表达式或命名的函数表达式,因为这种模糊性,所以最好不要使用它。