#函数表达式
##函数声明和函数表达式的区别
函数的定义有两种形式,一种是函数声明,一种是函数表达式
使用声明时,要注意函数声明提升现象,比如说在if语句中使用声明会出错,但是表达式就不存在这个问题
表达式要在使用前定义,而声明不用
通过声明会获得一个name属性,而表达式中其name为空
function fn() {}
var fn1 = function() {};
console.log(fn.name); //fn
console.log(fn1.name); //
##递归调用
下面是一个很常见的递归的使用,进行阶乘:
function fn(num) {
if (num <= 1) {
return 1;
} else {
return num * fn(num - 1);
}
}
console.log(fn(5)) //120
如果我们把这个fn函数赋值给另一个变量,则这个变量中将指向这个函数。
var a = new Object();
a.say = fn;
console.log(a.say(5)); //120
函数里面的内容仍然是fn,如果该变这个fn,将会发生有意思的事情:
fn = function (num) {
if (num <= 1) {
return 1;
} else {
return num * fn(num - 2)
}
};
console.log(a.say(5)); //40
console.log(fn(5)); //15
在改变fn之后再次调用a.say(),实际上就相当于是在一个函数里面调用了一个外层定义的另外的不同函数而已。并不会让里面的fn变成a.say。
我们会想,这样一直定义这个函数会有些浪费,我把它设置为null,a.say不会变为空,因为他们已经不是指向同一个函数的指针了。
fn = null;
console.log(a.say(5)); //报错
但是还是会报错,因为a.say方法里面的函数fn已经纬null了.
我们希望a.say方法中会递归调用a.say本身,而不是一直fn,因为不知道后期它会不会有变回。
我们可以用到arguments.callee
function fn1(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
var b = new Object();
b.say = fn1;
fn1 = null;
console.log(b.say(5)); //120
看,这样无论fn1怎么变化,都不会再影响到我b.say方法了。
但要注意在严格模式下arguments.callee不能通过脚本访问。我们可以使用**命名函数表达式**
var c = new Object();
c.say = (function fn2(num) {
if (num <= 1) {
return 1;
} else {
return num * fn2(num - 1);
}
});
fn2 = null;
console.log(c.say(5)); //120
这样,即使后面fn2被改动,c.say同样能够实现递归调用自身。
##闭包
闭包的严格定义时“由函数(环境)及其封闭的自由变量组成的集合体”(Node.js开发指南,郭家宝)
我们先看一下这个有趣的例子
function fn() {
var result = [];
for (var i = 0; i < 10; i ++) {
result[i] = function() {
return i;
}();
}
console.log(result);
}
function fn1() {
var result = [];
var myarr = [];
for (var i = 0; i < 10; i ++) {
result[i] = function() {
return i;
};
}
for (var j = 0; j < 10; j ++) {
myarr.push(result[j]());
}
console.log(myarr);
}
fn(); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
fn1(); //[10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
从平时的角度来理解:
fn中,通过for循环,每一个result的成员都是得到的一个值;
fn1中,for循环下,每一个result的成员得到的是一个函数。通过对这个函数的调用,才获得一个值。可是为什么得到的结果和fn()不一样呢?
因为每一个result中的i都是从它外层的函数中获得的。fn中,当i=0时函数就执行了,所以它的得到的i的值是当前的0;而在fn1中,对result[j]函数调用时,i都已经是10了,所以它所有的结果都是10;
现在来学习一下作用域链的问题,再从这个角度来解释上面的例子。
当一个函数被**调用**时,会发生:
* 一个执行环境被创建;
* 响应的作用域链被创建,作用域链保存自函数的内部属性[[Scope]]中,作用域链本质是一个列表,里面是指向不同‘变量对象’的指针。
* 函数的arguments和this值被获得。
* arguments和其他命名参数的值初始化了函数的‘活动对象’
在作用域链中,自身函数的活动对象排在第一位,然后是外层函数的活动对象,再是外层的外层,最后是全局执行环境的‘变量对象’。
在函数中访问一个变量,就会从作用域链不同变量对象中去搜索具有相同名字的变量。搜索到了就不往外搜索了。
上面的第一个fn中,并不是在里面创建了一个函数,而是直接执行了一个函数,将它的返回值赋给了result[i];
原文:http://www.cnblogs.com/supersoup/p/4808853.html