JavaScript中函数纪要(上)

JavaScript中函数是一等公民,所有的函数实际上是一个Function对象,与其他引用类型一样拥有着属性和方法,也可以被外界或者自身调用,也可以像传递参数一样将函数传递给另一个函数。

JavaScript中函数没有重载的概念,当定义两个同名函数的时候,前一个函数会被覆盖掉,举个栗子。

1
2
3
4
5
6
7
8
function add(num){
console.log(num+100)
}

function add(num){
console.log(num+200)
}
add(100)//300

为何会没有函数重载,是因为JavaScript中函数定义有以下三种,分别是函数声明,函数表达式以及使用Function构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
//函数声明
function add(num){
console.log(num+100)
}
//函数表达式
var add=function(num){
console.log(num+200)
}
//Function构造函数
var add=new Function("num","console.log('num+300')")

add(100)//400

可以看到,当定义同名的函数的时候,也就相当于定义了同名的变量,故而后面的变量会覆盖掉前面的变量,另外需要注意的是JavaScript作用域中的 hoist,包括变量声明提升与函数函数提升。

对于变量来说,在ES5中var定义的变量会提升到作用域中所有的函数与语句前面,而ES6中let定义的变量则不会,let声明的变量会在其相应的代码块中建立一个暂时性死区,直至变量被声明。

1
2
3
4
5
6
//var声明变量
console.log(x === undefined); // "true"
var x = 3;
//let声明变量
console.log(x === undefined); // Uncaught ReferenceError: x is not defined
let x = 3;

对于函数来说,函数声明会被提升到作用域顶部,而函数表达式则不会,因而比较稳妥的是在最后调用函数

1
2
3
4
5
6
7
8
9
10
//函数声明
hoist()// hello world
function hoist(){
console.log("hello world")
}
//函数表达式
hoist()// hoist is not a function
var hoist = function(){
console.log("hello world")
}

也可以将函数作为另一个函数的结果返回,若是返回函数中保存着对外部函数的引用,会发生很好玩的情况:

1
2
3
4
5
6
7
8
9
function closure(){
var foo=10;
var bar=2;
return function(){
return foo*bar;
}
};
var handle=closure();
console.log(handle());//20

可以看到的是,我们在closure函数内部定义了两个局部变量,然后返回带有局部变量操作结果的匿名函数,将closure函数执行后的结果(保存着foo,bar变量引用的匿名函数)赋给handle变量,此时执行handle函数可以看到,输出了closure函数内局部变量操作的结果,此时我们称这种现象为闭包,闭包其实是一个函数,在上述栗子中便是指代的closure函数内的匿名函数。不过值得注意的是,由于闭包会携带包含它的外部函数的整个作用域,故而会很占内存,因此要及时释放变量的引用,不要让其常驻内存,不然会导致内存占用过多,最后出现内存泄漏的情况。