最近我一直在阅读很多Javascript,我注意到整个文件都像以下代码一样被包裹在.js文件中以进行导入。
(function() {
...
code
...
})();
为什么要这样做,而不是简单地使用构造函数集合?
最近我一直在阅读很多Javascript,我注意到整个文件都像以下代码一样被包裹在.js文件中以进行导入。
(function() {
...
code
...
})();
为什么要这样做,而不是简单地使用构造函数集合?
通常会使用命名空间(见后文)来控制成员函数和/或变量的可见性,类似于对象定义。技术上称之为立即调用函数表达式(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)可以避免污染全局作用域(因此这也意味着您无法多次调用该函数,因为它没有名称,但由于此函数只需要执行一次,所以这真的不是问题)。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本身)。这样做没有迫切需要,但有一些优点:
;(function(jQ) { ... code ... })(jQuery);
这样,如果有人在他们的脚本中漏掉了一个分号,它不会破坏你的代码,特别是如果你计划将你的脚本与其他脚本进行压缩和连接。 - Taras Alenin(function (context) { ..... })(this)
,从而允许你将任何你喜欢的内容附加到父级上下文中,从而使其暴露出来。 - Callum Liningtonthis.myLib = myLib
,define(myLib)
,module.exports = myLib
或这些类型的组合结尾。 - Roberto Stelling简单来说,这种技术旨在将代码包装在一个函数范围内。
它有助于减少以下的可能性:
它不能检测文档是否就绪,它不是document.onload
或window.onload
之类的东西。
它通常被称为立即调用的函数表达式(IIFE)
或自执行匿名函数
。
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所说)。换句话说,这些变量在函数外部不可见/无法访问。 查看实时演示。最后,之前发布的代码也可以按以下方式完成:
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',因为我们想要的只是立即执行它。”
如果你回到基础知识,你会发现:
表达式
:某个值的评估方式。例如:3+11/x
语句
:执行某些操作的代码行,但它不会评估为一个值。例如: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 =
之外的其他函数表达式?”
答案是:“你可以”,实际上有许多方法可以做到这一点:添加+
、!
、-
或者可能在一对括号中包装(按照约定现在已经完成),还有更多我相信。例如:
(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
因此,一旦将相关修改添加到曾经是“备选代码”的代码中,我们回到与“Code Explained”示例中使用的完全相同的代码
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
了解更多关于 表达式 vs 语句
的内容:
有一件事情你可能会好奇,那就是“如果你没有在函数内部 '正确' 定义变量,而只是进行了简单的赋值操作,会发生什么?”
(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
:无论是数字、对象还是函数,甚至在全局范围内。这使得代码更简单。
注意:
块级作用域
(更新:在ES6中添加了块级作用域局部变量)函数作用域
和全局作用域
(在浏览器环境中为window
作用域)阅读更多关于Javascript作用域
的内容:
一旦你理解了这个 IIFE
的概念,它会引导你进入 模块模式
,通常是通过利用这个 IIFE 模式来完成的。祝玩得开心 :)
在浏览器中,JavaScript 只有两个有效的作用域:函数作用域和全局作用域。
如果变量不在函数作用域中,它就在全局作用域中。而全局变量通常是不好的,因此这是一种将库的变量保持私有的构造方式。
这被称为闭包。它基本上将函数内的代码封装起来,以防止其他库干扰。它类似于在编译语言中创建命名空间。
例如,假设我写了以下代码:
(function() {
var x = 2;
// do stuff with x
})();
现在其他库无法访问我创建用于自己库中的变量 x
。
(function(){ ... return { publicProp1: 'blah' }; })();
。显然这并不完全等同于命名空间,但这种思考方式可能会有所帮助。 - Joel 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;
})()
}
除了将变量保持本地化外,其中一个非常方便的用法是在使用全局变量编写库时,您可以为其提供较短的变量名称以在库内使用。它经常用于编写jQuery插件,因为jQuery允许您禁用指向jQuery的$变量,使用jQuery.noConflict()。如果被禁用,您的代码仍然可以使用$而不会中断,只需执行如下操作:
(function($) { ...code...})(jQuery);
(function() {
'use strict';
//Your code from here
})();
从https://requirejs.org/docs/whyamd.html提供被接受答案的示例:
(function () {
var $ = this.jQuery;
this.myExample = function () {};
}());
this
来导出它们,其中this
是浏览器的window
对象。