函数表达式
函数声明:函数声明提升,执行代码前会先读取函数声明
1
2
3
4sayHi(); // 正常执行
function sayHi(){
console.log("hi");
}函数表达式
1
2
3
4
5
6
7
8
9// 创建一个函数并将它赋值给变量 functionName,这种叫做匿名函数,name 属性是空字符串
var functionName = function(){
// 函数体
}
sayHi(); //错误:函数还不存在
var sayHi = function(){
console.log("hi");
}
递归
- 递归函数是在一个函数通过名字调用自身的情况下构成的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function factorial(num){
if(num <= 1){
return 1;
} else {
return num * factorial(num - 1); // 跟函数名耦合
return num * arguments.callee(num - 1); // 解耦 严格模式不支持
}
}
// 通用
var factorial = (function f(num){
if(num <= 1){
return 1;
} else {
return num * f(num - 1);
}
})
闭包
- 闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
闭包只能取得包含函数中任何变量的最后一个值
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
26
27
28
29
30
31
32
33
34
35
36function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(){
return i;
}
}
return result;
}
console.log(createFunctions()[0]()); // 10
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
console.log(createFunctions()[0]()) // 0
var adder = (function (){
var n = 0
return function(){
return n += 1
}
})()
console.log(adder()) // 1
console.log(adder()) // 2在闭包中使用 this 对象也可能导致一些问题。this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象。匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window
1
2
3
4
5
6
7
8
9
10
11
12
13var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function(){
var that = this // this 指向的是window
return function(){
return that.name;
return this.name; // 非严格模式下"The Window"
}
}
}
console.log(object.getNameFunc()()) // "My Object"闭包会引用包含函数的整个活动对象,因此要解除对象引用释放内存
1
2
3
4
5
6
7
8function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
console.log(id);
};
element = null; // 解除引用
}
模仿块级作用域
JavaScript 没有块级作用域。这意味着在块语句中定义的变量,实际上是包含函数中而非语句中创建的1
2
3
4
5
6
7
8
9
10function outputNumber(count){
for(var i = 0;i<count;i++){
console.log("for:" + i)
}
var i; // 视而不见
// var i = 0 ; 会执行
console.log("output:" + i);
}
outputNumber(10) // for: 1 for: 2 for: 3... output: 10
JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到上述情况,它只会对后续的声明视而不见(变量初始化会执行)。匿名函数可以用来模仿块级作用域。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15(function(){
// 这里是块级作用域
})();
function outputNumber(count){
(function(){
for(var i = 0;i<count;i++){
console.log("for:" + i)
}
}
)();
console.log("output:" + i);
}
outputNumber(10) // i is not defined
私有变量
JavaScript 中没有私有成员的概念;所有对象属性都是公有的。但是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。包括函数的参数、局部变量和在函数内部定义的其他函数
(1) 即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量
(2) 有权访问私有变量的公有方法叫做特权方法
(3) 可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法