JS函数表达式

JS函数的递归、闭包、模仿块级作用域、及函数的私有变量。

定义函数:

  1. 函数声明:
1
2
3
function functionName(arg0, arg1, ... ){
// 函数体
} // name属性访问函数名

函数声明提升:执行代码之前会先读取函数声明。所以即便函数声明在调用函数代码之后,也不会报错。

  1. 函数表达式:
1
2
3
var functionName = function(arg0, arg1, ... ){
// 函数体
}; //匿名函数

并没有函数声明提升,使用之前必须先赋值。

递归

一个函数通过名字调用自身的情况下称为递归函数

问题:

1
2
3
4
5
6
7
8
9
10
11
function fnuc (num){
if (num <= 1){
return 1;
}
else{
return num*func(num - 1);
}
}
var anotherFunc = func;
func = null;
console.log(anotherFunc(10)); // error,因为func指向的已经变成null而不再是函数

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 普通模式
function fnuc (num){
if (num <= 1){
return 1;
}
else{
return num*argeuments.callee(num - 1);
}
}
// 严格模式
var func = (function f (num){
if (num <= 1){
return 1;
}else{
return num * f(num - 1);
}
});

递归函数使用arguments.callee来递归调用自身,不需要使用函数名。

闭包

有权访问另一个函数作用域中的变量称为闭包。

闭包是内部函数在其父函数已经终止之后可以引用其外部封闭函数中存在的变量的一种手段。

1
2
3
4
5
6
7
function Func (propertyName) {
return function (object_1, object_2) {
var value_1 = object_1(propertyName);
var value_2 = object_2(propertyName);
}
} // 即使内部函数被返回,而且在其他地方调用了,但他依然可以访问变量property
// 原因是因为内部函数作用域链始终包含Func()的作用域

​ 当某个函数被调用时,会创建一个执行环境及相应的作用域链,使用argeuments其他命名参数初始化函数的活动对象

​ 但在作用域链中,外部函数活动对象始终处于第二位,外部函数的外部函数活动对象始终处在第三位,以此类推,知道作用域链的终点为全局执行环境

闭包与变量:

闭包的副作用:

闭包只能取得包含函数中任何变量的最后一个值

1
2
3
4
5
6
7
8
9
function arr () {
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function(){
return i; // i的值一直为10
};
}
return result;
}

可以利用创建另一个匿名函数强制让闭包符合预期:

1
2
3
4
5
6
7
8
9
10
11
function arr () {
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function (num){
return function(){
return num;
};
}(i);
}
return result;
}

this对象:

匿名函数的执行环境具有全局性,因此this对象通常指向window

1
2
3
4
5
6
7
8
9
10
var name = "Dad";
var object = {
name: "Son",
getNameFunc: function (){
return function (){
return this.name;
};
}
};
console.log(objject.getNameFunc()()); // "Dad" 在非严格模式下

将外部作用域中的this保存在一个闭包能够访问的变量里,就能让闭包访问该变量:

1
2
3
4
5
6
7
8
9
10
11
var name = "Dad";
var object = {
name: "Son",
getNameFunc: function(){
var that = this;
return function(){
return that.name;
};
}
}
console.log(objject.getNameFunc()()); //"Son"

过度的使用闭包,会占用大量的内存。

模仿块级作用域

JavaScript没有块级作用域的概念,所以在块语句中定义的变量,实际上是在包含函数中创建的而非语句中创建的。

1
2
3
4
5
6
7
function numbers (count) {
for (var i = 0; i < count; i++){
alert(i);
} // 一般拥有块级作用域的语言,在循环结束后,便对循环内定义的变量进行销毁。
var i; // 重新声明变量,会被执行,但并不覆盖原来的i
alert(i); // 输出正常
}

解决办法:利用匿名函数模仿块级作用域。

1
2
3
4
5
6
7
8
9
10
(function(){
// 这里是块级作用域
})(); // 后面的圆括号表示立即执行函数,其等价于:

/***********************
var func = function (){
// 块级作用域
};
func();
***********************/

优点:可以减少闭包中占有内存的问题,由于没有指向匿名函数的引用,所以只要函数执行完毕就被销毁。

私有变量

任何在函数中定义的变量,都认为是私有变量,因为不能在函数的外部访问这些变量。

私有变量包括:函数的参数、局部变量、在函数中定义的其他函数。

特权方法:

有权访问私有变量私有函数的公有方法称为特权方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 利用构造函数定义特权方法
function MyObject () {
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction () {
return false;
}

// 特权方法
this.publicMethod = function () {
privateVariable++;
return privateFunction();
};
}

构造函数模式针对每个实例都会创造一个新方法,利用静态私有变量可以解决这个问题。

静态私有变量

通过在私有作用域中定义私有变量和函数,创建特权方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(){   // 创建私有域
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction(){
return false;
}
// 构造函数
MyObject = function(){ // 初始化未经声明的变量,总是会创建全局变量 [非严格模式下]
};
// 公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();

模块模式

为单例创建私有变量和特权方法。单例即只有一个实例的对象。

单例:

1
2
3
4
5
6
var obj = {
name: 'Xuchao',
method: function(){
// 方法代码
}
};

模块模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var oo = function(){
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction(){
return false;
}
// 特权/公有方法和属性
return { // 单例的公共接口
publicProperty: true;
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();

增强的模块模式

如果要返回一个特定模式的对象,则需要用到增强的模块模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var oo = function(){
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction(){
return false;
}
// 创建对象
var object = new CustomType();
// 特权/公有方法和属性
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
return object; // 返回这个对象
}();

客官里面请~