一切起源于一段代码(近来学完java基础 开始学习敲一下javascript):

var getter = function(){var dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];return function(number){return dayNames[number];};
}();
console.log(getter(4));

来自 Eloquent Javascript    http://eloquentjavascript.net/10_modules.html

对于一个只学过C , java的娃来说 看到最后那对 parenthesis 括弧"()" 真的是一脸懵逼,

第一次打的时候 我打漏了,后果就是 log 直接输出 整个 function :

function(number){return dayNames[number];};

强迫症发作,请求google 和 stackoverflow,现在大概梳理清楚 原因,特作此笔记

首先,清楚,Javascript中的全局变量作用域和局部函数作用域(和java有一点点区别),here is a sample:

(>>> 后面紧跟输出结果)

1. 函数引用与函数变量空间

function counter(){// this is a function declarationvar count = 0;count += 1; //功能就是每调用一次就给内部变量 count 加一  再输出
        console.log(count);
}
//三个 counter 包括 1 ,2 ,还有原函数引用(reference)
)
var counter1 = counter; // do not add "()" right after "counter"
var counter2 = counter;counter();counter1();counter2();>>> 111

可以看出,三个引用 指向同一个函数,但各自都有自己独立的变量空间,外部空间或者全局不能访问。
(但具体解析情况,函数声明和函数表达式会有所不同,参见下文 )
总结为,Javascript中,不同于java,使用 "{}" 并不能创建新的变量空间(即Java的代码块),只能通过function关键词 来创建新的变量空间
因此 ,存在 命名空间函数(类似一个封装(Capsulation)),用一个function 关键词将一个函数封装起来,从而使外界无法访问内部变量,
叫做一个 模块函数(module)

2.三种函数声明方式

1 (1)var functionOne =  function(){}
2 (2)function functionTwo(){}
3 (3)functionThree = new Function(){}  //少用 不讨论

1 var functionOne = function(){
2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
3               "Thursday","Friday","Saturday"];
4                     return dayNames[number];
5
6
7 }();
8 console.log(getter.name);
9 >>> "" 空字符

该代码等价于:
首先定义一个匿名函数:

1 (function(){
2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
3               "Thursday","Friday","Saturday"];
4              return function(number){
5                     return dayNames[number];
6                 };
7 }());

再将这个函数引用赋值给getter变量。
因此执行以下代码可以使得其他变量引用指向同一个匿名函数:

var getter2 = getter;

这个时候,使用name函数,返回一个空字符,表明getter只是调用function的一个引用,匿名函数肯定名字为空嘛。

再看function Declaration

 1 function getter(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3               "Thursday","Friday","Saturday"];
 4              return function(number){
 5                     return dayNames[number];
 6                 };
 7
 8 };
 9 console.log(getter.name);
10 >>> "getter"

表明这种声明方式,给一个函数指定了一个名字,同时函数内部变量域里面存储了一个name = getter的定值
当此时进行:

var getter2 = getter;

会报错。

3.函数声明(Function Expression) 和 函数表达式 (Function statement/declaration)

直接总结:

1 function expression :
2 var test1 = function(){/*code*/};
3
4
5 function declaration:
6 function test2(){/*code*/}; 

再回到开头那段代码:

1 var functionOne = function(){
2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
3               "Thursday","Friday","Saturday"];
4                     console.log( dayNames[4]);
5
6
7 }();
8 >>> Friday

上面代码中,由函数命名方式,明显是一个 Function expression,在一个function expression 后紧跟一对 parenthesis"()"
Javasc解析器(Parser)会即刻调用该被functionOne引用 的 匿名函数,此时无需再通过 functionOne(); 来调用该匿名函数
已经直接执行了 function() 的代码,输出 Friday
在这里,要用到一个概念: IIFE (immediately-invoked function expression)即时调用函数表达式,在一个匿名函数表达式(注意哦 是表达式哦)后面直接加 "()"
就可以不需通过赋值分配引用,通过引用来调用函数,而是即时执行。
理论上,上面的函数等价于:

1 function(){
2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
3               "Thursday","Friday","Saturday"];
4                     console.log( dayNames[4]);
5 }();
6 >>> Uncaught SyntaxError: Unexpected token (

看到上面结果发现,报错了。但原因却不在上面的思路错误(相反思路很nice),而是Javascript的语法问题,
因为当解析器Parser读到 function这个关键词时,发现前面没有变量var 赋予引用,parser就会默认这是一个function declaration,此时
它就会按照declaration 语法 function functionName(){} 来继续读取,发现 wtf 没有读到名字字符串,而直接就 来个"(" 了 ,所以报错 "Unexpected token ("
好气哟,那如果我想上面这段出错的代码正常运行 怎么办?
解决办法很简单,同样利用Javascript的语法————"()",只能是expression(类似于 运算式如(2+3)*5 中 括弧可以改变运算顺序,且括弧内必须为运算式(即表达式expression))
因此上面代码只需要改为:

 1 (function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3               "Thursday","Friday","Saturday"];
 4                     console.log( dayNames[4]);
 5
 6
 7 })();
 8 >>> Friday
 9 //或者
10 (function(){
11     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
12               "Thursday","Friday","Saturday"];
13                     console.log( dayNames[4]);
14
15
16 }());
17 >>> Friday

两种官方都认可,没问题的。
问题解决了!

另外还有一些细节,考虑一下代码:

1 test();
2 function test(){};
3 test2();//Uncaught TypeError: test2 is not a function
4 var test2 = function(){};
5 test2();

可以看到 中间那个test2 是会报错的,这也是Javascript的解析问题,对于test() 即function declaration 只有到调用该函数时,解析器才回去解析对应函数主体
(可能是节省资源吧 ,你没用过 我也没必要解析了)
而对于test2() ,必须要解析器阅读到 var test2 = function(){}; 这句时 后面才可以调用该函数,在该行前面,函数时没被解析器解析的
最后,自己强迫症发作,继续搞了一个无聊透顶的代码:

 1 (function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3                   "Thursday","Friday","Saturday"];
 4              return function(){
 5                 return function(){
 6                    console.log( dayNames[4]);
 7               }();
 8               }();
 9
10 }());
11 >>> Friday

当复习一下,首先最外面的"()" 是不能缺少的,否则会出现函数declaration后怎么是 "(" 没有紧跟名字的问题
继续,我们说到IIFE ,说到利用这个就可以想上面一样即时调用。
但如果我不想即时调用怎么办? 当然可以:

 1 var functionOne = function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3                   "Thursday","Friday","Saturday"];
 4              return function(){
 5                 return function(){
 6                    console.log( dayNames[4]);
 7               };
 8               };
 9
10 };
11 functionOne()()();//没错因为有三个function你需要三个括弧 一个一个调用 哈哈
12 >>> Friday

再来

 1 var functionOne = function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3                   "Thursday","Friday","Saturday"];
 4              return function(){
 5                 return function(){
 6                    console.log( dayNames[4]);
 7               }();
 8               };
 9
10 };11 functionOne()();//没错因为函数体本身已经即刻执行最里面的函数体 所以调用时 两个括弧就够啦

  12  >>> Friday

这样是不是很好理解 Javascript 编译器的原理了嘛
这里的即时运行怎么理解呢,什么即时?什么时刻算即时?
继续探究:

 1 var functionOne = function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3                   "Thursday","Friday","Saturday"];
 4              return function(){
 5                 return function(){
 6                    console.log( dayNames[4]);
 7               }();
 8               };
 9
10 };
11 >>> ""

这里没有调用functionOne()(),发现没有东西输出(空字符),表明即时执行(IIFE)是指该函数(嵌套的最外层函数引用)被调用时,里面加了IIFE 的括弧的函数体已经执行了,并且
return 该函数的结果(可以带着变量),我们叫它句柄(handler)
另外,括弧里面当然可以有参数啦,示范一下:

 1 var functionOne = function(){
 2     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
 3                   "Thursday","Friday","Saturday"];
 4              return function(number){
 5                 return function(){
 6                    console.log( dayNames[number]);
 7               }();
 8               };
 9
10 };
11 functionOne()(2);
12 >>> Wednesday
13
14 或者:
15
16 var functionOne = function(number){
17     var dayNames=["Sunday","Monday","Tuesday","Wednesday",
18                   "Thursday","Friday","Saturday"];
19              return function(){
20                 return function(){
21                    console.log( dayNames[number]);
22               }();
23               };
24
25 };
26 functionOne(2)();
27 >>> Wednesday

总结完毕,有错欢迎指出,自己有时间有新的发现也会来更新。

Thanks

转载于:https://www.cnblogs.com/chencanhui/p/7279513.html

Javascript 学习笔记 - 函数 - 关于IIFE - 关于函数声明和函数表达式 - 个人总结相关推荐

  1. JavaScript学习笔记——函数 Part4:向函数传递函数、从函数返回函数(函数是一等公民)

    要点 函数是值,这个值就是函数引用 函数是一等公民:函数引用是一等值 可将函数引用赋给变量.含在数据结构(如对象)中.传递给其他函数或从其他函数返回 函数是一等公民 不要再认为函数是特殊的,有别于Ja ...

  2. JavaScript学习笔记(四)---闭包、递归、柯里化函数、继承、深浅拷贝、设计模式

    JavaScript学习笔记(四)---闭包.递归.柯里化函数.继承.深浅拷贝.设计模式 1. 匿名函数的使用场景 2.自运行 3.闭包 3.1前提: 3.2闭包 4.函数对象的三种定义方式 5.th ...

  3. python eval 入门_Python学习笔记整理3之输入输出、python eval函数

    Python学习笔记整理3之输入输出.python eval函数 来源:中文源码网    浏览: 次    日期:2018年9月2日 Python学习笔记整理3之输入输出.python eval函数 ...

  4. r语言c函数怎么用,R语言学习笔记——C#中如何使用R语言setwd()函数

    在R语言编译器中,设置当前工作文件夹可以用setwd()函数. > setwd("e://桌面//") > setwd("e:\桌面\") > ...

  5. 【Java】Java学习笔记(2)——Java面向对象基础作业函数题

    本人私人博客:Megalomania,大部分文章会现在博客上传,有不足之处欢迎指正. 学校小学期Java课程的练习题,留个档便于以后需要时候有例子可以回忆,写的烂的地方请多多包含 1.求两个数值之和 ...

  6. oracle创建索引index,【学习笔记】Oracle索引 创建含sysdate的函数index案例

    天萃荷净 分享一篇运维DBA需求,创建含sysdate的函数index案例 1.模拟Oracle数据库环境 创建表插入数据库 [oracle@node1 ~]$ sqlplus chf/oraclep ...

  7. Oracle数据二进制传输,学习笔记:Oracle数值函数 十进制与二进制相互转换的函数使用案例...

    天萃荷净 ORACLE 十进制与二进制互转函数,开发DBA向我咨询数值转换的方法,Oracle数值转换函数NUMBER_TO_BIT等函数可用 1.Oracle数值转换之十进制转换二进制 CREATE ...

  8. JavaScript学习笔记(五)

    JavaScript学习笔记(五) ①Array类 本地对象 ②Date类 ①Global对象 对象的类型   内置对象 ②Math对象 宿主对象 今天继续学习JS中的对象,昨天内置对象Global对 ...

  9. Java程序猿的JavaScript学习笔记(10—— jQuery-在“类”层面扩展)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  10. JavaScript 学习笔记(1)

    1.     何为 Jscript JScript 是一种解释型的.基于对象的脚本语言. 局限性: 1)        不能使用该语言来编写独立运行的应用程序 2)        没有对读写文件的内置 ...

最新文章

  1. 医疗信息化、医学相关资料下载
  2. wayos利用easyradius实现WEB认证页面的记住密码及到期提醒功能
  3. Python零基础学习笔记(二十二)—— set
  4. oracle M4,oracle高级部分 - osc_9gm4ypss的个人空间 - OSCHINA - 中文开源技术交流社区...
  5. C++ - 类模板(class template) 详解 及 代码
  6. Java即时编译器JIT之简单介绍
  7. WebView的简单使用
  8. 在VMware安装Ubuntu后一直停留在VMware Easy Install
  9. 免费的身份证归属地查询接口
  10. java null转string_java null强转string
  11. uniapp 安卓ios端热更新
  12. YoloV3 先验框
  13. windows server 2019 安装CA-证书服务器
  14. Gimp 替换白色背景
  15. Angular2+ 双向数据绑定
  16. python解魔方程序_写一个解二阶魔方的程序
  17. 什么是开发环境、测试环境、UAT环境、仿真环境、生产环境?
  18. 关于征集全国信标委人工智能分委会知识图谱工作组成员单位的通知
  19. spring5之IOC入门笔记
  20. java中equals合if的用法_java中的equals和==

热门文章

  1. c 语言 怎样编写图形窗口,「分享」C语言如何编写图形界面
  2. 程序员最深情的告白——《致对象》
  3. c语言参数列表定义一个三维数组,C语言多维数组
  4. 关于Servlet报错:405 HTTP method GET is not supported by this URL问题解决方法
  5. 如何按距离排序 php,php做附近的人,根据距离由近到远进行排序
  6. 基于WirёGuαrd和UDP speeder的网游加速实现方案
  7. GDAL库中WFS服务中含有中文不能获取数据的问题
  8. 现实给了梦想多少时间?
  9. 移动端前端UI框架推荐
  10. 仿苹果涂鸦软件_ipad平板电脑有哪些绘画软件?