js学习笔记-第五章函数表达式-闭包和模仿块级作用域
//闭包的概念
//闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,
//示例
function createComparisonFunction(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;
}
};
}
//这两行代码访问了外部函数中的变量 propertyName。
function compare(value1, value2) {
if(value1 < value2) {
return -1;
} else if(value1 > value2) {
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
//先定义了 compare()函数
//又在全局作用域中调用了它
//当调用 compare()时,会创建一个包含 arguments、value1 和 value2 的活动对象。全局执行环境的变量对象(包含 result和 compare)在 compare()执行环境的作用域链中则处于第二位
//闭包的基本使用:一是可以读取函数内部的 变量,二是让变量的值始终保持在内存中
function test() {
var name = "zhangsan";
}
//如何获取函数内部的name
/*function test() {
var name = "zhangsan";
//通过返回一个函数
return function() {
return name;
//console.log(name);
}
}
var fun = test();
fun(); //zhangsan
console.log(fun()); //zhangsan;
*/
/*由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过
度使用闭包可能会导致内存占用过多,我们建议读者只在绝对必要时再考虑使用闭
包。虽然像 V8 等优化后的 JavaScript 引擎会尝试回收被闭包占用的内存,但请大家
还是要慎重使用闭包*/
/*作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最
后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰地说
明这个问题
*/
function createFunctions() {
var result = new Array();
for(var i = 0; i < 10; i++) {
result[i] = function() {
return i;
};
}
return result;
}
/*因为每个函数的作用域链中
都保存着 createFunctions() 函数的活动对象,所以它们引用的都是同一个变量 i 。 当
createFunctions()函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量
对象,所以在每个函数内部 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;
}
//this对象:this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象
//匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window①。
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); //"The Window"(在非严格模式下)
//如何让它取到object中的name呢?
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this; //保存该对象的引用
return function() {
//输出该对象name
return that.name;
};
}
};
console.log(object.getNameFunc()()); //"My Object"(在非严格模式下)
//内存泄漏
//如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。来看下面的例子
function assignHandler() {
var element = document.getElementById("someElement");
element.onclick = function() {
alert(element.id);
};
}
/*由于匿名函数保存了一个对 assignHandler()的活动对象的引用,因此
就会导致无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是 1,因此它所
占用的内存就永远不会被回收。*/
//这个问题可以通过稍微改写一下代码来解决
function assignHandler() {
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function() {
alert(id);
};
element = null;
}
/*必须要记住:闭包会引用包含函数
的整个活动对象,而其中包含着 element。即使闭包不直接引用 element,包含函数的活动对象中也
仍然会保存一个引用。因此,有必要把 element 变量设置为 null。这样就能够解除对 DOM 对象的引
用,顺利地减少其引用数,确保正常回收其占用的内存*/
//模仿块级作用域
//JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的
/*function outputNumbers(count) {
for(var i = 0; i < count; i++) {
console.log(i);
}
//循环结束后也可取到i的值
console.log(i); //计数
}
outputNumbers(2) //2*/
//即使像下面这样错误地重新声明同一个变量,也不会改变它的值
function outputNumbers(count) {
for(var i = 0; i < count; i++) {
console.log(i);
}
var i; //重新声明变量
console.log(i); //计数:count
}
//outputNumbers(2)
/*JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不
见(不过,它会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。
用作块级作用域(通常称为私有作用域)的匿名函数的语法如下所示。*/
/*(function() {
//这里是块级作用域
console.log(11);
})();*/
/*以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个
函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。*/
//理解匿名函数的调用
//一下示例
/*var count = 5;
outputNumbers(count);*/
/*里初始化了变量 count,将其值设置为 5。当然,这里的变量是没有必要的,因为可以把值直接
传给函数。为了让代码更简洁,我们在调用函数时用 5 来代替变量 count,如下所示。*/
//outputNumbers(5);
//变量只不过是值的另一种表现形式
/*var someFunction = function() {
//这里是块级作用域
};
someFunction();*/
/*定义了一个函数,然后立即调用了它。定义函数的方式是创建一个匿名函数,并把匿名
函数赋值给变量 someFunction。而调用函数的方式是在函数名称后面添加一对圆括号,即
someFunction()*/
//那在这里是不是也可以用函数的值直接取代函数名呢?
/*function() {
//这里是块级作用域
}(); //出错!;
*/
/*是因为 JavaScript 将 function 关键字当作一个函数声明的开始,而函
数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。要将函数声明转换成函数表达式,
只要像下面这样给它加上一对圆括号即可*/
//一是作为私有作用域(由于JavaScript中没有块级作用域的概念),而是匿名函数的自我调用
(function() {
//这里是块级作用域
})();
//无论在什么地方,只要临时需要一些变量,就可以使用私有作用域,例如:
function outputNumbers(count) {
(function() {
for(var i = 0; i < count; i++) {
console.log(i);
}
})();
console.log(i); //导致一个错误!
}
//因此,变量 i 只能在循环中使用,使用后即被销毁
//而在私有作用域中能够访问变量 count,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量
/*在一个由很多开发人员共同参与的大型
应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员既可
以使用自己的变量,又不必担心搞乱全局作用域。*/
//outputNumbers(2)
(function() {
var now = new Date();
if(now.getMonth() == 0 && now.getDate() == 1) {
alert("Happy new year!");
}
})();
//这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
</script>
</head>
<body>
<div id="someElement">someElement</div>
</body>
- JavaScript高级程序设计之函数表达式之模仿块级作用域第7.3讲笔记
- 【JavaScript学习】函数表达式:模仿块级作用域
- javascirpt如何模仿块级作用域(js高程笔记)
- JS学习笔记3_函数表达式
- JS学习笔记——正则表达式在replace()函数中的小技巧
- javascirpt怎样模仿块级作用域(js高程笔记)
- 【学习笔记】深入理解js原型和闭包(13)——【作用域】和【上下文环境】
- 你不知道的JS之作用域和闭包(三)函数 vs. 块级作用域
- 【学习笔记】深入理解js原型和闭包(12)——简介【作用域】
- JS学习之路——初探函数、作用域、递归、预编译与闭包及立即执行函数
- js学习笔记-函数表达式
- JavaScript学习笔记之函数表达式与闭包
- Javascript学习笔记3 函数表达式 闭包
- 【学习笔记六】 - js中 创建对象的模式与继承 及 js中实现块级作用域和函数私有变量 《js高程》6-7笔记
- js学习之函数表达式及闭包
- JavaScript高级程序设计(第2版) 学习笔记:(二)js函数作用域与闭包
- 【学习笔记】深入理解js原型和闭包(14)——从【自由变量】到【作用域链】
- JS 模仿块级作用域
- JS 正则表达式的相关方法(正则学习笔记1)
- 【js学习笔记-047】-- 函数的实参和形参