1 function keith() { 2 console.log(1); 3 } 4 keith(); //2 5 function keith() { 6 console.log(2); 7 } 8 keith(); //2
上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升,前一次声明在任何时候都是无效的。JavaScript引擎将函数名视同变量名,所以采用函数声明的方式声明函数时,整个函数会像变量声明一样,被提升到代码头部。表面上,上面代码好像在声明之前就调用了函数keith。但是实际上,由于“变量提升”,函数keith被提升到了代码头部,也就是在调用之前已经声明了。再看一个典型的例子。
1 if (true) { 2 function foo() { 3 return 1; 4 } 5 } else { 6 function foo() { 7 return 2; 8 } 9 } 10 11 console.log(foo()) //2
这个例子十分典型,调用foo函数之后返回的是2,而不是1。在条件语句中声明函数会在下面说到。
1.5:不能在条件语句中声明函数
参考这篇文章,原文有那么一句话(本人翻译):在条件语句中声明函数是非标准结构的特征。也就是说,在if代码块声明了函数,按照语言规范,这是不合法的。但是,实际情况是各家浏览器往往并不报错,能够运行。
由于存在函数名的提升,所以在条件语句中声明函数,可能是无效的。
1 if (false) { 2 function f() {} 3 } 4 console.log(f()); //undefined
上面代码的原始意图是不声明函数f,但是由于f的提升,导致if语句无效,所以上面的代码不会报错。要达到在条件语句中定义函数的目的,只有使用函数表达式。
1 if (false) { 2 var f = function () {}; 3 } 4 5 console.log(f()) //Uncaught TypeError: f is not a function
2.函数的部分属性和方法
2.1:name属性
name属性返回紧跟在function关键字之后的那个函数名。
1 function k1() {}; 2 console.log(k1.name); //'k1' 3 4 var k2 = function() {}; 5 console.log(k2.name); //'' 6 7 var k3 = function hello() {}; 8 console.log(k3.name); //'hello'
上面代码中,name属性返回function 后面紧跟着的函数名。对于k2来说,返回一个空字符串,注意:匿名函数的name属性总是为空字符串。对于k3来说,返回函数表达式的名字(真正的函数名为k3,hello这个函数名只能在函数内部使用。)
2.2:length属性
length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。返回的是个数,而不是具体参数。
1 function keith(a, b, c, d, e) {} 2 console.log(keith.length) // 5
上面代码定义了空函数keith,它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于5。也就是说,当调用时给实参传递了6个参数,length属性会忽略掉一个。
2.3:toString()方法
函数的toString方法返回函数的代码本身。
1 function keith(a, b, c, d, e) { 2 // 这是注释。 3 } 4 console.log(keith.toString()); 5 //function keith(a, b, c, d, e) { // 这是注释。 }
可以看到,函数内部的注释段也被返回了。
3.函数作用域
3.1:全局作用域和局部作用域