第七章 函数表达式
第七章 函数表达式
7.1 递归
// 递归阶乘
var factorial = (function f(num){
if(num==1){
return 1;
}else{
return num * f(num-1)
}
})
7.2 闭包
- 闭包和匿名函数有区别
- 闭包是指有权访问另一个函数作用域中的变量的函数
- [拓展]7.2 闭包
7.2.1 闭包与变量
- 副作用:闭包只能取得包含函数中任何变量的最后一个值
function createFunctions(){
var result = new Array();
for(var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var funs = createFunctions();
funs[0](); //10
funs[1](); //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;
}
var funs = createFunctions();
funs[0](); //0
funs[1](); //1
7.2.2 关于this对象
-
但函数作为某个对象的方法调用时,this等于那个对象
-
不过,匿名函数的执行环境具有全局性,因此this通常指向window
-
外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象
var name = "The Window";
var obj = {
name: "My Obj",
getNameFunc: function(){
return function(){
return this.name;
};
}
}
console.log(obj.getNameFunc()()); //The Window
var name = "The Window";
var obj = {
name: "My Obj",
getNameFunc: function(){
var that = this;
return function(){
return that.name;
};
}
}
console.log(obj.getNameFunc()()); //My Obj
- this和arguments存在同样的问题
var name = "The Window";
var obj = {
name: "My Obj",
getName: function(){
return this.name;
}
}
obj.getName(); //My Obj
(obj.getName)(); //My Obj
(obj.getName = obj.getName)(); //The Window
7.2.3 内存泄漏
- IE9之前浏览器垃圾回收机制使用引用计数方式(有缺陷)
- 因此闭包在这些IE版本中,如果闭包的作用域链保存着一个HTMl元素,则该元素无法被销毁,导致内存一直被占用,成为内存泄漏
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
console.log(element.id);
};
}
// 创建了一个element元素时间处理程序的闭包,而闭包创建了循环引用,导致无法减少element的引用数。只要匿名函数存在,element的引用数至少都是1,内存永远不会被回收
- 改进
function addignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
console.log(id);
};
element = null;
}
7.3 模仿块级作用域
function outputNumbers(count){
for(var i=0; i < count; i++){
console.log('->',i);
}
console.log('-->',i);
}
outputNumbers(3);
//-> 0
//-> 1
//-> 2
//--> 3
- 由来
var somefunction = function(){
//块级作用域
};
somefunction();
//是否可以使用函数值直接取代函数名,结论:不行
function(){
//块级作用域
}(); //error 因为函数声明后不能加括号
//将函数声明转换成函数表达式
(function(){
//块级作用域
})();
function outputNumbers(count){
(function(){
for(var i=0; i < count; i++){
console.log('->',i);
}
})();
console.log('-->',i); //error
}
- 优点:
- 限制全局作用域中添加过多的变量和函数,适合大型应用程序
- 减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕就会销毁其作用域链
(function(){
var now = new Date();
if(now.getMonth() == 1 && now.getDate() == 8){
alert("新年快乐!!");
}
})();
7.4 私有变量
-
严格来讲,JS没有私有成员,倒是有私有变量
-
私有变量:任何在函数中定义的变量,包括函数参数、局部变量和函数内定义的其他函数
-
特权方法:有权访问私有变量和私有函数的公有方法
- 创建方式1:在构造函数中定义
function MeOjb(){ //私有变量和私有函数 var privateVariable = 10; function privateFunction(){ return false; } //特权方法 this.publicMethod = function(){ privateVariable++; return privateFunction(); }; }
function Person(name){ this.name = name; this.getName = function(){ return this.name; }; this.setName = function(value){ this.name = value; }; } var person = new Person("Tom"); console.log(person.getName()); //Tom person.setName("Bob"); console.log(person.getName()); //Bob
- 缺点:对于每个实例,都会创建同样一组新方法,可以使用静态私有变量解决
7.4.1 静态私有变量
- 创建方式2:在私有作用域中定义私有变量或函数
(function(){
var name = "";
Person = function(value){
name = value;
}; //Person会变为全局变量
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function(value){
name = value;
};
})();
var person1 = new Person("Tom");
console.log(person1.getName()); //Tom
person1.setName("Bob");
console.log(person1.getName()); //Bob
var person2 = new Person("Kevin");
console.log(person1.getName()); //Kevin
console.log(person2.getName()); //Kevin
- 不足:使用闭包和私有变量 会延长作用域链查找 影响查找速度
7.4.2 模块模式
- 单例:只有一个实例的对象
- 模块模式:为单例创建私有变量和特权方法
- 在需要对单例进行某些初始化,同时有需要维护其私有变量时非常有用
//例子:项目组件注册 初始化应用程序集信息
var application = function(){
//私有变量和函数
var components = new Array();
//初始化
components.push({
version: '0.0.1'
})
//公共
return {
getComponentCount: function(){
return components.length;
},
registComponentCount: function(component){
if(typeof component == "objec"){
component.push(component);
}
}
}
}();
7.4.3 增强的模块模式
- 模块模式的增强版
- 适合那些单例必须是某种类型的实例
var singletom = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//插件对象
var obj = new CustomType();
//添加特权/公有属性和方法
obj.publicProperty = true;
obj.publicMethod = function(){
privateVariable++;
return privateFunction();
}
//返回这个对象
return obj;
}();
小结
- 递归函数使用arguments.callee来递归调用自身,不要使用函数名
- 当在函数内部创建了其他函数,就创建了闭包。闭包有权访问包含函数内部的所有变量