博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javascript 学习笔记 - 函数 - 关于IIFE - 关于函数声明和函数表达式 - 个人总结
阅读量:5134 次
发布时间:2019-06-13

本文共 7442 字,大约阅读时间需要 24 分钟。

一切起源于一段代码(近来学完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    

对于一个只学过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 declaration var 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(); >>> 1 1 1

可以看出,三个引用 指向同一个函数,但各自都有自己独立的变量空间,外部空间或者全局不能访问。

(但具体解析情况,函数声明和函数表达式会有所不同,参见下文 )
总结为,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 function4 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 >>> Wednesday13 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

你可能感兴趣的文章
【JS】jQuery设置定时器,访问服务器(PHP示例)配合微信、支付宝原生支付,跳转web网页...
查看>>
实验四2
查看>>
VS2012+Win7网站发布详细步骤
查看>>
Android现学现用第十一天
查看>>
Bin Packing 装箱问题——NPH问题的暴力枚举 状压DP
查看>>
多路复用
查看>>
python 列表
查看>>
Python数据可视化之Pygal(雷达图)
查看>>
Django组件--cookie与session
查看>>
12. javacript高级程序设计-DOM2和DOM3
查看>>
Centos7安装vsftpd (FTP服务器)
查看>>
当前主流读取Excel技术对比
查看>>
js-格式化数字保留两位小数-带千分符
查看>>
【Java】forward & redirect 的差异
查看>>
Java学习笔记--字符串和文件IO
查看>>
【BZOJ1951】古代猪文(CRT,卢卡斯定理)
查看>>
poj 2823 线段树
查看>>
转 Silverlight开发历程—(画刷与着色之线性渐变画刷)
查看>>
SQL语法(3)
查看>>
在js在添版本号
查看>>