最近,我已经阅读了很多Javascript,并且注意到整个文件都被包装成如下格式,要导入的.js文件中。
(function() {
...
code
...
})();
为什么这样做而不是使用一组简单的构造函数呢?
最近,我已经阅读了很多Javascript,并且注意到整个文件都被包装成如下格式,要导入的.js文件中。
(function() {
...
code
...
})();
为什么这样做而不是使用一组简单的构造函数呢?
Answers:
通常是到名称空间(请参阅下文)并控制成员函数和/或变量的可见性。可以将其视为对象定义。它的技术名称是立即调用函数表达式(IIFE)。jQuery插件通常是这样写的。
在Javascript中,您可以嵌套函数。因此,以下是合法的:
function outerFunction() {
function innerFunction() {
// code
}
}
现在您可以调用outerFunction()
,但是的可见性innerFunction()
仅限于的范围outerFunction()
,这意味着它是私有的outerFunction()
。它基本上遵循与Javascript中的变量相同的原理:
var globalVariable;
function someFunction() {
var localVariable;
}
相应地:
function globalFunction() {
var localFunction1 = function() {
//I'm anonymous! But localFunction1 is a reference to me!
};
function localFunction2() {
//I'm named!
}
}
在上述情况下,您可以globalFunction()
从任何地方拨打电话,但不能拨打localFunction1
或localFunction2
。
在编写时(function() { ... })()
,您正在做的是使第一组括号内的代码成为函数文字(意味着整个“对象”实际上是一个函数)。之后,您将自动调用()
刚刚定义的函数(最后一个)。因此,正如我前面提到的,此方法的主要优点是您可以拥有私有方法/函数和属性:
(function() {
var private_var;
function private_function() {
//code
}
})();
在第一个示例中,您将按globalFunction
名称显式调用以运行它。也就是说,您只globalFunction()
需要运行它即可。但是在上面的示例中,您不仅在定义一个函数;您可以一口气定义和调用它。这意味着当您的JavaScript文件被加载时,将立即执行它。当然,您可以这样做:
function globalFunction() {
// code
}
globalFunction();
除了一个显着的区别外,该行为基本上是相同的:您避免使用IIFE时污染全局范围(因此,这也意味着您不能多次调用该函数,因为它没有名称,但是由于该功能仅在确实不是问题后才可以执行)。
IIFE的整洁之处在于,您还可以在内部定义事物,并且只向外界公开想要的部分(例如,命名空间的示例,这样您就可以基本上创建自己的库/插件):
var myPlugin = (function() {
var private_var;
function private_function() {
}
return {
public_function1: function() {
},
public_function2: function() {
}
}
})()
现在您可以拨打电话myPlugin.public_function1()
,但无法访问private_function()
!因此非常类似于类定义。为了更好地理解这一点,我建议以下链接以供进一步阅读:
编辑
我忘了提。在最后()
,您可以在内部传递任何内容。例如,当您创建jQuery插件时,您传入jQuery
或$
类似如下:
(function(jQ) { ... code ... })(jQuery)
因此,您在这里要做的是定义一个带有一个参数的函数(称为jQ
,局部变量,仅对该函数已知)。那么你的自我调用函数,并传递一个参数(也叫jQuery
,但是这一个是从外面的世界,并以实际的jQuery自身的引用)。这样做并没有紧迫的需求,但是有一些优点:
早先,我描述了这些函数如何在启动时自动运行,但是如果它们自动运行,谁来传递参数?此技术假定您所需的所有参数都已定义为全局变量。因此,如果尚未将jQuery定义为全局变量,则此示例将不起作用。您可能会猜到,jquery.js在其初始化期间所做的一件事是定义了一个'jQuery'全局变量,以及它更为著名的'$'全局变量,这使该代码在包含jQuery之后可以工作。
;(function(jQ) { ... code ... })(jQuery);
这样,如果有人在脚本中遗漏了分号,则不会破坏您的脚本,尤其是如果您打算缩小脚本并将其与其他脚本连接时。
(function (context) { ..... })(this)
,然后允许您将所需的任何内容附加到父上下文,从而将其公开。
以最简单的形式,该技术旨在将代码包装在函数范围内。
它有助于减少以下情况的机会:
这不,当文档准备好检测-这是不是某种document.onload
也不window.onload
通常称为Immediately Invoked Function Expression (IIFE)
或Self Executing Anonymous Function
。
var someFunction = function(){ console.log('wagwan!'); };
(function() { /* function scope starts here */
console.log('start of IIFE');
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})(); /* function scope ends */
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
在上面的示例中,函数中定义的任何变量(即使用声明var
)将是“私有”的,并且只能在函数范围内访问(如Vivin Paliath所言)。换句话说,这些变量在函数外部不可见/不可访问。观看现场演示。
Javascript具有功能范围。“在函数外部定义的参数和变量在函数外部不可见,并且在函数内部任何位置定义的变量在函数内部均可见。” (摘自“ Javascript:好的部分”)。
最后,之前发布的代码也可以按如下方式进行:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
};
myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
有一天,可能有人认为“必须有一种避免命名“ myMainFunction”的方法,因为我们想要的只是立即执行它。”
如果您回到基础知识,就会发现:
expression
:评估价值的东西。即3+11/x
statement
:代码行正在执行某项操作,但不会求值。即if(){}
同样,函数表达式求值。结果(我想?)是它们可以立即被调用:
var italianSayinSomething = function(){ console.log('mamamia!'); }();
因此,我们更复杂的示例变为:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
}();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
下一步是“为什么var myMainFunction =
我们什至不使用它!!”。
答案很简单:尝试删除它,如下所示:
function(){ console.log('mamamia!'); }();
由于“函数声明不可调用”,因此无法使用。
诀窍是通过删除var myMainFunction =
将函数表达式转换为函数声明。有关更多详细信息,请参见“资源”中的链接。
下一个问题是“为什么我不能将其保留为函数表达式而不包含var myMainFunction =
?
答案是“您可以”,实际上您可以通过多种方式执行此操作:在一对括号中添加a +
,a !
,a -
或换行符(如现在所约定的那样),我相信还有更多。例如:
(function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.
要么
+function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console
要么
-function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console
因此,一旦将相关修改添加到曾经的“替代代码”中,我们将返回与“代码解释”示例中使用的代码完全相同的代码
var someFunction = function(){ console.log('wagwan!'); };
(function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
进一步了解Expressions vs Statements
:
一个人可能会想知道的一件事是:“当您在函数内部未正确定义变量时,会发生什么—即执行简单赋值呢?”
(function() {
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
myOtherFunction = function(){ /* oops, an assignment instead of a declaration */
console.log('haha. got ya!');
};
})();
myOtherFunction(); // reachable, hence works: see in the console
window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope
myFunction(); // unreachable, will throw an error, see in the console
基本上,如果为未在其当前作用域中声明的变量分配了一个值,则“将进行范围链查找,直到找到该变量或命中全局作用域(此时将创建全局作用域)”。
在浏览器环境(与服务器环境(如nodejs)相比)中,全局范围由window
对象定义。因此,我们可以做window.myOtherFunction()
。
我在该主题上的“良好实践”技巧是在定义任何东西时始终使用var
:无论是数字,对象还是函数,甚至是在全局范围内。这使代码更加简单。
注意:
block scope
(更新:在添加区块范围的局部变量ES6)。function scope
&global scope
(window
浏览器环境中的范围)进一步了解Javascript Scopes
:
一旦掌握了这个IIFE
概念,它就会引出module pattern
,这通常是通过利用这种IIFE模式来完成的。玩得开心 :)
浏览器中的JavaScript实际上仅具有两个有效范围:函数范围和全局范围。
如果变量不在函数范围内,则在全局范围内。而且全局变量通常是不好的,因此这是一种将库的变量保留给自己的构造。
这就是所谓的关闭。它基本上将代码密封在函数内部,以便其他库不会干扰它。这类似于以编译语言创建名称空间。
例。假设我写:
(function() {
var x = 2;
// do stuff with x
})();
现在,其他库无法访问x
我创建的要在我的库中使用的变量。
(function(){ ... return { publicProp1: 'blah' }; })();
。显然,它与命名空间并不是完全平行的,但是以这种方式思考可能会有所帮助。
您也可以将函数闭包用作较大表达式中的数据,就像这种确定浏览器对某些html5对象的支持的方法一样。
navigator.html5={
canvas: (function(){
var dc= document.createElement('canvas');
if(!dc.getContext) return 0;
var c= dc.getContext('2d');
return typeof c.fillText== 'function'? 2: 1;
})(),
localStorage: (function(){
return !!window.localStorage;
})(),
webworkers: (function(){
return !!window.Worker;
})(),
offline: (function(){
return !!window.applicationCache;
})()
}
我们还应该在范围函数中使用“ use strict”,以确保代码应在“ strict模式”下执行。示例代码如下所示
(function() {
'use strict';
//Your code from here
})();