文档章节

js中(function(){…})()立即执行函数写法理解(经过控制台测试后修改)

文文1
 文文1
发布于 2015/06/19 10:24
字数 1494
阅读 24523
收藏 48

( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达到函数定义后立即执行的目的,后来发现加括号的原因并非如此。要理解立即执行函数,需要先理解一些函数的基本概念。

 

函数声明、函数表达式、匿名函数

 

函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。

 

函数表达式 var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。

 

匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。

 

函数声明和函数表达式不同之处在于,一、Javascript引擎在解析javascript代码时会‘函数声明提升’(Function declaration Hoisting)当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。

实例:

fnName();
function fnName(){
    ...
}//正常,因为‘提升’了函数声明,函数调用可在函数声明之前
 
fnName();
var fnName=function(){
    ...
}//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后

var fnName=function(){
    alert('Hello World');
}();//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数

function fnName(){
    alert('Hello World');
}();//语法错误,Uncaught SyntaxError: Unexpected token ),这个函数会被js引擎解析为两部分:
    //1.函数声明 function fnName(){ alert('Hello World'); } 
    //2.分组表达式 () 但是第二部分作为分组表达式语法出现了错误,因为括号内没有表达式,把“()”改为“(1)”就不会报错
    //但是这么做没有任何意义,只不过不会报错,分组表达式请见:
    //分组中的函数表达式http://www.nowamagic.net/librarys/veda/detail/1664

function(){
    console.log('Hello World');    
}();//语法错误,Uncaught SyntaxError: Unexpected token (

 

在理解了一些函数基本概念后,回头看看( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。

 

实例:

function(a){
	    console.log(a);   //报错,Uncaught SyntaxError: Unexpected token (
}(12);
(function(a){
    console.log(a);   //firebug输出123,使用()运算符
})(123);
 
(function(a){
    console.log(a);   //firebug输出1234,使用()运算符
}(1234));
 
!function(a){
    console.log(a);   //firebug输出12345,使用!运算符
}(12345);
 
+function(a){
    console.log(a);   //firebug输出123456,使用+运算符
}(123456);
 
-function(a){
    console.log(a);   //firebug输出1234567,使用-运算符
}(1234567);
 
var fn=function(a){
    console.log(a);   //firebug输出12345678,使用=运算符
}(12345678)           
//需要注意的是:这么写只是一个赋值语句,即把函数匿名函数function(a){...}()的返回值赋值给了fn,如果函数没有返回值,那么fn为undefined,
//下面给出2个例子,用来解答读者的疑惑:
var fn=function(a){
    console.log(a);   //firebug输出12345678,使用=运算符
}(12345678);
console.info(fn);//控制台显示为undefined;
fn(123);//函数未定义报错,fn is undefiend 

var fn=function(a){
    console.log(a);   //firebug输出12345678,使用=运算符
    return 111;
}(12345678);
console.info(fn);//会发现fn就是一个返回值111,而不是一个函数
fn(123);//报错,因为fn不是一个函数

可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。

 

加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。

 

不过这样的写法有什么用呢?

 

javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”,“容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

 

JQuery使用的就是这种方法,将JQuery代码包裹在 ( function (window,undefined){…jquery代码…} (window))中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。

 

© 著作权归作者所有

文文1
粉丝 25
博文 449
码字总数 141525
作品 0
长沙
程序员
私信 提问
加载中

评论(17)

文文1
文文1 博主

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?
这个已经修改,在文章中写明原因了
文文1
文文1 博主

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?
恩,确实是这样,已经修改
文文1
文文1 博主

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?
经过测试,写明了报错的原因
文文1
文文1 博主

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?
经过测试,已经修改
文文1
文文1 博主

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?
已经修改
发飙的大蜗牛

引用来自“大大果”的评论

看了文章之后恍然大悟 但是有一点不明白。
/*var fn=function(a){
       console.log(a); //firebug输出12345678,使用=运算符
}(12345678);*/ 没有问题
然后我再调用fn(123456789);就要报错了; 如果不让fn自己调用的话就没问题
/*var fn=function(a){
console.log(a); //firebug输出12345678,使用=运算符
}(12345678);*/
这段代码只是一个赋值语句。函数表达式后面加了()会立即执行,所以fn会被赋值为一个undefined值。你可以在函数表达式里return一下,就可以确认了。
大大果
看了文章之后恍然大悟 但是有一点不明白。
/*var fn=function(a){
       console.log(a); //firebug输出12345678,使用=运算符
}(12345678);*/ 没有问题
然后我再调用fn(123456789);就要报错了; 如果不让fn自己调用的话就没问题
大大果

引用来自“imyiligege”的评论

楼主,最后一段少了一个 ) 在哪里吗?

引用来自“文文1”的评论

能把这段代码贴出来吗?我没看出来少了...
哈哈哈 说的是这里吧 ( function (window,undefined){…jquery代码…} (window));
b
beckyao
79
文文1
文文1 博主
多谢指教,抽个时间我会把这篇文章好好整理下,以免误导他人,感谢指出错误,21
浅谈var、let、闭包以及立即执行函数(namespace)

首先我们来看个常见的例子 上述答案有同学可能回答 6和12345,那么就大错特错了 分析:上述例子其实是一样的 不管有没有存在异步函数(这里的setTimeout就是异步函数),例子一我执行a6和例子...

vnues
06/25
0
0
好程序员前端教程之JavaScript闭包和匿名函数的关系详解

好程序员前端教程之JavaScript闭包和匿名函数的关系详解 本文讲的是关于JavaScript闭包和匿名函数两者之间的关系,从匿名函数概念到立即执行函数,最后到闭包。下面一起来看看文章分析,希望...

好程序员IT
03/22
15
0
简析JavaScript中的Function类型(四)——函数的内部属性

函数的内部属性主要有三个:。其中在简析JavaScript中的this关键字中探讨的已经比较清楚了,这里来说下和。 1. arguments 看下面的代码: 如代码所示,声明时没有显式地声明参数,但是在函数...

前端二牛
03/30
6
0
JavaScript初学者必看“this”

译者按: JavaScript的和Java等面向对象语言中的大不一样,、和函数更是将的灵活度进一步延伸。 原文: JavaScript: The Keyword ‘This’ for Beginners 译者: Fundebug 为了保证可读性,本文...

Fundebug
2017/05/17
24
0
ECMAScript 学习笔记(2)--基本类型及语法相关

1、变量名区分大小写; 注意代码块的概念。 变量分为原始值和引用值:原始值存储在栈,引用值存储在堆(引用值一般是一个指针或对象名、函数名)。在C语言中编译的程序占用的内存分为以下几个...

wx599c47c7bdcad
2018/05/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

手写RPC框架指北另送贴心注释代码一套

Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的。所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架。 完整代码...

全菜工程师小辉
5分钟前
0
0
【Java】开发收货

简介 谨以此篇,记载开发过程中的一些tips。 编译器 【Shift + F6】可实现变量的联动修改。

Areya
22分钟前
2
0
DOM官方定义

DOM Document Object Model 文档对象模型 DOM的官方定义:W3C的DOM,可以使程序或者脚本(JS或AS\JScript),动态的访问或者操作文档的内容、结构、样式。 DOM只是一个标准,操作网页的标准。...

前端老手
28分钟前
4
0
IT兄弟连 HTML5教程 HTML5的学习线路图 第一阶段学习网页制作

学习HTML5技术可并不是简单学会几个新增的标签而已,HTML5现在可以说是前端所有技术的代名词。需要学习的语言和工具不仅多,对于刚接触他们的新人会感觉很乱。另外,前端开发也会细分很多个开...

老码农的一亩三分地
29分钟前
4
0
可见性有序性,Happens-before来搞定

写在前面 上一篇文章并发 Bug 之源有三,请睁大眼睛看清它们 谈到了可见性/原子性/有序性三个问题,这些问题通常违背我们的直觉和思考模式,也就导致了很多并发 Bug 为了解决 CPU,内存,IO ...

tan日拱一兵
45分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部