请教一下js函数调用后,所返回函数,的调用。


代码一:


 function show(){
    alert(12);
    return function(){
        alert(3);
    };
}
alert(show()());

我觉得这段函数应该会报错,因为 下面的show(括号一),返回的是
function(){ alert(3); } 那么这个返回的函数后面还有一个括号来调用它,即 function(){ alert(3); }()

我在全局环境下,写这段代码就会报错, function(){ alert(3); }()
请问为什么代码一所示函数就可以正常运行呢?谢谢。

web前端开发 JavaScript

杯具泡参D囧飛 9 years, 7 months ago

谢谢各位的详细回答,相信如果有像我一样问题的人看到了,也能加深对函数的理解。

好名都让狗起了 answered 9 years, 7 months ago


 function show(){
    var f=function(){alert(3);};
    alert(12);
    return f;
}
var s=show();
alert(s());

这样写?

冷水煮泡面 answered 9 years, 7 months ago

不好意思
可以参考知乎中的回答
http://www.zhihu.com/question/20292224
1)函数声明、函数表达式


 function(){alert(3);}();

函数声明不能立即执行
函数表达式可以的
解析器识别函数声明的条件是执行语句以function关键字开始,那么自然,只要在function关键字的前面有任何其他的元素,就会从函数定义转变为函数表达式
所以


 ~function() {}();
!function() {}();
void function() {}();

可以执行的


 function(){alert(3);}();

解析为函数申明,会报语法解析错误
2)括号的作用


 (function(){alert(3);})();

确定优先级及分组运算,会将括号中语句作为表达式运行

gloom answered 9 years, 7 months ago

代码一其实是 函数柯里化 即 将函数当做值传递给下一个函数,形如


 show()  --> 值
var k = show();
var g = k();

g是结果

那么你的这句为什么会报错呢?


 function(){ 
    alert(3);
}()

因为JavaScript可以执行函数表达式,但是不能执行函数声明,也就是说,JavaScript把


 function(){ 
    alert(3);
}

当做了函数声明,想要正常运行,在函数声明的外部套一对小括号即可,像这样:


 (function(){ 
    alert(3);
})();

当然,想要套其他的玩意也行,比如:


 +function(){ 
    alert(3);
}();

-function(){ 
    alert(3);
}();

以上

叽哩咕噜哔 answered 9 years, 7 months ago

其他地方你的理解是对的,但是你搞错了一点:

我觉得这段函数应该会报错,因为 下面的show(括号一),返回的是
function(){ alert(3); } 那么这个返回的函数后面还有一个括号来调用它,即function(){ alert(3); }()。

show()返回的并不是函数的那一大段,函数是存在内存里的,返回的只是指向这个函数的引用。
说到这里你可能还不太理解什么叫引用,简单来说就是:对象本身存在内存的某个位置,而变量只能持有一个指向这个位置的标记。你可以想一想C语言里的指针。

这时就好理解了, show()() 并不是


 function () {
    alert(3);
} ()

这只是简单的字符串替换,JavaScript并不是这么工作的。


 return function () {
    alert(3);
}

这段代码的实际过程是:在内存中创建匿名函数,将这个函数的引用返回出去。
所以 show() 得到的是对匿名函数的引用x,然后 x() 执行了这个匿名函数。
所以根本不会有什么错误。

说了这么多题主并不一定能完全理解,所以给出一些代码,题主可以试一试。


 var fa = function() {
    alert('hello,world');
};

var fb = function() {
    alert('hello,world');
}

alert(fa === fb); //结果是false

这是因为内存中出现了两个长得完全一样的函数,但是他们仍然是两个函数。

进一步:


 alert(
    function () { return 1 } === function () { return 1; }
); //结果仍然是false

现在考虑向一个函数传入另一个函数呢?


 var f = function (x) {
    alert('执行f,下面将执行x');
    x();
    alert('x执行完了,现在已经回到了f中');
}
f(function () {
    alert('x执行中');
});

题主不妨亲自试一试。

上面的代码说明了在JavaScript中,函数和其他的引用类型一样,可以方便地传入另一个函数,被函数返回,当然,传入和返回的都只是对函数的引用,而不是函数本身,更不是函数的那段字符串。


函数表达式有两种,匿名的和具名的:


 var af = function () {
    //这是一个匿名函数表达式
};

var nf = function name() {
    //这是一个具名函数表达式
};

匿名函数表达式很容易看出来,就是没有名字嘛啊哈哈哈。
具名函数表达式则需要和函数声明语法进行区别:


 function name () {
    //这是一个函数声明
}

var nf = function name() {
    //这是将一个函数表达式赋值给一个变量
};

实际上只有明显的使用上面代码中那种函数声明方式,JS才会把它当作是声明,否则就会当作表达式,比如 @qianjiahao 提到的那几种形式:


 (function name() {
    //不管有没有函数名,这个东西都会被看作是函数表达式
}());

其他形式可以参考 @qianjiahao 的答案。

具名函数表达式(NFE)和函数声明还有一个有意思的区别,就是在NFE中,你可以访问函数名但是不能修改函数名。
例如:


 function f1() {
    console.log(f1); //执行后显示函数f1本身
    f1 = 42;
    console.log(f1); //执行后显示42
}
f1();

var f2 = function nfe() {
    console.log(nfe); //执行后显示函数nfe本身
    nfe = 17;
    console.log(nfe); //执行后还是显示nfe本身,也就是说上面写nfe这个操作是无效的。
}

更详细的内容参考我之前的问题: http://segmentfault.com/q/1010000002810093

崔斯特REX answered 9 years, 7 months ago

Your Answer