【学习笔记二】- JS 闭包相关的简单函数

写在前面:

本人自学前端不久,博客的【学习笔记】既不算原创也不算转载,都是自己通过各个网站、博客、书籍、视频课程等学习的笔记,以及做的一些测试。

下面的代码是在sublime text3上编辑在nodejs上运行过的。


【闭包】

function add(){
	var i=0;
	return function(){console.log(i++);};
}
var f=add();
f();//0
f();//1
f=null;//解除对匿名函数的引用,通知垃圾回收,释放内存

闭包是指有权访问另一个函数作用域中的变量的函数;

闭包由函数和与其相关的引用环境组合而成;

闭包允许函数访问其引用环境中的变量(又称自由变量);

广义上说,所以js函数都可以称为闭包,因为js函数在创建时保存了当前的词法环境;


像上面这个add函数,它返回了一个匿名函数,在这个匿名函数中访问了外部函数中的变量i,这个匿名函数被返回后在外部被调用,仍然能访问add()中的变量i,因为内部匿名函数的作用域链中包含了外部add()的作用域。


后台每个执行环境都有一个表示变量的对象——变量对象。作用域链本质上是一个指向变量对象的指针列表。

add()函数执行完之后,其执行环境的作用域链会被销毁,但活动对象仍然存在于内存,直到匿名函数被销毁才被销毁。



//闭包内函数的调用方法
function a(){
	var x=10;
	return function fun(){                        
		x++;
		console.log(x)
	}
}
a()();//11
//第一个括号返回函数fun,第二个括号执行fun
function b(){
	var x=10;
	return function fun(){                        
		x++;
		console.log(x);
		}
}
var closure=b();
closure();//11
//b返回函数fun给closure
function c(){
	var x=10;
	(function fun(){                        
		x++;
		console.log(x);
		})();
	}
console.log(c());
//11
//undefined
//如果一个函数没有return,则返回undefined
//fun函数在c函数内部直接执行

闭包的应用:保存变量现场、封装


//保存变量现场
var result1=[];
function foo1(){
	for (var i= 0;i<3;i++){ 
		result1[i]=function(){ 
			console.log(i); 
			}
}; 
foo1();
result1[0](); // 3
result1[1](); // 3
result1[2](); // 3
//每次循环返回给result的是一个函数,函数在循环中并没有立即执行,console.log(i)中的i没有直接赋值
//因为每个函数的作用域链中都保存着foo1的活动对象,所以它们引用的都是同一个变量i
//最终foo()执行完,i的值为3
//再依次执行数组中的函数,返回的都是3
console.log('---------------------------');
var result2=[];
function foo2(){
    for (var i= 0;i<3;i++){
        result2[i]=(function(x){
            return function(){console.log(x)};
        })(i);
    }
};
foo2();
result2[0](); // 0
result2[1](); // 1
result2[2](); // 2
//在循环体中,(function(j){...})(i)在赋给result时直接执行,并传入参数i
//函数获得参数命名为x,返回function(){console.log(x)};
//所以数组中储存的即函数:function(){console.log(x)};
//且传入的i已保存在x中
//最后依次执行数组中的函数,返回的是已存储在函数内部的x
console.log('---------------------------');
var result3=[];
function foo3(){
	for (let i= 0;i<3;i++){ 
		result3[i]=function(){ 
			console.log(i); 
			}
}; 
foo3();
result3[0](); // 0
result3[1](); // 1
result3[2](); // 2
//const、var、let
//const定义的变量不可修改,必须初始化
//var定义的变量可修改,不初始化时默认为undefined
//let是块级作用域,函数内部使用let对函数外无影响
//封装——信息隐藏
var observer=(function(){
	var observerList=[];
	return {
		add:function(obj){
			observerList.push(obj);
		},
		empty:function(){
			observerList=[];
		},
		getCount:function(){
			return observerList.length;
		},
		get:function(){
			return observerList;
		}
	}
})();


在闭包中使用this对象

//在闭包中使用this可能导致的问题
var name='window';
var obj1={
	name:'obj',
	getName:function(){
		return function(){
			console.log(this.name);
		};
	}
};
obj1.getName()();//在浏览器中运行应为window,因为我在sublime的nodejs插件上运行所以出来的结果是undefined
//每个函数在被调用时都会自动取得两个特殊变量:this和参数数组arguments
//内部函数在搜索这连个变量时,只会搜索其活动对象

var name='window';
var obj2={
	name:'obj',
	getName:function(){
		var that=this;
		return function(){
			console.log(that.name);
		};
	}
};
obj2.getName()();//obj
//把外部作用域中的this对象保存在一个闭包能够访问的变量里(内部函数活动对象->外部函数活动对象->全局变量对象)

var name='window';
var obj3={
	name:'obj',
	getName:function(){
		console.log(this.name);
	}
};
obj3.getName();//obj
(obj3.getName)();//obj
(obj3.getName=obj3.getName)();//在浏览器中运行应为window,因为我在sublime的nodejs插件上运行所以出来的结果是undefined
//第三条先赋值再执行,赋的是函数本身


JS中的垃圾回收机制:

1.如果js中的一个对象不再被引用,那么这个对象将被回收;

2.如果两个对象相互引用,且不再被其他对象引用,这两个对象也会被回收;


内存泄漏


IE8及以前的DOM和BOM对象使用C++以COM对象形式实现,存在循环引用的问题

var element=document.getElementById('someElement');
var obj=new Object();
obj.element=element;
element.obj=obj;
//存在循环引用
//消除方法
obj.element=null;
element.obj=null;
//优化内存占用最佳方式是只保存必要数据,一旦不在使用就设为null解除引用,让其脱离执行环境,以便垃圾收集器下次循环时将其回收


//当闭包的作用域链中存在html元素,该元素无法被销毁
function assignHandler(){
	var element=document.getElementById('someElement');
	element.onclick=function(){
		alert(element.id)
	};
}
//该闭包包含一个循环引用。匿名函数保存对assignHandler()活动对象的引用,
//只有匿名函数存在,element的引用数至少为1,所占内存永远不会被回收。
function assignHandler(){
	var element=document.getElementById('someElement');
	var id=element.id;//保存到一个变量中,消除了循环引用
	element.onclick=function(){
		alert(id)
	};
	element=null;
	//即使闭包不直接引用element,assignHandler()的活动对象中仍保存一个引用,设为null解除引用
}




猜你喜欢

转载自blog.csdn.net/jiuto_crx/article/details/77585194