网站手机网页如何做,电子商务系统网站开发总结,东莞市小程序定制开发丨网站建设,网站底部流程要想理解闭包#xff0c;应当先理解JavaScript的作用域和作用域链。 JavaScript有一个特性被称之为“声明提前#xff08;hoisting#xff09;”#xff0c;即JavaScript函数里声明的所有变量#xff08;但不涉及赋值#xff09;都被“提前”至函数体的顶部#xff0c;“… 要想理解闭包应当先理解JavaScript的作用域和作用域链。 JavaScript有一个特性被称之为“声明提前hoisting”即JavaScript函数里声明的所有变量但不涉及赋值都被“提前”至函数体的顶部“声明提前”这步操作是在JavaScript引擎的“预编译”时进行的是在代码开始运行之前看一下下面的例子 var name YY;
function getName(){console.log(name); //输出undefine而不是“YY”var name Crucify;console.log(name); //输出“Crucify”
} 首先局部变量定义了一个和全局变量相同名字的变量则在函数体内部局部变量遮盖了同名的全局变量然后在函数体内部变量name的声明被提前至函数体顶部但并没有赋值所以此时name是一个只被声明但并没有初始化的变量我们知道变量只进行声明但并不初始化则它的值为undefine所以第一行打印时undefine下一行开始为变量name进行赋值所以第二行打印的输出是我们所期望的。 当某个函数被调用时会创建一个执行环境及相应的作用域链。 执行环境execution context定义了变量或函数有权访问的其他数据决定了它们各自的行为。每个函数都有自己的执行环境。当执行流进入一个函数时函数的环境就会被推入一个环境栈中。而在函数执行之后栈将其环境弹出把控制权返回给之前的执行环境。当代码在一个环境中执行时会创建变量对象的一个作用域链scope chain。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。 作用域链本质上是一个指向变量对象的指针列表它只引用但不实际包含变量对象。当JavaScript需要查找变量x的值的时候这个过程称作“变量解析”variable resolution他会从列表之中的第一个对象开始查找直到最后一个对象如果某个对象有一个名为x的属性则会直接食用这个属性的值。如果作用域链上没有任何一个对象含有属性x那么就认为这段代码的作用域上不存在x并最终抛出一个引用错误异常。 所谓的“变量对象的指针列表”很好理解。在JavaScript的最顶层代码中也就是不包含在任何函数定义内的代码作用域链是由一个全局对象组成。在不包含嵌套的函数体内作用域链上有两个对象第一个是定义函数参数和局部变量的对象第二个是全局对象。在一个嵌套的函数体内作用域链上则至少有三个对象看下面的例子 var name YY;
function getName(){var name Crucify;function f(){return name; }return f();
} 函数f()的作用域链上有三个对象第一个是定义函数f()参数和局部变量的对象第二个是定义函数getName()参数和局部变量的对象第三个是全局对象。 每个环境都可以向上搜索作用域链以查询变量和函数名但任何环境都不能通过向下搜索作用域链而进入另一个执行环境即函数f()可以向上搜索函数getName()和全局对象中的属性但是全局对象不能向下搜索getName()和f()中的值。 而创建闭包的常见方式就是在一个函数内部创建另一个函数。闭包是指有权访问另一个函数作用域中的变量的函数。 一般来讲当函数执行完毕后局部活动对象就会被销毁内存中仅保存全局作用域全局执行环境的变量对象。但是闭包的情况有所不同因为在另一个函数内部定义的函数会将包含函数即外部函数的活动对象添加到它的作用域链中参考下面的代码 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 compare createComparisonFunction(name);
var result compare({ name: Nicholas }, { name: Greg }); 当createComparisonFunction()函数在执行完毕后其活动对象也不会被销毁因为匿名函数的作用域链仍然在引用这个活动对象。直到匿名函数被销毁后 createComparisonFunction()的活动对象才会被销毁 compare null; //解除对匿名函数的引用以便释放内存 由于闭包会携带包含它的函数的作用域因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多所以在绝对必要时再考虑使用闭包。 作用域链的这种配置机制引出了一个值得注意的副作用即闭包只能取得包含函数中任何变量的最后一个值。因为闭包所保存的是整个变量对象而不是某个特殊的变量 function createFunctions(){var result new Array();for (var i0; i 10; i){result[i] function(){return i;};}return result;
} 这个函数会返回一个函数数组且每个函数都返回 10。 在闭包中使用 this 对象也可能会导致一些问题。我们知道 this 对象是在运行时基于函数的执行环境绑定的而匿名函数的执行环境具有全局性因此其 this 对象通常指向 window在通过 call()或 apply()改变函数执行环境的情况下 this 就会指向其他对象看下面的例子 var name The Window;
var object {name : My Object,getNameFunc : function(){return function(){return this.name;};}
};
alert(object.getNameFunc()()); //The Window在非严格模式下 把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里就可以让闭包访问该对象了如下所示 var name The Window;
var object {name : My Object,getNameFunc : function(){var that this;return function(){return that.name;};}
};
alert(object.getNameFunc()()); //My Object arguments 也存在同样的问题。如果想访问作用域中的 arguments 对象必须将对该对象的引用保存到另一个闭包能够访问的变量中。 转载于:https://www.cnblogs.com/crucify-lee/p/4521273.html