js 函数表达式
一、函数表达式 和 函数声明 的区别
- 函数的创建有两种方式:函数声明 和 函数表达式。
- 函数声明具有函数声明提升,函数表达式不具有函数声明提升。
(1)、函数声明
函数声明有一个重要的特点就是 函数声明提升——在执行代码之前会先读取函数声明,这就意味着可以把函数声明放在调用它的语句之后。
fn(); // hello
function fn(){
alert("hello");
}
(2)、函数表达式
函数表达式就是常规的变量赋值——创建一个函数(一般为匿名函数,当然也有命名函数),并将它赋值给一个变量,使用前必须先赋值,不存在函数声明提升。
// sayHello(); // error
var sayHello = function(){
alert("hello");
};
sayHello(); // hello
二、递归
递归函数,是在一个函数内部,通过名字调用自身的情况下形成的。
举个栗子:这是一个经典的递归实现阶乘的函数。
function factorial(num){
if(num <= 1){
return 1;
} else {
return num * factorial(num - 1);
}
}
递归有时会面临一个问题:如上代码,在函数有名字,而且名字以后也不会变得情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 仅仅耦合在了一起。为了消除这种紧密的耦合现象,就可以用 argument.callee 来指向正在执行的函数的指针。这样,无论引用函数时使用的是什么名字,都可以保证正常完成递归调用。
function factorial (num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee (num - 1);
}
}
在严格模式下,不能访问 arguments.callee,否则报错。不过,可以使用 命名函数表达式 来达到相同的结果。
var factorial = (function f(num){
if(num <= 1){
return 1;
} else {
return num * factorial(num - 1);
}
});
上述代码,创建了一个名为 f() 的命名函数表达式,然后将她赋值给变量 factorial。即便把函数赋值给了另一个变量,函数的名字 f 仍然有效,所以递归调用照样能正确完成。
三、闭包
1、函数被调用时都发生了什么?
当某个函数被调用时,会创建一个执行环境及其相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位,……直至作为作用域链终点的全局执行环境。在函数执行的过程中,为了读取和写入变量的值,就需要在作用域链中查找变量。
后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,而像函数中的局部环境的变量对象,则只能在函数执行的过程中存在。
function compare(value1, value2){
if(value1 < value2){
return -1;
} else if(value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局执行环境的变量对象。但是,闭包的情况例外。
2、闭包
- 闭包是指有权访问另一个函数作用域中的变量的函数。
- 创建闭包的常见方式,就是在一个函数内部创建另一个函数。
在一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域中。
例如:
function createComparisionFunction(propertyName){
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2){
return -1;
} else if(value1 > value2){
return 1;
} else {
return 0;
}
};
}
var compare = createComparisionFunction("name");
var result = compare({ name: "marry" }, { name: "lily" });
console.log(result); // 1
上述代码,在匿名函数从 createComparisionFunction() 函数中被返回后,它的作用域链被初始化为包含 createComparisionFunction() 函数的活动对象 和 全局变量对象。这样,匿名函数就能访问在 createComparisionFunction() 函数中定义的所有变量了。
更为重要的是,在 createComparisionFunction() 函数执行完毕后,其执行环境的作用域链会被销毁,但它的活动对象仍会留在内存中,知道匿名函数被手动销毁后,createComparisionFunction() 函数的活动对象才会被销毁。
function createComparisionFunction(propertyName){
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2){
return -1;
} else if(value1 > value2){
return 1;
} else {
return 0;
}
};
}
// 创建函数
var compareNames = createComparisionFunction("name");
// 调用函数
var result = compare({ name: "marry" }, { name: "lily" });
// 解除匿名函数的引用(以便释放内存)
compareNames = null;
3、闭包与变量
4、闭包与 this 对象
5、闭包与内存泄漏
四、模仿块级作用域
五、私有变量
1、静态私有变量
2、模块模式
3、增强的模块模式
上一篇: 中文乱码问题解决方案
下一篇: vue swiper的插件