이 글에서는 주로 JS에서 즉시 실행되는 함수에 대한 설명을 공유합니다. 소위 즉시 실행 함수(Immediately-Invoked Function Expression)는 함수 표현식 뒤에 ()를 추가하면 함수가 자체 실행될 수 있다는 의미입니다. 참고: 함수 선언 뒤에는 괄호()를 추가할 수 없습니다.
(function foo(){/* code */}()); //正确,推荐这样使用 (function foo(){/* code */})(); //正确,这样可以 var foo = function foo(){/* code */}(); //正确 function foo(){/* code */}(); //SyntaxError: Unexpected token ( // 但是如果你在括弧()里传入一个表达式,将不会有异常抛出 // 但是foo函数依然不会执行 function foo(){ /* code */ }( 1 ); // 因为它完全等价于下面这个代码,一个function声明后面,又声明了一个毫无关系的表达式: function foo(){ /* code */ } ( 1 ); // 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的 // 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了 // 不过,请注意下一章节的内容解释 var i = function () { return 10; } (); true && function () { /* code */ } (); 0, function () { /* code */ } (); // 如果你不在意返回值,或者不怕难以阅读 // 你甚至可以在function前面加一元操作符号 !function () { /* code */ } (); ~function () { /* code */ } (); -function () { /* code */ } (); +function () { /* code */ } (); // 还有一个情况,使用new关键字,也可以用,但我不确定它的效率 // http://twitter.com/kuvos/status/18209252090847232 new function () { /* code */ } new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()
IIFE는 IIFE가 반환한 함수에서 액세스할 수 있는 지역 변수에 대한 캡슐화된 범위를 제공할 수 있습니다. 이 접근 방식을 사용하면 함수가 IIFE의 어휘 범위 외부에서 실행되는 경우에도 함수가 이 지역 변수에 액세스할 수 있습니다. const uniqueId1 = function() {
let count1 = 0;
return function() {
++count1;
return count1;
};
};
uniqueId1(); //ƒ () {++count1;return count1;}
const uniqueId2 = function() {
let count = 0;
return count;
};
uniqueId2(); //0
uniqueId2(); //0
uniqueId2(); //0
const uniqueId3 = (function() {
let count3 = 0;
return function() {
++count3;
return count3;
};
})();
uniqueId3(); //1
uniqueId3(); //2
uniqueId3(); //3
uniqueId3(); //4
const uniqueId4 = function() {
let count4 = 0;
return (function() {
++count4;
return count4;
})();
};
uniqueId4(); //1
uniqueId4(); //1
uniqueId4(); //1
uniqueId4(); //1
count
. 이 변수는 IIEF에서 반환된 함수를 제외하고는 다른 사람이 읽거나 쓸 수 없습니다. 이는 통제된 방식으로만 수정될 수 있는 진정한 비공개 상태를 생성합니다. count
。除了从IIEF中返回的函数,别人无法读写该变量。这样就能创建真正的私有状态,它只能以受控的方式进行修改。
Module模式非常依赖IIFE这种机制。当使用IIFE来返回一个”封闭”一些本地变量来管理私有数据的函数时,let
和const
모듈 모드는 IIFE 메커니즘에 크게 의존합니다. IIFE를 사용하여 개인 데이터를 관리하기 위해 일부 지역 변수를 "포함"하는 함수를 반환하는 경우 let
const
// 创建一个立即调用的匿名函数表达式
// return一个变量,其中这个变量里包含你要暴露的东西
// 返回的这个变量将赋值给counter,而不是外面声明的function自身
var counter = (function () {
var i = 0;
return {
get: function () {
return i;
},
set: function (val) {
i = val;
},
increment: function () {
return ++i;
}
};
} ());
// counter是一个带有多个属性的对象,上面的代码对于属性的体现其实是方法
counter.get(); // 0
counter.set(3);
counter.increment(); // 4
counter.increment(); // 5
counter.i; // undefined 因为i不是返回对象的属性
i; // 引用错误: i 没有定义(因为i只存在于闭包)
일반 함수가 실행될 때 매개변수를 전달하는 것과 마찬가지로 자체 실행 함수 표현식도 이러한 방식으로 매개변수를 전달할 수 있습니다. 클로저는 전달된 매개변수를 직접 참조하고 이러한 매개변수를 사용할 수 있기 때문입니다. 잠긴 수신 매개변수에 대해 자체 실행 함수 표현식을 사용하면 상태를 효과적으로 저장할 수 있습니다.
// 这个代码是错误的,因为变量i从来就没背locked住 // 相反,当循环执行以后,我们在点击的时候i才获得数值 // 因为这个时候i操真正获得值 // 所以说无论点击那个连接,最终显示的都是I am link #10(如果有10个a元素的话) var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + i); }, 'false'); } // 这个是可以用的,因为他在自执行函数表达式闭包内部 // i的值作为locked的索引存在,在循环执行结束以后,尽管最后i的值变成了a元素总数(例如10) // 但闭包内部的lockedInIndex值是没有改变,因为他已经执行完毕了 // 所以当点击连接的时候,结果是正确的 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { (function (lockedInIndex) { elems[i].addEventListener('click', function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }, 'false'); })(i); } // 你也可以像下面这样应用,在处理函数那里使用自执行函数表达式 // 而不是在addEventListener外部 // 但是相对来说,上面的代码更具可读性 var elems = document.getElementsByTagName('a'); for (var i = 0; i < elems.length; i++) { elems[i].addEventListener('click', (function (lockedInIndex) { return function (e) { e.preventDefault(); alert('I am link #' + lockedInIndex); }; })(i), 'false'); }
for (var i = 0; i < 5; i++) { setTimeout(function(i) { console.log(i); }, i * 1000); }
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000); }
for (var i = 0; i < 5; i++) { setTimeout((function(i) { return function() { console.log(i); } })(i), i * 1000); }
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); }).bind(this,i), i * 1000); }
window.$ = function somethingElse() { // ... }; (function($) { // ... })(jQuery);
为了解决命名冲突问题,可以将一段代码封装在一个IIEF中,将一个全局变量(比如,jQuery)作为参数传入IIFE。在函数内部,就可以以一个任意的参数名(比如,$)来访问该参数值。
IIFE的这种特性可以用来优化代码,这种方式使代码能够被更有效的压缩。例如:
(function(window, document, undefined) { // ... })(window, document);
可以缩短函数的参数名为单个字母的标识符,更短标识符名会使文件的体积变得更小。
(function(w, d, u) { // ... })(window, document);
2、括号和分号的使用
我们知道,以下两种方法都是立即执行函数的写法:
// 下面2个括弧()都会立即执行 (function () { /* code */ } ()); (function () { /* code */ })();
注意这两种写法:匿名函数上面的写法都存在前后文;问题,所以需要注意的是匿名函数在压缩工具打包压缩后会出现上下文错误合并()的问题,例如第二种写法。如果下面的代码,未压缩之前是正常的,压缩后就不正常了,所以要严格上下文的;问题,而第一种就不会出现类似问题。
var a="bbb" (function(){ alert(1); })();
//var a = function(){} var a="bbb" (function(){ alert(1); }());
上述代码会报""bbb" is not a function"错误,若变量a是一函数,则会报"undefined is not a function",这是因为a变量或a函数会把他后面的匿名函数作为参数传入a中,这就很好的解释了为什么报前面提到的错误,这也很好地解释了为什么有人习惯在匿名函数之前添加;了,就是为了防止上文没有严格遵循javascript语法,漏掉;的问题。
相关推荐:
위 내용은 JS의 즉시 실행 기능 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!