热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深切明白JavaScript系列4:马上挪用的函数表达式

媒介人人学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形式的信息,请继承接见下面列出的网站:

  1. ECMA-262-3 in detail. Chapter 5\. Functions. – Dmitry A. Soshnikov

  2. Functions and function scope – Mozilla Developer Network

  3. Named function expressions – Juriy “kangax” Zaytsev

  4. 深切邃晓Javascript系列3:周全剖析Module形式 – hiyangguo

  5. Closures explained with Javascript – Nick Morgan

关于本文

本文转自TOM大叔的深切邃晓Javascript系列

【深切邃晓Javascript系列】文章,包含了原创,翻译,转载,整顿等各范例文章,原文是TOM大叔的一个异常不错的专题,现将其重新整顿宣布。感谢大叔。假如你以为本文不错,请帮助点个引荐,支撑一把,感激涕零。

更多优异文章迎接关注我的专栏


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
手机用户2502891303_279
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有