JavaScript中的马上实行函数

注:此文只在明白马上实行函数,不在所谓原创,文中大批援用阮一峰的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://segmentfault.com/a/1190000003902899
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞