作者:手机用户2502891303_279 | 来源:互联网 | 2023-06-09 18:44
媒介人人学JavaScript的时刻,常常碰到自实行匿名函数的代码,本日我们重要就来想想说一下自实行。在细致相识这个之前,我们来谈相识一下“自实行”这个叫法,本文对这个功用的叫法也
媒介
人人学Javascript的时刻,常常碰到自实行匿名函数的代码,本日我们重要就来想想说一下自实行。
在细致相识这个之前,我们来谈相识一下“自实行”这个叫法,本文对这个功用的叫法也不一定完整对,重假如看个人怎样邃晓,因为有的人说马上挪用,有的人说自动实行,所以你完整能够根据你本身的邃晓来取一个名字,不过我听许多人都叫它为“自实行”,但作者背面说了许多,来压服人人称谓为“马上挪用的函数表达式”。
本文英文原文地点:http://benalman.com/news/2010/11/immediately-invoked-function-expression/
什么是自实行?
在Javascript里,任何function在实行的时刻都邑建立一个实行上下文,因为为function声明的变量和function有能够只在该function内部,这个上下文,在挪用function的时刻,供应了一种简朴的体式格局来建立自在变量或私有子function。
// 因为该function里返回了别的一个function,个中这个function能够接见自在变量i
// 一切说,这个内部的function实际上是有权限能够挪用内部的对象。
function makeCounter() {
// 只能在makeCounter内部接见i
var i = 0;
return function () {
console.log(++i);
};
}
// 注重,counter和counter2是差别的实例,离别有本身范围内的i。
var counter = makeCounter();
counter(); // logs: 1
counter(); // logs: 2
var counter2 = makeCounter();
counter2(); // logs: 1
counter2(); // logs: 2
alert(i); // 援用毛病:i没有defind(因为i是存在于makeCounter内部)。
许多状况下,我们不须要makeCounter多个实例,以至某些case下,我们也不须要显现的返回值,OK,往下看。
题目的中心
当你声明相似function foo(){}或var foo = function(){}函数的时刻,经由过程在背面加个括弧就可以够完成自实行,比方foo(),看代码:
// 因为想下面第一个声明的function能够在背面加一个括弧()就可以够本身实行了,比方foo(),
// 因为foo仅仅是function() { /* code */ }这个表达式的一个援用
var foo = function(){ /* code */ }
// ...是否是意味着背面加个括弧都能够自动实行?
function(){ /* code */ }(); // SyntaxError: Unexpected token (
//
上述代码,假如运转,第2个代码会失足,因为在剖析器剖析全局的function或许function内部function关键字的时刻,默许是以为function声明,而不是function表达式,假如你不显现通知编译器,它默许会声明成一个缺乏名字的function,而且抛出一个语法毛病信息,因为function声明须要一个名字。
函数,括弧,语法毛病(SyntaxError)
风趣的是,即使你为上面谁人毛病的代码加上一个名字,他也会提醒语法毛病,只不过和上面的缘由不一样。在一个表达式背面加上括号(),该表达式会马上实行,然则在一个语句背面加上括号(),是完整不一样的意义,他的只是分组操纵符。
// 下面这个function在语法上是没题目的,然则依旧只是一个语句
// 加上括号()今后依旧会报错,因为分组操纵符须要包含表达式
function foo(){ /* code */ }(); // SyntaxError: Unexpected token )
// 然则假如你在括弧()里传入一个表达式,将不会有异常抛出
// 然则foo函数依旧不会实行
function foo(){ /* code */ }(1);
// 因为它完整等价于下面这个代码,一个function声明背面,又声清楚明了一个毫无关系的表达式:
function foo(){ /* code */ }(1);
你能够接见ECMA-262-3 in detail. Chapter 5\. Functions 猎取进一步的信息。
自实行函数表达式
要处置惩罚上述题目,异常简朴,我们只须要用大括弧将代码的代码悉数括住就好了,因为Javascript里括弧()内里不能包含语句,所以在这一点上,剖析器在剖析function关键字的时刻,会将响应的代码剖析成function表达式,而不是function声明。不邃晓的,能够看深切邃晓Javascript系列2:揭秘定名函数表达式中的函数表达式和函数声明
// 下面2个括弧()都邑马上实行
(function () { /* code */ } ()); // 引荐运用这个
(function () { /* code */ })(); // 然则这个也是能够用的
// 因为括弧()和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 */ } () // 假如须要通报参数,只须要加上括弧()
上面所说的括弧是消弭歧义的,实在压根就没必要,因为括弧原本内部原本希冀的就是函数表达式,然则我们依旧用它,重假如为了轻易开发人员浏览,当你让这些已自动实行的表达式赋值给一个变量的时刻,我们看到开首有括弧(,很快就可以邃晓,而不须要将代码拉到末了看看到底有没有加括弧。
用闭包保留状况
和一般function实行的时刻传参数一样,自实行的函数表达式也能够这么传参,因为闭包直接能够援用传入的这些参数,应用这些被lock住的传入参数,自实行函数表达式能够有效地保留状况。
下面是毛病的运用:
var elems = document.getElementsByTagName('a');
for (var i = 0; i elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am link #' + i);
}, 'false');
}
因为变量i
从来就没背locked住。相反,当轮回实行今后,我们在点击的时刻i取得数值,所以说不管点击哪一个衔接,终究显现的都是I am link #10(假如有10个a元素的话)
下面是准确的运用:
var elems = document.getElementsByTagName('a');
for (var i = 0; i (function (lockedInIndex) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am link #' + lockedInIndex);
}, 'false');
})(i);
}
因为在自实行函数表达式闭包内部i的值作为locked的索引存在,在轮回实行终了今后,只管末了i的值变成了a元素总数(比方10)但闭包内部的lockedInIndex值是没有转变,因为他已实行终了了所以当点击衔接的时刻,结果是准确的。
或许你也能够像如许运用:
var elems = document.getElementsByTagName('a');
for (var i = 0; i elems[i].addEventListener('click', (function (lockedInIndex) {
return function (e) {
e.preventDefault();
alert('I am link #' + lockedInIndex);
};
})(i), 'false');
}
上面的代码在处置惩罚函数那边运用自实行函数表达式,而不是在addEventListener外部,如许也能够到达locked的结果,然则前面的代码更具有可读性。
实在,前面两个例子里的lockedInIndex变量,也能够换成i,因为和表面的i不在一个作用于,所以不会出现题目,这也是匿名函数+闭包的威力。
自实行匿名函数和马上实行的函数表达式区分
在这篇文章中,我们一向叫自实行函数,确实的说是自实行匿名函数(Self-executing anonymous function)
,但英文原文作者一向建议运用马上挪用的函数表达式(Immediately-Invoked Function Expression)
这一称号,作者又举了一堆例子来诠释,好吧,我们来看看:
// 这是一个自实行的函数,函数内部实行本身,递归
function foo() { foo(); }
// 这是一个自实行的匿名函数,因为没有函数称号
// 必需运用arguments.callee属性来实行本身
var foo = function () { arguments.callee(); };
// 这能够也是一个自实行的匿名函数,仅仅是foo函数名援用它本身
// 假如你将foo转变成别的的,你将获得一个自实行(used-to-self-execute)的匿名函数
var foo = function () { foo(); };
// 有些人叫这个是自实行的匿名函数(即使它不是),因为它没有挪用本身,它只是马上实行罢了。
(function () { /* code */ } ());
// 为函数表达式增加一个函数称号,能够轻易Debug
// 注重:一旦定名为函数增加了函数名,这个函数就不再是匿名的了
(function foo() { /* code */ } ());
// 马上挪用的函数表达式(IIFE)也能够自实行,不过能够不经常使用罢了
(function () { arguments.callee();} ());
(function foo() { foo(); } ());
愿望这里的一些例子,能够让人人邃晓,什么叫自实行,什么叫马上挪用。
注:arguments.callee在ECMAScript 5 strict mode里被烧毁了,所以在这个形式下,实际上是不能用的。
Module形式
在讲到这个马上挪用的函数表达式的时刻,我又想起来了Module形式,假如你还不熟习这个形式,我们先来看看代码:
// 建立一个马上挪用的匿名函数表达式
// 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只存在于闭包)
关于更多Module形式的引见,请接见我的上一篇文章:深切邃晓Javascript系列2:揭秘定名函数表达式
更多浏览
愿望上面的一些例子,能让你对马上挪用的函数表达(也就是我们所说的自实行函数)有所相识,假如你想相识更多关于function和Module形式的信息,请继承接见下面列出的网站:
ECMA-262-3 in detail. Chapter 5\. Functions. – Dmitry A. Soshnikov
Functions and function scope – Mozilla Developer Network
Named function expressions – Juriy “kangax” Zaytsev
深切邃晓Javascript系列3:周全剖析Module形式 – hiyangguo
Closures explained with Javascript – Nick Morgan
关于本文
本文转自TOM大叔的深切邃晓Javascript系列
【深切邃晓Javascript系列】文章,包含了原创,翻译,转载,整顿等各范例文章,原文是TOM大叔的一个异常不错的专题,现将其重新整顿宣布。感谢大叔。假如你以为本文不错,请帮助点个引荐,支撑一把,感激涕零。
更多优异文章迎接关注我的专栏