函数自执行

一 闭包和作用域

闭包是一个受到保护的变量空间,由内嵌函数生成。JavaScript具有函数级的作用域。这意外着定义在函数内部的变量在函数外部不能被访问。JavaScript的作用域又是词法性质的。这意味着函数运行在定义它的作用域中,而不是在调用它的作用域中。把这两个因素结合起来,就能通过把变量包裹在匿名函数中而对其加以保护。(引自《JavaScript设计模式》)

var i = 100;
function makeCounter() {
    var i = 0; // makeCounter的私有变量
    return function() {  console.log(++i);  } //该函数运行的环境是makeCounter的作用域
}
var counter = makeCounter(); // counter即makeCounter的内嵌函数,运行时获取的i来源于makeCounter作用域中的i
counter(); // 输出: 1 而非 101 –>因为makeCounter返回的内嵌函数能获取i,所以也称为makeCounter的特权函数。
counter(); // 输出: 2 
i; // 输出: 100 –>说明makeCounter内的i不能被调用

二 () 调用函数

(1)function foo() {} 和 var foo = function() {},foo都是这里function(){}的引用,调用时只需要直接foo()。但如果直接将()放在function(){}后面则会触发语法错误。

function() {..}() // SyntaxError: Unexpected token(

原因:当解析器遇到function关键字时,默认将其视为一个函数声明(function declaration),而不是一个函数表达式(function expression)。如果不显式告诉解析器将其视为一个函数表达式,则会视其为一个函数声明缺少名字的语法错误。

(2)针对上面语法错误,给函数加个名称并加()执行,会触发另外一个语法错误:

function foo() {….} () ; // SyntaxError: Unexpected token ) 

原因:()放在表达式后面表示这个表达式是个函数并将要被调用,而()放在statement(函数声明也是个statement)后面则是用来与前一个statement分隔,仅仅是一个分组运算符(用在控制运算优先级的,即: 1 + 2 * ( 3 + 4 )这里括号的作用 )
修正:()作为分组运算符时,需要包含一个表达式,所以简单加个表达式 (1)。

function foo() {…}(1) ; //不会报错了,这样的写法其实等同于下面
function foo() {…} // 所以foo在这里并没有被调用,而仅仅是个声明而已。
(1)

(3)上面的修正只是解决了语法错误,但实际上并没有达到我们的期望:让函数立即执行。最常用的方法是将其包在()内。因为在JavaScript中,()只能包含表达式而不能是语句statement。

(function() {..}()) 或者 (function() {..})()

(4)其他方法:原理都是令解析器认识到()前是一个表达式

var x = function() { return 1; }();
true && function() { .. } ();
0, function() {..} ()
! function() {..} ()
~ function() {..} ()
+ function() {..} ()
– function() {..} ()

三 利用闭包保存状态
(1)下面例子给元素添加事件监听,结果每个a点击后输出的都是elmes数组的长度。
原因:for执行后的每个elems的点击事件函数中绑定的是 j ,当点击时输出的 j 确确实实就是 j 在for运行之后的值。

var elems = document.getElementsByTagName(‘a’);
for (var j = 0; j < elems.length; j++) {
    elems[j].addEventListener(‘click’, function(e) {
        e.preventDefault();
        console.log (j);
    }, false);
}

(2)下面例子利用立执行函数,将 j 锁定。
for执行完后 j 虽然已经变成elems数组的长度,但是匿名函数每次立即执行的时候传入的参数是相应的 j

var elems = document.getElementsByTagName(‘a’);
for (var j = 0; j < elems.length; j++) {
    (function( lockedIndex ) {
        elems[j].addEventListener(‘click’, function(e) {
            e.preventDefault();
            console.log(lockedIndex);
        }, false);
    })(j);
}

四 函数定义内调用本身

function foo() { foo(); } 
var foo = function() { arguments.callee(); } // arguments.callee 在ECMAScript5中被废弃了。

五 参考
immediately-invoked-function-expression

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值