ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScriptの即時実行関数に対する詳細な回答

JavaScriptの即時実行関数に対する詳細な回答

亚连
リリース: 2018-05-21 12:01:33
オリジナル
1140 人が閲覧しました

JavaScriptの即時実行関数モードを使用すると、関数を定義した後すぐに実行できます。JavaScriptの即時実行関数を詳しく分析してみましょう

それは何ですかJavaScriptでは、それぞれの関数。呼び出されると、新しい実行コンテキストが作成されます。関数内で定義された変数および関数は、外部ではなく内部的にアクセスされる唯一の変数であるため、関数によって提供されるコンテキストは、関数の呼び出し時にプライベート変数を作成する非常に簡単な方法を提供します。

function makeCounter() {
  var i = 0;
  return function(){
    console.log(++i);
  };  
}

//记住:`counter`和`counter2`都有他们自己的变量 `i`

var counter = makeCounter();
counter();//1
counter();//2

var counter2 = makeCounter();
counter2();//1
counter2();//2

i;//ReferenceError: i is not defined(它只存在于makeCounter里)
ログイン後にコピー

多くの場合、複数の累積値を返すために makeWhatever のような関数は必要なく、単一の値を取得するために一度だけ呼び出すことができます。また、戻り値を明示的に知る必要さえない場合もあります。

その核心 さて、この function foo(){} のような関数を定義するか、var foo = function(){} のような関数を定義するかにかかわらず、それを呼び出すときは、次のように、その後に一対のかっこを追加する必要があります。ふー()。

//向下面这样定义的函数可以通过在函数名后加一对括号进行调用,像这样`foo()`,
//因为foo相对于函数表达式`function(){/* code */}`只是一个引用变量

var foo = function(){/* code */}

//那这可以说明函数表达式可以通过在其后加上一对括号自己调用自己吗?

function(){ /* code */}(); //SyntaxError: Unexpected token (
ログイン後にコピー

ご覧のとおり、ここでエラーが発生しました。関数を呼び出すための関数の後にかっこが出現すると、グローバル環境でそのような関数キーワードに遭遇した場合でも、ローカル環境で遭遇した場合でも、デフォルトでは、それを関数式ではなく関数宣言として扱います。かっこにそれが式であることを明示的に伝えると、関数宣言には名前が必要なため、名前のない関数宣言として扱われ、エラーがスローされます。

質問 1: ここで質問について考えます。var foo = function(){console.log(1)}() のように関数を直接呼び出すこともできますか。答えは「はい」です。
質問 2: 同様に、このような関数宣言を最後に括弧を付けて直接呼び出した場合はどうなるでしょうか?という質問も考えることができます。以下の答えをご覧ください。

関数、括弧、エラー 興味深いことに、関数に名前を付け、その後に括弧を 1 対置くと、同じエラーがスローされますが、今回は別の理由で発生します。関数式の後に括弧が置かれている場合は、これが呼び出される関数であることを示し、宣言の後に括弧が置かれている場合は、前の関数宣言から完全に分離されていることを意味します。このとき、括弧は単なる単純なものです。括弧 (演算の優先順位を制御するために使用される括弧)。

//然而函数声明语法上是无效的,它仍然是一个声明,紧跟着的圆括号是无效的,因为圆括号里需要包含表达式

function foo(){ /* code */ }();//SyntaxError: Unexpected token

//现在,你把一个表达式放在圆括号里,没有抛出错误...,但是函数也并没有执行,因为:

function foo(){/* code */}(1)

//它等同于如下,一个函数声明跟着一个完全没有关系的表达式:

function foo(){/* code */}
(1);
ログイン後にコピー

即時実行関数式 (IIFE)幸いなことに、構文エラーを修正するのは簡単です。最も一般的で受け入れられている方法は、関数宣言を括弧で囲んでパーサーに関数式を表現するように指示することです。これは、JavaScript では括弧に宣言を含めることができないためです。このため、括弧で関数が囲まれており、関数キーワードに遭遇すると、それを関数宣言ではなく関数式として解析することが認識されます。ここでの括弧は、関数に遭遇したとき、つまり、上記の括弧とは異なる動作をすることを理解するように注意してください。

匿名関数の末尾に括弧があり、関数を呼び出したい場合、関数宣言としてデフォルトで関数が使用されます。
括弧で関数を囲むと、デフォルトで関数宣言ではなく式として関数が解析されます。

//这两种模式都可以被用来立即调用一个函数表达式,利用函数的执行来创造私有变量

(function(){/* code */}());//Crockford recommends this one,括号内的表达式代表函数立即调用表达式
(function(){/* code */})();//But this one works just as well,括号内的表达式代表函数表达式

// Because the point of the parens or coercing operators is to disambiguate
// between function expressions and function declarations, they can be
// omitted when the parser already expects an expression (but please see the
// "important note" below).

var i = function(){return 10;}();
true && function(){/*code*/}();
0,function(){}();

//如果你并不关心返回值,或者让你的代码尽可能的易读,你可以通过在你的函数前面带上一个一元操作符来存储字节

!function(){/* code */}();
~function(){/* code */}();
-function(){/* code */}();
+function(){/* code */}();

// Here's another variation, from @kuvos - I'm not sure of the performance
// implications, if any, of using the `new` keyword, but it works.
// http://twitter.com/kuvos/status/18209252090847232

new function(){ /* code */ }
new function(){ /* code */ }() // Only need parens if passing arguments
ログイン後にコピー

括弧に関する重要な注意事項関数式を追加のあいまいな括弧で囲む場合、場合によっては括弧が必要ない場合があります(このときの括弧はすでに式として表現されているため)。関数式を呼び出すために使用されます。

このような括弧は、関数式がすぐに呼び出され、変数には関数自体ではなく関数の結果が格納されることを示します。これが非常に長い関数式の場合、コードを読んでいる人が関数が呼び出されているかどうかを確認するためにページの一番下までスクロールする手間を省くことができます。

原則として、明確で明確なコードを記述するときは、JavaScript がエラーをスローしないようにする必要があります。また、他の開発者がエラー WTFError をスローしないようにすることも必要です

クロージャの状態を保存してください like when 関数 引数は名前で呼び出されたときに渡され、関数式がすぐに呼び出されたときに引数が渡されます。関数内で定義された関数は外部関数から渡されたパラメーターと変数を使用できるため (この関係はクロージャーと呼ばれます)、すぐに呼び出される関数式を使用して値をロックし、現在の状態を効果的に保存できます。

// 它的运行原理可能并不像你想的那样,因为`i`的值从来没有被锁定。
// 相反的,每个链接,当被点击时(循环已经被很好的执行完毕),因此会弹出所有元素的总数,
// 因为这是 `i` 此时的真实值。

var elems = document.getElementsByTagName('a');
for(var i = 0;i < elems.length; i++ ) {
  elems[i].addEventListener(&#39;click&#39;,function(e){
    e.preventDefault();
    alert(&#39;I am link #&#39; + i)
    },false);
}

// 而像下面这样改写,便可以了,因为在IIFE里,`i`值被锁定在了`lockedInIndex`里。
// 在循环结束执行时,尽管`i`值的数值是所有元素的总和,但每一次函数表达式被调用时,
// IIFE 里的 `lockedInIndex` 值都是`i`传给它的值,所以当链接被点击时,正确的值被弹出。

var elems = document.getElementsByTagName(&#39;a&#39;);
for(var i = 0;i < elems.length;i++) {
  (function(lockedInIndex){
    elems[i].addEventListener(&#39;click&#39;,function(e){
      e.preventDefault();
      alert(&#39;I am link #&#39; + lockedInIndex);
      },false)
  })(i);
}

//你同样可以像下面这样使用IIFE,仅仅只用括号包括点击处理函数,并不包含整个`addEventListener`。
//无论用哪种方式,这两个例子都可以用IIFE将值锁定,不过我发现前面一个例子更可读

var elems = document.getElementsByTagName( &#39;a&#39; );

for ( var i = 0; i < elems.length; i++ ) {
  elems[ i ].addEventListener( &#39;click&#39;, (function( lockedInIndex ){
    return function(e){
      e.preventDefault();
      alert( &#39;I am link #&#39; + lockedInIndex );
    };
    })( i ),false);
  }
ログイン後にコピー

覚えておいてください、これらの最後の 2 つの例では、lockedIndex は問題なく i にアクセスできますが、関数のパラメーターとして別の名前付き識別子を使用すると、概念を説明しやすくなります。

関数をすぐに実行する最も重要な利点の 1 つは、関数が名前なしまたは匿名であっても、識別子を使用せずに関数式をすぐに呼び出すことができ、現在使用されている変数を汚さずにクロージャーをすぐに呼び出すことができることです。 。

自己実行匿名関数 (「自己実行匿名関数」) の何が問題なのでしょうか?

你看到它已经被提到好几次了,但是它仍然不是那么清楚的被解释,我提议将术语改成"Immediately-Invoked Function Expression",或者,IIFE,如果你喜欢缩写的话。

什么是Immediately-Invoked Function Expression呢?它使一个被立即调用的函数表达式。就像引导你去调用的函数表达式。

我想Javascript社区的成员应该可以在他们的文章里或者陈述里接受术语,Immediately-Invoked Function Expression和 IIFE,因为我感觉这样更容易让这个概念被理解,并且术语"self-executing anonymous function"真的也不够精确。

//下面是个自执行函数,递归的调用自己本身

function foo(){foo();};

//这是一个自执行匿名函数。因为它没有标识符,它必须是使用`arguments.callee`属性来调用它自己

var foo = function(){arguments.callee();};

//这也许算是一个自执行匿名函数,但是仅仅当`foo`标识符作为它的引用时,如果你将它换成用`foo`来调用同样可行

var foo = function(){foo();};

//有些人像这样叫&#39;self-executing anonymous function&#39;下面的函数,即使它不是自执行的,因为它并没有调用它自己。然后,它只是被立即调用了而已。

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

//为函数表达式增加标识符(也就是说创造一个命名函数)对我们的调试会有很大帮助。一旦命名,函数将不再匿名。

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

//IIFEs同样也可以自执行,尽管,也许他不是最有用的模式

(function(){arguments.callee();}())
(function foo(){foo();}())

// One last thing to note: this will cause an error in BlackBerry 5, because
// inside a named function expression, that name is undefined. Awesome, huh?

(function foo(){ foo(); }());
ログイン後にコピー

希望上面的例子可以让你更加清楚的知道术语'self-executing'是有一些误导的,因为他并不是执行自己的函数,尽管函数已经被执行。同样的,匿名函数也没用必要特别指出,因为,Immediately Invoked Function Expression,既可以是命名函数也可以匿名函数。

最后:模块模式当我调用函数表达式时,如果我不至少一次的提醒我自己关于模块模式,我便很可能会忽略它。如果你并不属性 JavaScript 里的模块模式,它和我下面的例子很像,但是返回值用对象代替了函数。

var counter = (function(){
  var i = 0;
  return {
    get: function(){
      return i;
    },
    set: function(val){
      i = val;
    },
    increment: function(){
      return ++i;
    }
  }
  }());
  counter.get();//0
  counter.set(3);
  counter.increment();//4
  counter.increment();//5

  conuter.i;//undefined (`i` is not a property of the returned object)
  i;//ReferenceError: i is not defined (it only exists inside the closure)
ログイン後にコピー

模块模式方法不仅相当的厉害而且简单。非常少的代码,你可以有效的利用与方法和属性相关的命名,在一个对象里,组织全部的模块代码即最小化了全局变量的污染也创造了使用变量。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

JavaScript中各数制转换全面总结(图文教程)

原生JavaScript来实现对dom元素class的操作方法(图文教程)

JavaScript中的字符串连接问题(图文教程)

以上がJavaScriptの即時実行関数に対する詳細な回答の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート