函数表达式

定义函数的方式有两种:一种是函数声明,一种是函数表达式。关于函数声明,它有一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。这就意味着可以把函数声明放在条用它的语句后面。函数表达式和其他表达式一样,使用之前必须先赋值,否则会发生错误。

递归

递归函数是在一个函数通过名字调用自身的情况下构成的。 arguments.callee 是一个指向正在执行函数的指针,因此可以用它来实现函数的递归调用。严格模式下不可使用。

闭包

闭包是指有权访问另一个函数作用域种的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

闭包和变量

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任意变量的最后一个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function createFunctions(){
var result = [];
for(var i=0; i<10; i++){
result[i] = function(){
return i;
}
}
return result;
}
var arr = createFunctions();//返回一个函数数组,每个函数都返回10
arr[0]();//10
function createFunctions(){
var result = [];
for(var i=0; i<10; i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
var arr = createFunctions();//返回一个函数数组,函数的返回值从0-9
arr[0]();//0

关于 this 对象

我们知道,this 对象是在运行是基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数作为某个对象的方法调用时,this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var name = 'this name is window';
var obj = {
name: 'this name is obj',
getName: function(){
return function(){
return this.name
}
}
}
obj.getName()();//'this name is window'
var name = 'this name is window';
var obj = {
name: 'this name is obj',
getName: function(){
var that = this;
return function(){
return that.name
}
}
}
obj.getName()();//'this name is obj'

内存泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//如果闭包的作用域中保存着一个HTML 元素,那么意味着该元素无法被销毁。
function assignHandler(){
var el = document.getElementById('box');
el.onclick = function(){
alert(el.id);
}
}
//以上代码创建了一个作为 el 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用,导致无法减少 el 的引用数,因此它占用的内存也永远不会被回收,这个问题可以通过稍微改下下代码来解决,如下:
function assignHandler(){
var el = document.getElementById('box');
var id = el.id;
el.onclick = function(){
alert(id);
}
el = null;
}

模仿块级作用域

JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的。但是我们可以通过使用匿名函数来模拟块级作用域。

1
2
3
(function(){
//块级作用域
})();