原文参考链接:http://javascript.ruanyifeng.com/grammar/function.html#toc1
1.1 函数名提升
javascript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。
f();
function f() {}
表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript就会报错。
f();
var f = function (){};
// TypeError: undefined is not a function
//等同于
var f;
f();
f = function () {};
//调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错
1.2 不能在条件语句中声明函数
根据ECMAScript的规范,不得在非函数的代码块中声明函数,最常见的情况就是if和try语句
if (foo) {
function x() {}
}
try {
function x() {}
} catch(e) {
console.log(e);
}
1.3 函数作用域#
作用域(scope)指的是变量存在的范围。Javascript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。
注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
if (true) { var x = 5;}
console.log(x); // 5
//虽然x在{}里面,但是,由于局部变量只存于函数内部。因此这里x依然是全局变量
1.4 函数内部变量的提升#
与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
function foo(x) {
if (x > 100) {
var tmp = x - 100;
}
}
//等同于
function foo(x) {
var tmp;
if (x > 100) {
tmp = x - 100;
}
}
1.5 function的arguments对象转js数组#
var args = Array.prototype.slice.call(arguments);
//或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
1.6 关于闭包#
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中。
function createIncrementor(start) {
return function () {
return start++;
};
}
var inc = createIncrementor(5);
inc() // 5
inc() // 6
inc() // 7
//start是属于createIncrementor函数的局部变量。 每一次调用inc方法,都会使其自增。
//注意,外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大
1.7 js立即调用表达式#
目的
- 一是不必为函数命名,避免了污染全局变量;
- 二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
//注意分号结尾