让学习“上瘾”,成为更好的自己!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>闭包</title>
<script>
/*
闭包:有权访问另一个函数作用域中变量的函数
创建闭包的常见方式:在一个函数内部创建另一个函数
有关如何创建作用域链以及作用域链有什么作用的细节,对彻底理解闭包至关重要!!!
理解如下:(结合下边例程)
(1) 全局环境的变量对象始终存在,而函数的执行环境对应的局部变量对象只在函数执行过程中存在
(2) 当某一个函数第一次被调用时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建执行环境作用域链
然后,使用this,arguments和其他命名参数的值来初始化函数的活动对象
(3) 无论什么时候,在函数中访问一个变量,都会从作用域链中搜索具有相应名字的变量
(4) 作用域链的本质:一个指向变量对象的指针列表,只引用但不实际包含变量对象!!
(5) 闭包:在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到他的作用域链中!!
*/
function compare(value1, value2){
if(value1 < value2){
return -1;
} else if(value1 > value2){
return 1;
}else{
return 0;
}
}
/*
创建函数compare() : 会创建一个预先包含在全局变量对象的作用域链,该链保存在[[Scope]]属性中
=
=
+
调用函数compare():会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建执行环境作用域链
然后有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端
*/
function createComparionFunction(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 result = compare(2,43);
// 创建函数
// var compareNames = createComparionFunction('name'); // compareNames()函数就是一个闭包!!
// 调用函数
// var result = compareNames({name:'Kai'},{name:'Lice'});
// 解除对匿名函数的引用(以便释放内存)
// compareNames = null;
// 闭包:在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到他的作用域链中!!
// 即createComparionFunction()函数内部定义的匿名函数的作用域链中,会包含外部函数createComparionFunction()的活动对象
// 匿名函数从createComparionFunction()中返回时,他的作用域链被初始化为包含createComparionFunction()函数的活动对象和全局变量对象
// createComparionFunction()函数执行完毕后,其执行环境的作用域链会被销毁,但由于匿名函数的作用域链在引用这个活动对象,所以,createComparionFunction()函数的活动对象仍然在内存中
// 直到匿名函数也被销毁后,createComparionFunction()函数的活动对象才会被销毁
// 绝对必要时在考虑使用闭包!!
// 1,闭包与变量
/*
闭包只能取得包含函数中任何变量(比如下边的递增变量i)的最后一个值
闭包所保存的是整个变量对象,而不是某个特殊的变量
【共性】 把需要返回的值赋给一个变量保存,然后在返回该值【参照以下例程】
*/
// function outer(){
// var i = 12,
// j = 21,
// c = 34;
// return function(){
// ++ i;
// return i+j+c;
// }
// }
// console.log(outer()());
// 目的:每个函数返回自己的索引值
// function createFunctions(){
// var result = new Array();
// for(var i=0;i<10;i ++){
// result[i] = function(){ // 函数只是定义在这里,所以不要认为程序读到这里每次的i都会被赋给函数!!
// return i;
// // 每个函数都会返回10
// // 原因:每个函数的作用域中都保存着createFunctions()函数的活动对象,所以这些函数引用的都是同一个变量i
// // 当createFunctions()函数返回后,变量i的值为10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部的i的值都是10
// };
// }
// return result;
// }
// 不要被上边的程序影响,数组中每个函数能访问到的变量i一定是在createFunctions()函数返回后访问createFunctions()作用域变量对象的变量i
// for(var i=0;i < 10;i++){
// console.log(createFunctions()[i]()); // 都是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; // 返回一个函数数组
}
// 此时,每个函数都会返回各自不同的索引值了!!
// 没有直接把闭包赋值给数组,而是定义一个匿名函数,并将立即执行该匿名函数的结果赋给数组
// for(var i=0;i < 10;i++){
// console.log(createFunctions()[i]());
// }
// 2,关于this对象
// (1)匿名函数的执行环境具有全局性,因此其this对象通常指向window
// var name = 'The window';
// var object = { //
// name:'my Object',
// getNameFunc: function(){
// return function(){
// return this.name;
// };
// }
// }
// console.log(object.getNameFunc()()); // 'The window'
// 每次函数被调用时,其活动对象都会自动取得两个特殊的变量:this and arguments
// 内部函数在搜索这两个变量时,只会搜索到内部函数活动对象为止,永远不可能直接访问到外部函数中的this and arguments对象
// 把外部作用域中的this对象保存在一个闭包能够访问到的变量中,就可以让闭包访问该对象了(如下)
// var name = 'The window';
// var object = { // 外部作用域
// name:'my Object',
// getNameFunc: function(){
// var that = this; // 区别处1
// return function(){
// return that.name; // 区别处2
// }
// }
// }
// console.log(object.getNameFunc()); // 'my Object'
//(2) 细微差别导致this对象改变!!
var name = 'The window';
var object = {
name: 'my Object',
getName: function(){
return this.name
}
};
// console.log(object.getName()); // my Object
// console.log((object.getName)()); // my Object
// console.log((object.getName = object.getName)()); // The window
// 先赋值,然后调用赋值后的结果 --> 因为这个赋值表达式的值是函数本身,所以this的值不能得到维持
// 3,内存泄漏
// IE 中存在一些问题:如果闭包的作用域链中保存着一个HTML元素,那么意味着该元素将无法被销毁!
function assignHandler(){
var element = document.getElementById('someElement');
element.onclick = function(){
console.log(element.id);
};
}
function assignHandler(){
var element = document.getElementById('someElement');
var id = element.id;
element.onclick = function(){
console.log(element.id);
};
element = null;
}
</script>
</head>
<body>
</body>
</html>