在javaScript中,每一个函数被调用时,都会创建一个新的运行上下文。由于在一个函数里面定义的变量和函数仅仅能在里面訪问。在外面是不行的。上下文提供了一种非常easy的方法来创建私有性。

//makeCounter函数返回另外一个匿名函数,这个匿名函数可以訪问到“私有”变量i, 好像有一点“特权”性。
function makeCounter() {
// i仅仅能在makeCounter的里面被訪问到
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
i; // ReferenceError: i没有被定义(它仅仅存在于makeCounter的内部)

在非常多情况下,你不须要函数返回多个“实例”。你只须要一个单例。又或者在其它情况下,你可能连一个返回值都不须要。

问题的关键

如今你定义了像function foo(){}或var foo = function(){}这种函数,这样你得到了一个函数的标识符。你能够通过()来调用它。如foo()。

//像这样定义的函数能够通过后面的()来调用,如foo(),由于foo不过一个
//函数表达式'function(){/*...*/}'的引用
var foo = function(){ /* ... */ }
//只能过后面的() ,函数表达式就能被运行,这是不合常理的。
function(){ /* ... */ }(); // SyntaxError: Unexpected token (

正如你以上所看见的,出现了异常。

当JS解释器在全局作用域或函数的里面遇到functionkeyword时,会默觉得是一个函数声明,而不是一个函数表达式。假设你不确切地告诉解释器是一个表达式,它会觉得是一个没有名字的函数声明,所以导致语法错误异常,由于函数声明是须要名字的。

顺便说一下:函数、括号、语法错误

有趣的是,假设你为函数指定了一个名字,而且把括号放在它后面,出于不同的原因。解析器也会抛出一个语法错误。当括号放在表达式的的后面意味着表达式是一个将被调用的函数,括号放在声明的后面,会与前面的声明全然分开。只代表一个分组操作符(作为一种控制优先级的一种控制方式)。

<span style="font-weight: normal;">//这个函数在语法声明上是有效的。它不过一个声明,紧跟着的括号是无效的,
//由于分组操作符须要包含一个表达式
function foo(){ /* ... */ }(); // SyntaxError: Unexpected token )
//如今假设你在括号里放一个表达式,没有异常抛出
//可是函数仍然没有运行
function foo(){ /* ... */ }( 1 );
//事实上上面的相当于下面两个:一个是函数声明,一个是不相关的表达式
function foo(){ /* ... */ } ( 1 )</span>

想要了解很多其它。请訪问, ECMA-262-3
in detail. Chapter 5. Functions

马上调用表函数达式(IIFE)

幸运的是以上语法错误能够简单地被修复。最广泛的做法是告诉解析器函数表达式不过用括号括起来的,由于在javaScript中,括号不能包含声明。依据这一点,当解析器遇到functionkeyword时,会把它解析成一个表达式而不是函数声明。

//下面两种都能够被用来马上运行一个函数表达式,用函数的运行上下文去创建
//私有性
(function(){ /* ... */ }()); // Crockford推荐使用这个
(function(){ /* ... */ })(); // 可是这一个也工作得非常好 //从括号或强制操作符来看,消除了函数声明和函数表达式的歧义,
//当解析器已经预期是一个表达式时,他们也能够被忽略,请看下面的样例
var i = function(){ return 10; }();
true && function(){ /* ...*/ }();
0, function(){ /* ... */ }(); //假设你不关心函数的返回值或你的代码可读性
//你能够节省一个字节。通过在函数前面加一个一元操作符
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }(); //还有另外的版本号,但我不清楚它的实现性能会如何,用'new'keyword实现。
//运行是正常的
new function(){ /* code */ }
new function(){ /* code */ }()
关于括号须要注意的重要点

特别在“消除歧义”的括号(即包着函数表达式的括号),是不须要的(由于解析器已经觉得它是表达式了)。对于用在赋值中还是一个不错的想法。

这种括号非常明显暗示了函数表达式能够马上被调用,而且这个变量将包含函数的返回结果。不是函数本身。这能够免去一些人阅读你代码的麻烦,由于假设你的函数声明非常长时。可能要滚动到以下才知道你这个函数有没有被调用。

一般来说。想写出清晰的代码。从技术上说要阻止解析器抛出语法错误异常,编写清晰的代码也须要阻止其它开发人员把错误异常抛向你。

用闭包来保存状态

通过传递參数来调用马上函数。调用具有名称标识的函数并同一时候向它传递參数,这两者的调用方式是非常像的。

不论什么定义在一个函数里面的函数都能訪问它外面函数的所传递进来的參数以及外层函数的变量(这样的关系被称为闭包),一个马上调用函数表达式能够被用来“锁住”值和保存状态值。

假设你想学习很多其它关于闭包的内容,请阅读Closures explained with JavaScript.

以下例2和例3会依照你期望的运行,马上运行函数表达式有一个非常大的缺点是它是匿名或没有命名的。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="###">点我</a>
<a href="###">点我</a>
<a href="###">点我</a>
<a href="###">点我</a>
<a href="###">点我</a>
<script type="text/javascript">
var elems = document.getElementsByTagName( 'a' );
//例1
for ( var i = 0; i < elems.length; i++ ) {
console.log(i);
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
alert( 'I am link #' + i );
}, 'false' );
}
//运行结果:
//I am link #5
//I am link #5
//I am link #5
//I am link #5
//I am link #5 //例2
for ( var i = 0; i < elems.length; i++ ) {
(function( lockedInIndex ){
elems[ i ].addEventListener( 'click', function(e){
e.preventDefault();
alert( 'I am link #' + lockedInIndex );
}, 'false' );
})( i );
}
//运行结果:
//I am link #0
//I am link #1
//I am link #2
//I am link #3
//I am link #4 //例3
for ( var i = 0; i < elems.length; i++ ) {
elems[ i ].addEventListener( 'click', (function( lockedInIndex ){
return function(e){
e.preventDefault();
alert( 'I am link #' + lockedInIndex );
};
})( i ), 'false' );
} //运行结果:
//I am link #0
//I am link #1
//I am link #2
//I am link #3
//I am link #4
</script>
</body>
</html>

【javaScript基础】马上调用函数表达式的更多相关文章

  1. 理解JavaScript的立即调用函数表达式(IIFE)

    首先这是js的一种函数调用写法,叫立即执行函数表达式(IIFE,即immediately-invoked function expression).顾名思义IIFE可以让你的函数立即得到执行(废话). ...

  2. JavaScript 基础(七) 箭头函数 generator Date JSON

    ES6 标准新增了一种新的函数: Arrow Function(箭头函数). x => x *x 上面的箭头相当于: function (x){ return x*x; } 箭头函数相当于匿名函 ...

  3. JavaScript基础精华02(函数声明,arguments对象,匿名函数,JS面向对象基础)

    函数声明 JavaScript中声明函数的方式:(无需声明返回值类型) function add(i1, i2) {             return i1 + i2;//如果不写return返回 ...

  4. Javascript基础三(函数)

    函数第一节: 1.函数的概念及作用     函数是由事件驱动的或者当他被调用时可执行的可重复使用的代码块.   具备一点功能的代码段,代码段来实现具体的功能.要想实现一个函数的功能需要对函数进行调用. ...

  5. javascript笔记05:函数表达式和函数语句的区别

    1.首先是函数语句: myfunc(); function myfunc() { //执行一些语句 } 当函数语句被定义的时候,在一个脚本代码被优先考虑,因此,无论该函数是定义之前或者定义之后都可以被 ...

  6. JavaScript基础知识(函数)

    函数的基础 函数: 把实现相同功能的代码放到一个函数体中,当想实现这个功能时,直接执行这个函数即可:减少了的冗余:高内聚,低耦合--> 函数的封装: 函数:引用数据类型: var a = 10; ...

  7. javascript基础之回调函数

    简单来说,回调函数:也就是将要执行的函数. 回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A.我们就说函数A叫做回调函数.如果没有名称(函数表达式),就叫 ...

  8. Javascript基础学习(2)_表达式和运算符

    1.==和===的区别(!=和!==是相反的比较) 它们采用了同一性的两个不同定义.==是相等性,===是等同性. ①“===”进行两个值的比较 两个值的类型不同,就不相等 两个值是数字,并且值相同, ...

  9. JavaScript基础学习(六)—函数

    一.函数的定义 1.function语句形式 //1.function语句式 function test1(){ alert("I am test1"); } test1(); 2 ...

随机推荐

  1. C#:文件操作(待补充)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.I ...

  2. redis.windows.conf各项配置参数介绍 (九)

    # 默认情况下,redis不是在后台模式运行的,如果需要在后台进程运行,把该项的值更改为yes,默认为no daemonize:是否以后台daemon方式运行 # 如redis服务以后台进程运行的时候 ...

  3. linux学习规划

  4. 基于Vivado的嵌入式开发 ——PS+PL实践

    基于Vivado的嵌入式开发 ——PS走起 硬件平台:ZedBoard 开发工具:Vivado 2014.2 1.规划 废话不多说,依然是流水灯,这次是采用PS+PL实现. 功能依旧简单,目标是为了学 ...

  5. 【Android】14.0 第14章 内部存储与外部SD卡存储—本章示例主界面

    分类:C#.Android.VS2015: 创建日期:2016-02-27 一.简介 Android使用的文件系统是基于Linux的文件系统,在Android应用程序中,开发人员既可以建立和访问程序自 ...

  6. Experience on Namenode backup and restore --- checkpoint

    Hadoop version: Hadoop 2.2.0.2.0.6.0-0009 Well, We can do this by building Secondary Namenode, Check ...

  7. samba 文件和目录权限控制

    [laps_test]         comment = laps_test         path = /home/laps         browseable = yes         w ...

  8. ov5640 video capture时,vfe_v4l2.ko模块挂掉问题分析

    1.问题描述 在r16 tina平台,基于ov5640获取摄像头数据时,vfe_v4l2.ko模块挂掉. 2.配置信息 2.1上层应用设置的像素格式为V4L2_PIX_FMT_YUYV,分辨率为480 ...

  9. lua工具库penlight--04路径和目录

    使用路径 程序不应该依赖于奇葩的系统,这样你的代码会难以阅读和移植.最糟糕的是硬编码的路径, windows和Unix的路径分隔符正好相反.最好使用path.join,它可以帮助你解决这个问题. pl ...

  10. Unix系统编程():分散输入和集中输出(Scatter-Gather IO):readv和writev

    分散输入和集中输出(Scatter-Gather IO):readv和writev 请问这个v又代表什么? readv和writev系统调用分别实现了分散输入和集中输出的功能. #include< ...