写在前面
注:这个系列是本人对js学问的一些梳理,个中不少内容来自书本:Javascript高等程序设计第三版和JavaScript威望指南第六版,谢谢它们的作者和译者。有发明什么题目的,迎接留言指出。
1.实行环境
- 实行环境简称“环境”,定义了变量或函数有权接见的其他数据。每一个实行环境都有一个与之关联的变量对象,环境中定义的一切变量和函数都保留在这个对象中。
- 全局实行环境是最外围的一个实行环境。在Web浏览器中,全局实行环境被认为是window对象,因而一切全局变量和函数都是作为window对象的属性和要领建立的。
- 某个实行环境中的一切代码实行终了,环境被烧毁,保留在个中的一切变量和函数定义也随之烧毁。
- 实行流:每一个函数都有本身的实行环境,当实行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数实行以后,栈将其环境弹出,把控制权返回给之前的实行环境。
- 代码在一个环境中实行时,会建立变量对象的一个作用域链(scope chain),用处是保证对实行环境有权接见的一切变量和函数的有序接见。作用域链的前端,一向都是当前实行的代码地点环境的变量对象。假如这个环境是函数,则将其运动对象作为变量对象。运动对象在最开始时只包括arguments 对象。作用域链的下一个变量对象来自包括环境,而再下一个变量对象则来自下一个包括环境。如许,一向延续到全局实行环境;全局实行环境的变量对象一向都是作用域链中的末了一个对象:
var color = "blue";
function changeColor() {
if(color == "blue"){
color = "red";
}else{
color = "blue";
}
}
changeColor();
console.log("color is:" + color);//red
例子中,函数changeColor()的作用域链包括两个对象:它本身的变量对象(个中定义着arguments对象)和全局环境的变量对象。能够在函数内部接见变量 color,就是因为能够在作用域链中找到它。
var color = "blue";
function changeColor() {
var anotherColor = "red";
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//这里能够接见color、anotherColor和tempColor
}
//这里能够接见color和anotherColor,但不能接见tempColor
swapColors();
}
// 这里只能接见color
changeColor();
以上共触及3个实行环境:全局环境、changeColor()的部分环境和swapColors()的部分环境。明显,内部环境能够经由历程作用域链接见一切的外部环境,但外部环境不能接见内部环境中的任何变量和函数。
2.闭包
闭包是指有权接见另一个函数作用域中的变量的函数。建立闭包的罕见体式格局,就是在一个函数内部建立另一个函数。
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;
}
}
}
//建立函数
var compareNames = createComparisonFunction("name");
//挪用函数
var result = compareNames({name:'jaychou'},{name:'xiaoming'});
//消除对匿名函数的援用(开释内存)
compareNames = null;
以上历程:
1.定义函数内部的函数时,会将它的包括函数的运动对象添加到它的作用域链中。在此例中,在匿名函数被返回后,它的作用域链初始化为包括createComparisonFunction()函数的运动对象和全局变量对象。
2.createComparisonFunction()函数在实行终了后,其运动对象也不会被烧毁,因为匿名函数的作用域链仍然在援用这个运动对象,效果就是只是createComparisonFunction()的实行环境的作用域链会被烧毁,其运动对象会留在内存中。
3.直到代码中将匿名函数置为null开释内存后,createComparisonFunction()的运动对象才会被烧毁。
注重:因为闭包会照顾包括它的函数的作用域,所以会比其他函数占用更多的内存,过量运用闭包会致使内存占用过量,所以在很有必要时才斟酌运用闭包。
3.闭包的最罕见题目
function createFunctions() {
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function () {
return i;
};
}
return result;
}
console.log(createFunctions()[4]());//会打印10
数组内里的每一个函数都是打印10,因为每一个函数的作用域链中都保留着createFunctions()函数的运动对象,所以他们援用的都是同一个变量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;
}
console.log(createFunctions()[4]());//会打印4
没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将马上实行该匿名函数的效果赋给函数,函数参数是按值通报的,所以会把变量i的当前值复制给参数num。
在这个马上实行函数内里建立并返回了一个接见num的闭包。这个闭包被返回时都正确包括了对应的包括环境的运动对象的num值。
4.闭包的罕见作用
- 给组织函数建立私用变量和私有函数,并定义特权要领;
- 建立单例,在函数末了返回公用要领
等等
整体而言,闭包关于我们明白实行环境,明白作用域链很有协助,但寻常假如不是很有必要就不要用,占用内存比较多。