说起来ECMAScript中什么最有意思,用原书(《JavaScript高级程序设计》)作者的话说——莫过于函数了,有意思的根源在于函数实际上是对象。每个函数都是Function
类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也就是一个指向函数对象的指针,不会与某个函数绑定。
函数的定义方式有三种:
- 函数声明
- 函数表达式
- 使用
Function
构造函数
函数声明的方式是比较常见的一种,如下面例子所示:
function sum(num1, num2){
return num1 + num2;
}
这与下面使用函数表达式定义函数的方式几乎相差无几:
var sum = function(num1, num2){
return num1 + num2;
};
上面的函数表达式语法定义了变量sum
,并将其初始化为一个函数。有读者可能会注意到,function
关键字后面没有函数名,这是因为在使用函数表达式定义函数的时候,没有必要使用函数名,通过变量sum
即可以引用函数。另外,还要注意函数未尾有个分号,就像声明其它变量一样。
使用Function
构造函数定义函数时,Function
构造函数可以接收任意数量的参数,但最后一个参数始终都被看作是函数体,而前面的参数则枚举出了新函数的参数。来看下面的例子:
var sum = new Function('num1', 'num2', 'return num1 + num2');
从技术上讲,这也是一个函数表达式。但是,我们不推荐使用这种方式定义函数,因为这种函数定义方式会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。不过,这种方式对于理解“函数是对象,函数名是指针”来说倒是非常直观的。
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。换句话说,一个函数可能会有多个名字,如下例所示:
function sum(num1, num2){
return num1 + num2;
}
console.log(sum(10, 10));// 20
var anotherSum = sum;
console.log(anotherSum(10, 10));// 20
sum = null;
console.log(anotherSum(10, 10));// 20
上面的代码首先定义了一个名为sum
的函数,用于求两个数的和。然后,又声明了变量anotherSum
,赋值为sum
,此时anotherSum
和sum
就指向了同一个函数,因此anotherSum()
也正常返回了结果。即使切断sum
与函数对象的引用关系,也不会影响anotherSum
。
函数名作为指针,也可以理解为什么ECMAScript中没有函数重载(函数名相同,参数列表不同)的概念。来看下面的示例:
function add(num){
return 100 + num;
}
function add(num, num2){
return 200 + num;
}
console.log(add(100));// 300
按照函数重载的概念,两个add
函数的参数列表不同,当传入一个参数时应该调用第一个add
,当传入两个参数时应该调用第二个add
。但如上例所示,即使传入一个参数依然是调用第二个add
,结果为300
,这是为什么呢?
答案在函数名是指针,所以第二个add
覆盖了第一个add
,也许用函数表达式的写法更容易理解:
var add = function(num){
return 100 + num;
};
var add = function(num, num2){
return 200 + num;
};
如上例所示,重新声明add
会覆盖第一个add
。而JavaScript中的函数调用会根据实际传入的参数个数按顺序匹配参数列表,比如这里传入了一个100
,那么add
中的num
就为100
,num2
为undefined
,如果像这样调用add(100, 20)
,那么num2
则为20
。
ECMAScript 2015引入了let
关键字可以避免变量覆盖的问题,如下所示:
var add = function(num){
return 100 + num;
};
//Uncaught SyntaxError: Identifier 'add' has already been declared
let add = function(num, num2){
return 200 + num;
};
解析的时候已经报错,提示add
已经声明,所以使用let
会更安全一些。
最近在重读唐浩明先生的小说《曾国藩》,里面曾国藩讲到,看书要做到四点:看读写作。看指的就是通常的默默的阅读,可以增加阅读量;读指高声朗诵,他讲一些诗词歌赋等文章,非高声朗诵不能体会其中奥妙;写是抄写,看书时遇到经典的段落文章、好的句子,要抄写下来,既能加深理解,又能以备日后查阅;作是根据自己的理解再创作,这是检测自己是否真的理解以及提高作文能力的重要途径。
本文算是抄写吧,虽然有稍些改动,但基本和原书内容一致,记录下来,一方面加深印象,另一方面也是分享给读者。