我见过使用下面的专家来声明一个函数:
(function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
//etc
}());
e.g。 https://github.com/douglascrockford/JSON-js/blob/master/json.js
有人可以帮助我了解我们何时应该使用上述模式以及我们如何使用它?
感谢。
答案 0 :(得分:2)
好吧,既然ECMA6还没有到来,那么函数就是在JS中创建作用域的最佳方法。如果在IIFE(立即调用的函数表达式)中包装排序的变量声明,则不会全局创建该变量。功能声明也是如此。
如果给你一个看似令人生畏的清除所有全局变量脚本的艰巨任务,你需要做的就是将整个脚本包装成一个简单的(function(){/*script here*/}());
,并且不创建全局变量,以免它们是隐含的全局变量,但是这只是一个懒惰的修复。这种模式非常强大。
我已更详细地解释了IIFE的使用here,here和here
基本的JS函数调用live-cycle类似的工作:
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
成为GC
但是等一下,正在返回的函数(并分配给foo
仍然引用该对象,因此它不能 gc'ed。换句话说:范围对象超过了函数调用,并创建一个闭包。
然后,在变量localFoo
超出范围或重新分配另一个值并且对返回函数的所有引用都丢失之前,foo
对象将不会被GC。
看看其中一个链接的答案(带图表的答案),在那个答案中有一篇文章的链接,从那里我偷走了我用过的图像。如果还没有,那应该为你清理。
IIFE可以不返回任何内容,但无论如何都要公开其范围:
var foo = {};
(function(obj)
{
//obj references foo here
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());
//and so on
这个日志是什么?实际上,它会一次又一次地记录true
。对象永远不会在JS中复制,它们的引用被复制,但对象始终是相同的。在某个范围内更改一次,这些更改将在所有引用中共享(逻辑上)
这只是为了向你展示闭包可能很难让你的头脑开始,但这也表明他们可以的强大程度:你可以通过各种IIFE传递一个对象,每次设置一个新的有权访问其他方法无法获得的独特范围的方法。
注意强>
对于JS引擎而言,对于垃圾收集来说,闭门不是那么容易,但是lately, that's not that big of an issue anymore
也花些时间来谷歌这些条款:
this
reference IIFE也可以命名为函数,但是你可以引用该函数的唯一地方是函数的范围:
(function init (obj)
{
//obj references foo here
var localFoo = {};
obj.property = 'I am set in a different scope';
obj.getLocal = function()
{
return localFoo;
};
if (!this.wrap)
{//only assign wrap if wrap/init wasn't called from a wrapped object (IE foo)
obj.wrap = init;
}
}(foo));
var fooLocal = foo.getLocal();
//assign all but factory methods to fooLocal:
foo.wrap(fooLocal);
console.log(fooLocal.getLocal());//circular reference, though
console.log(init);//undefined, the function name is not global, because it's an expression
这只是一个基本的例子,说明如何使用闭包来创建包装器对象......
答案 1 :(得分:1)
以上模式称为立即函数。这个功能做了三件事: -
此代码的结果是一个表达式,它在单个语句中执行以下所有操作:
JS开发人员使用它来创建变量和函数,而不会污染全局空间,因为它为变量和函数创建了自己的私有范围。
在上面的例子中,函数f(){}在immediate函数的私有范围内,你不能在全局或窗口范围内调用这个函数。
答案 2 :(得分:1)
基于浏览器的JavaScript只有两个可用的范围:全局和功能。这意味着您创建的任何变量都在全局范围内,或者仅限于您当前所在函数的范围。
有时,通常在初始化期间,您需要一堆只需要一次的变量。将它们放在全局范围内是不合适的,你不需要特殊的功能来完成它。
输入,即时功能。这是一个定义然后立即调用的函数。这就是你在Crockford(和其他人的)代码中看到的。它可以是匿名的或命名的,但不会破坏避免污染全局范围的目的,因为函数的名称将是函数体的本地名称。
它提供了一个包含变量的范围,而不会留下一个函数。保持清洁。