第一呷 :基础函数
关于函数,尽管少,但毫不逊色。
在javascript中,函数是值,但它们不仅仅是简单的数值,字符串,或者甚至复杂的数据结构树或者地图。函数表示要执行的运算。就像数值、字符串和数组,它们都有一个表现形式。我们从尽可能简单的函数开始。在javascript中,它看起来像这样:
function () {}
这是一个没有使用值也没有产生值的函数。我们如何在javascript中表示“没有值”呢?我们马上就会发现,首先,我们来验证一下函数是一个值:
(function () {}) //=>[Function]
什么?它为什么没有返回function () {}给我们,这似乎打破了“如果一个表达式是值,它将返回同样的值”这一规则,发生了什么事?最简单且最容易的回答是:尽管javascript解析器确实返回了那个值,呈现到屏幕上却稍微有点不同。[Function]是Node.js的作者做出的选择,javascript环境承载REPL(读取-求值-打印循环:read-eval-print loop) 。如果你在浏览器中测试,你就会看到你键入的代码了。
我更偏爱其他的,但必须承认的是,屏幕反馈给我们的是任意的,并且真正重要的是,它有益于人们阅读。但是我们必须要懂得,不管是看到[Function]还是function () {},在javascript内部有一个完整而适当的函数。
函数和恒等式
你还记得,关于恒等式我们有两种类型的值:值类型和引用类型。如果内容相同,值类型是恒等的,而引用类型不是。
函数是哪一种呢?我们来试一下。由于对javascript解析器让步的原因,我们把函数放在小括号中。
(function () {}) === (function () {})
//=> false
像数组一样,每次你对一个表达式求值产生一个函数时,你得到的是一个不与任何其他函数相同的新函数,即便你使用同样的表达式来生成它。“Function”是一种引用类型。
应用函数
我们把函数用到现实中来。我们使用函数的方式是把它们运用到零个或多个被称为参数的值。正如 2+2生成一个值(在这个场景中是4),把一个函数运用到零个或多个参数同样会生成一个值。
这里就是在javascript中,我们如何将函数应用于一些值。我们说fn_expr是一个当我们对其求值时会生成一个函数的表达式,我们把参数称为args,这里就是怎么把函数运用到一些参数了:
fn_expr(
args)
现在,我们只知道这样一个表达式:function () {},让我们来使用它。为了让解析器高兴,我们把它放在小括号中,就像我们上面做的那样:(function () {})。由于我们没有给它任何参数,因此我们只是简单地在表达式后面写上小括号。所以我们这样写:
(function () {})()
//=> undefined
这个undefined是什么?
undefined
在javascript中,缺少的值被写为undefined,这意味着没有值。它还会再次出现。
undefined是它自己的类型的值,它就像一个值类型:
undefined
//=> undefined
像数值、布尔和字符串一样,javascript可以打印出值undefined。
undefined === undefined
//=> true
(function () {})() === (function () {})()
//=> true
(function () {})() === undefined
//=> true
不管你怎么去求undefined的值,你会得到一个相同的返回值。undefined是一个意为"我没有值"的值,但是它还是一个值。
你可能会想javascript中的undefined如同SQL的NULL,不是的。在SQL中,两个“NULL”的东西不等同于相同,因为两个未知的东西不可能是相等的。在javascript中,每一个undefined和每一个其他的undefined都是相同的。
void
我们看到javascript通过键入undefined代表一个未定义的值,并且我们已经用两种方式生成了undefined值。
1.通过对一个没有返回值的函数求值(function () {})()和;
2.通过我们自己写undefined
这里有第三种方式,使用javascript的void操作符,看:
void 0
//=> undefined
void 1
//=> undefined
void (2 + 2)
//=> undefined
void是一个操作符,它总是把任何值和求值转为undefined,所以,当我们故意想要一个undefined的值时,我们应该使用第一种、第二种还是第三种形式呢?答案是使用void。按照惯例,使用void 0。
第一种形式虽有效却繁琐,第二种形式大部分时候是有效的,但是通过重新分配undefined一个不同的值有可能打破它,这一点我们将会在重新分配与突变中讨论。第三种形式始终保持正常工作,所以这是我们会使用的。
不带函数体和参数的函数
回到我们的函数,我们这样求值:
(function () {})()
//=> undefined
让我们回想一下,我们将函数function () {}应用到无参数的情况(因为()里面没有东西),所以我们怎么知道会产生undefined呢?这很简单:
当我们定义一个函数的时候,我们写单词function,然后放参数(可能是空的)列表,r然后我们给函数一个括在花括号里的函数体{...}。函数体是用分号分隔的javascript语句列表(可能为空)。
类似: {语句1; 语句2; 语句3; … ; 语句n }
我们还未讨论过这些语句。什么是语句?
javascript语句有许多种,第一种我们已经见过。一个表达式是一个javascript语句。尽管它们并不十分实用,以下都是有效的javascript函数,并且应用时它们的计算结果都为undefined。
(function () { 2 + 2 }) (function () { 1 + 1; 2 + 2 })
你也可以用换行符分离语句,约定是使用某种形式的一致缩进。
(function () {
1 + 1;
2 + 2
}) (function () {
(function () {
(function () {
(function () {
})
})
});
(function () {
})
})。。。
最后一种是个怪杰,但是既然函数体可以包含语句,语句可以是表达式,而函数是表达式...你明白了吧。
所以我们应用的时候怎么得到一个返回值的函数呢?使用return关键字和任何表达式:
(function () { return 0 })()
//=> 0 (function () { return 1 })()
//=> 1 (function () { return 'Hello ' + 'World' })()
// 'Hello World'
return关键字创建一个return 语句,立即终止函数应用并且返回表达式的计算结果。
计算结果为函数的函数
如果一个表达式的计算结果是一个函数,很好,一个表达式,并且如果可以有任何表达式在return 语句的右边,我们可以把计算结果为函数的表达式放在一个函数表达式的右边么?
是的:
function () {
return (function () {})
}
这是一个函数,它被调用时计算结果为一个函数,而这个返回的函数被调用时计算结果为undefined。让我们用一个简单的术语。不要说“当被调用时,计算结果为__”,我们会说“返回__”。不要说“返回undefined”,我们说“没有返回任何东西”。
所以我们有一个函数,它返回一个没有返回任何东西的函数。同样的:
function () {
return (function () {
return true
})
}
这是一个函数,它返回一个返回1的函数。
(function () {
return (function () {
return true
})
})()()
//=> true
很好,我们一直很聪明,但是到目前为止这一切看起来很抽象,晶体的衍射本身是美丽而有趣的,但你不能因为想要看它的实际应用而责怪我们,比如能够决定一颗百万光年远的星星的距离。所以,在下一章“我想要一个参数”中,我们会看到如何使函数实用。