好的,由于ECMA6尚未到来,函数大约是在JS中创建作用域的最佳方法。如果您将某种变量声明包装在IIFE(立即调用的函数表达式)中,则该变量将不会被全局创建。 函数声明也是如此。
如果您被赋予了清除脚本中所有全局变量的看似艰巨的任务,那么您只需要在一个简单的
(function(){ / *这里放置脚本* /}());
中包装整个脚本,就不会创建任何全局变量,除非它们是隐含的全局变量,但这只是一种懒惰的解决方法。 这种模式更加强大。
我已经更详细地解释了IIFE的使用方式,这里,这里和这里
基本的JS函数调用生命周期工作方式如下:
f();//call function
||
====> inside function, some vars are created, along with the arguments object
These reside in an internal scope object
==> function returns, scope object (all vars and args) are GC'ed
与JS中的所有对象一样,只要该对象不再被引用,该对象就会被标记为垃圾回收( GC)。但请考虑以下情况:
var foo = (function()
{
var localFoo = {bar:undefined};
return function(get, set)
{
if (set === undefined)
{
return localFoo[get];
}
return (localFoo[get] = set);
}
}());
当IIFE返回时,foo被赋予了另一个函数作为其返回值。现在localFoo是在IIFE的作用域中声明的,没有直接访问该对象的方法。乍一看,您可能希望localFoo被垃圾回收。
但请注意,被返回并分配给foo的函数仍然引用该对象,因此它不能被垃圾回收。换句话说:作用域对象超出函数调用的生命周期,从而创建了闭包。
因此,localFoo对象只有在变量foo超出范围或重新分配另一个值且所有对返回的函数的引用都丢失时才会被垃圾回收。
请查看其中一个链接答案(带有图表的答案),在该答案中有一篇文章的链接,我从中窃取了所使用的图像。如果这还没有解决问题,那么这应该为您澄清问题。
IIFE可以返回空,但仍然公开其作用域:
var foo = {};
(function(obj)
{
var localFoo = {};
obj.property = 'I am set in a different scope';
obj.getLocal = function()
{
return localFoo;
};
}(foo));
这个IIFE没有返回任何值(隐含的undefined),但是console.log(foo.getLocal())会输出空对象字面量。foo本身也将被分配property。但等等,我可以做得更好。假设foo已经通过上面的代码一次:
var bar = foo.getLocal();
bar.newProperty = 'I was added using the bar reference';
bar.getLocal = function()
{
return this;
};
console.log(foo.getLocal().newProperty === bar.newProperty);
console.log(bar ==== foo.getLocal());
console.log(bar.getLocal() === foo.getLocal().getLocal());
这段文字的翻译如下:
这会记录什么?实际上,它将一次又一次地记录true
。在JS中,对象永远不会被复制,它们的引用被复制,但对象始终是相同的。在某些范围内更改它一次,这些更改将在所有引用之间共享(逻辑上)。
这只是为了向您展示闭包可能一开始很难理解,但这也显示了它们可以有多么强大:您可以通过各种IIFE传递对象,每次设置一个新方法,该方法具有访问其自己唯一作用域的权限,其他方法无法访问。
注意
对于JS引擎来说,闭包并不容易进行垃圾回收,但是最近,这已经不再是一个大问题。
同时花些时间搜索这些术语:
JavaScript中的模块模式
使用它的一些原因,JavaScript中的闭包
第二个链接,JavaScript函数作用域
第一个链接,JavaScript函数上下文
可怕的this
引用。IIFE也可以是命名函数,但你只能在该函数的作用域内引用该函数。
(function init (obj)
{
var localFoo = {};
obj.property = 'I am set in a different scope';
obj.getLocal = function()
{
return localFoo;
};
if (!this.wrap)
{
obj.wrap = init;
}
}(foo));
var fooLocal = foo.getLocal();
foo.wrap(fooLocal);
console.log(fooLocal.getLocal());
console.log(init);
这只是一个基本示例,说明您如何使用闭包创建包装器对象...