我最近读了很多 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() { ... })()
,你正在做的是你在第一组括号内的代码是一个函数字面量(意味着整个“对象”实际上是一个函数)。之后,您将自行调用()
刚刚定义的函数(final)。因此,正如我之前提到的,它的主要优点是您可以拥有私有方法/函数和属性:
(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 后工作。
以最简单的形式,这种技术旨在将代码包装在函数范围内。
它有助于减少以下机会:
它不会检测文档何时准备就绪——它不是某种形式的,document.onload
也不是window.onload
它通常称为Immediately Invoked Function Expression (IIFE)
or 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
: 评估价值的东西。IE3+11/x
statement
: 代码行做某事但它没有评估为一个值。IEif(){}
类似地,函数表达式计算为一个值。一个后果(我假设?)是它们可以立即被调用:
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
添加了块作用域局部变量。)function scope
& global scope
(window
在浏览器环境中的作用域)阅读更多关于Javascript Scopes
:
一旦你得到这个IIFE
概念,它就会导致module pattern
,这通常是通过利用这个 IIFE 模式来完成的。玩得开心 :)
浏览器中的 Javascript 实际上只有几个有效范围:函数范围和全局范围。
如果变量不在函数范围内,则它在全局范围内。全局变量通常很糟糕,所以这是一个将库变量保留给自身的构造。
这叫做闭包。它基本上将代码密封在函数内部,以便其他库不会干扰它。这类似于在编译语言中创建命名空间。
例子。假设我写:
(function() {
var x = 2;
// do stuff with x
})();
现在其他库无法访问x
我创建的要在我的库中使用的变量。
您也可以在更大的表达式中使用函数闭包作为数据,就像在这种确定浏览器对某些 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;
})()
}
除了将变量保持在本地之外,一个非常方便的用途是在使用全局变量编写库时,您可以给它一个较短的变量名以在库中使用。它经常用于编写 jQuery 插件,因为 jQuery 允许您使用 jQuery.noConflict() 禁用指向 jQuery 的 $ 变量。如果它被禁用,您的代码仍然可以使用 $ 而不会中断,如果您这样做:
(function($) { ...code...})(jQuery);
我们还应该在作用域函数中使用“使用严格”来确保代码应该在“严格模式”下执行。示例代码如下所示
(function() {
'use strict';
//Your code from here
})();
为接受的答案提供一个示例,来自https://requirejs.org/docs/whyamd.html:
(function () {
var $ = this.jQuery;
this.myExample = function () {};
}());
代码表明我们可以:
this
,这是window
浏览器的对象。