资讯专栏INFORMATION COLUMN

JavaScript中的立即执行函数

W4n9Hu1 / 2584人阅读

摘要:函数名可以省略省略函数名的话该函数就成为了匿名函数被传入函数的参数的名称一个函数最多可以有个参数这些语句组成了函数的函数体。使用那我们通常为什么使用函数立即表达式呢,以及我如何使用呢通常情况下,只对匿名函数使用这种立即执行的函数表达式。

注:此文只在理解立即执行函数,不在所谓原创,文中大量引用阮一峰的JavaScript标准参考教程、MDN的JavaScript 参考文档和深入理解JavaScript系列(4):立即调用的函数表达式的内容。

描述

立即执行函数通常有下面两种写法:

(function(){ 
   ...
})();
(function(){ 
    ...
}());

在Javascript中,一对圆括号“()”是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。

这个写法和我们想象的写法不一样(知道的人当然已经习以为常)
很多人刚开始理解立即执行函数的时候,觉得应该是这样的:

function (){ ... }();

//或者

function fName(){ ... }();

然而事实却是这样:SyntaxError: Unexpected token (。这是为什么呢?

解释

要理解立即执行函数,需要先理解一些函数的基本概念:函数声明函数表达式,因为我们定义一个函数通常都是通过这两种方式

函数声明 (function 语句)

function name([param[, param[, ... param]]]) {
   statements
}

name:函数名;
param:被传入函数的参数的名称,一个函数最多可以有255个参数;
statements:这些语句组成了函数的函数体。

函数表达式 (function expression)

函数表达式和函数声明非常类似,它们甚至有相同的语法。

function [name]([param] [, param] [..., param]) {
   statements
}

name:函数名,可以省略,省略函数名的话,该函数就成为了匿名函数
param:被传入函数的参数的名称,一个函数最多可以有255个参数;
statements:这些语句组成了函数的函数体。

下面我们给出一些栗子说明:

// 声明函数f1
function f1() {
    console.log("f1");
}
// 通过()来调用此函数
f1();


//一个匿名函数的函数表达式,被赋值给变量f2:
var f2 = function() {
    console.log("f2");
}
//通过()来调用此函数
f2();


//一个命名为f3的函数的函数表达式(这里的函数名可以随意命名,可以不必和变量f3重名),被赋值给变量f3:
var f3 = function f3() {
    console.log("f2");
}
//通过()来调用此函数
f3();

上面所起的作用都差不多,但还是有一些差别
1、函数名和函数的变量存在着差别。函数名不能被改变,但函数的变量却能够被再分配。函数名只能在函数体内使用。倘若在函数体外使用函数名将会导致错误:

var y = function x() {};
alert(x); // throws an erro

2、函数声明定义的函数可以在它被声明之前使用

foo(); // alerts FOO!
function foo() {
   alert("FOO!");
}

但函数声明非常容易(经常是意外地)转换为函数表达式。当它不再是一个函数声明:

成为表达式的一部分

不再是函数或者script自身的“源元素” (source element)。在script或者函数体内“源元素”并非是内嵌的语句(statement)

var x = 0;               // source element
if (x == 0) {            // source element
   x = 10;               // 非source element
   function boo() {}     // 非 source element
}
function foo() {         // source element
   var y = 20;           // source element
   function bar() {}     // source element
   while (y == 10) {     // source element
      function blah() {} // 非 source element
      y++;               //非source element
   }
}

Examples:

// 函数声明
function foo() {}

// 函数表达式 
(function bar() {})

// 函数表达式
x = function hello() {}


if (x) {
   // 函数表达式
   function world() {}
}


// 函数声明
function a() {
   // 函数声明
   function b() {}
   if (0) {
      //函数表达式
      function c() {}
   }
}

现在我们来解释上面的SyntaxError: Unexpected token (
产生这个错误的原因是,Javascript引擎看到function关键字之后,认为后面跟的是函数定义语句,不应该以圆括号结尾。
解决方法就是让引擎知道,圆括号前面的部分不是函数定义语句,而是一个表达式,可以对此进行运算。所以应该这样写:

(function(){ /* code */ }()); 

// 或者

(function(){ /* code */ })();

这两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义,所以就避免了错误。这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。

注意,上面的两种写法的结尾,都必须加上分号。

推而广之,任何让解释器以表达式来处理函数定义的方法,都能产生同样的效果,比如下面三种写法。

var i = function(){ return 10; }();

true && function(){ /* code */ }();

0, function(){ /* code */ }();

甚至像这样写:

!function(){ /* code */ }();

~function(){ /* code */ }();

-function(){ /* code */ }();

+function(){ /* code */ }();

new关键字也能达到这个效果:

new function(){ /* code */ }

new function(){ /* code */ }() // 只有传递参数时,才需要最后那个圆括号。
使用

那我们通常为什么使用函数立即表达式呢,以及我如何使用呢?

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。
它的目的有两个:

一是不必为函数命名,避免了污染全局变量;

二是IIFE内部形成了一个多带带的作用域,可以封装一些外部无法读取的私有变量。

// 写法一
var tmp = newData;
processData(tmp);
storeData(tmp);

// 写法二
(function (){
  var tmp = newData;
  processData(tmp);
  storeData(tmp);
}());

上面代码中,写法二比写法一更好,因为完全避免了污染全局变量。

最后在举一个真实的栗子:在JavaScript的OOP中,我们可以通过IIFE来实现一个单例(关于单例的优化不再此处讨论)

// 创建一个立即调用的匿名函数表达式
// 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只存在于闭包)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91548.html

相关文章

  • 深入理解javascript中的立即执行函数(function(){…})()

    摘要:要理解立即执行函数,需要先理解一些函数的基本概念。函数表达式使用关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。 javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解。 ( functio...

    melody_lql 评论0 收藏0
  • JavaScript-立即调用函数表达式(IIFE)

    摘要:将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。组成这是一个被称为自执行匿名函数的设计模式,主要包含两部分。 一、函数声明&函数表达式 1.1 函数声明 (函数语句) showImg(https://segmentfault.com/img/bVbbqvT?w=278&h=166); (1)使用 function 关键字声明一个函数,再指定一个函数名,叫函数声明。...

    XUI 评论0 收藏0
  • JS之模仿块级作用域_立即执行函数

    摘要:模仿块级作用域立即执行函数前言最近在细读高级程序设计,对于我而言,中文版,书中很多地方一笔带过,所以用自己所理解的,尝试细致解读下。语法如下这里是块级作用域以上代码定义并立即调用了一个匿名函数。 模仿块级作用域-立即执行函数 前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方一笔带过,所以用自己所理解的,尝试细致解读下。如有纰漏或错误,会非常感谢您的指...

    NoraXie 评论0 收藏0
  • 进击的 JavaScript(五) 之 立即执行函数与闭包

    摘要:匿名函数是不能单独写的,所以就提不上立即执行了。六立即执行函数在闭包中的应用立即执行函数能配合闭包保存状态。来看下上节内容中闭包的例子现在,我们来利用立即执行函数来简化它第一个匿名函数执行完毕后,返回了第二个匿名函数。 前面的闭包中,提到与闭包相似的立即执行函数,感觉两者还是比较容易弄混吧,严格来说(因为犀牛书和高程对闭包的定义不同),立即执行函数并不属于闭包,它不满足闭包的三个条件。...

    vincent_xyb 评论0 收藏0
  • 深入理解JavaScript系列4:立即调用的函数表达式

    摘要:前言大家学的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行。其实,前面两个例子里的变量,也可以换成,因为和外面的不在一个作用于,所以不会出现问题,这也是匿名函数闭包的威力。 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行。 在详细了解这个之前,我们来谈了解一下自执行这个叫法,本文对这个功能的叫法也不一定完全对...

    roadtogeek 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<