Function Invocation Patterns

Function Invocation Patterns 今天看到微博上大家在讨论一个JavaScript的小问题,问题虽小,还是有思考的价值。我看到不少人对其展开了讨论,有很多答案,也有很多有意思的观点。学JavaScript的人很多,但是学好真不容易,哪怕就是基础的部分。

先看一下下面的代码:

var value = 500;
var obj = {
    value: 0,
    increment: function() {
        this.value++;

        var innerFunction = function() {
            alert(this.value);
        }

        innerFunction();
    }
}
obj.increment();

好,在往下阅读以前,想一想,你觉得会打印的结果是多少?

————————————————-思考———————————————————

不少人都觉得是1,因为obj对象的value初始值是0,打印的“this.value”在“this.value++”以后就应该变成1了。

其实答案是500,语句”innerFunction()”,正是使用了Function Invocation Patterns这种方式去调用一个方法,这个方法调用没有显式指定“this”,那么如果你按照一些其它编程语言的思路去思考的话,没有显式指定this,this就应该是当前对象吧……那你就错了。这正是JavaScript设计中一个很糟糕的部分,或者说,是一个坑——在没有显式指定this的时候,这个this其实是一个global对象,在这里就是window。

还有很多人解释说这是“闭包”的一个应用,有人又说不是,我来分析一下:闭包简单说就是“包含自由变量的代码块”——也就是说,一个条件是代码块,这点毫无疑问满足;另一个条件是自由变量,这就有争议了,代码中的那个全局变量value算不算所谓的“自由变量”,如果算,那就符合闭包的要求,如果不算(很多人认为global变量不能算作闭包的自由变量),那就不算闭包。

解决方法呢,其中的一个比较简单的办法就是持有一个当前正确this的引用,在这里保存到一个名为“that”的变量里,再让目标方法来调用:

var value = 500;
var obj = {
    value: 0,
    increment: function() {
        var that = this;
        that.value++;

        var innerFunction = function() {
            alert(that.value);
        }

        innerFunction();
    }
}
obj.increment();

这样结果就是1了。

还没完,如果我把程序改成这样(即把原来的“this.value++”改成“value++”):

var value = 500;
var obj = {
    value: 0,
    increment: function() {
        value++;

        var innerFunction = function() {
            alert(this.value);
        }

        innerFunction();
    }
}
obj.increment();

结果又是多少呢?

————————————————-思考———————————————————

结果应该是501。因为没有使用this.value的时候,“value++”这一行的这个value是全局的value。

那我把全局的value屏蔽掉吧:

//var value = 500;
var obj = {
    value: 0,
    increment: function() {
        value++;

        var innerFunction = function() {
            alert(this.value);
        }

        innerFunction();
    }
}
obj.increment();

这样的结果又是多少,这回总该是1了吧?

————————————————-思考———————————————————

不,这回会报错:Uncaught ReferenceError: value is not defined。因为在这样使用的时候,obj对象的value对increment方法内部来说直接不可见。

其实,在JavaScript中,有这样几种函数调用模式:

  1. 方法调用:Fethod Invocation
  2. 函数调用:Function Invocation
  3. 构造器调用:Constructor Invocation
  4. Apply调用:Apply Invocation

这些模式其实你都用过,如果你想知道更多,请阅读这篇文章,上面的例子代码也是从它里面引用来的。

如果你对上面说到的都理解了,那么看看这段代码,结果应该是多少?

var value = 500;
var obj = function(){
    var value = 0;
    return function() {
        value++;
        alert(value);
    }
}
obj()();

————————————————–思考——————————————————–

结果应该是1(其实这道题只是一个简单的闭包使用而已,并没有Function Invocation Patterns,我挖了个坑让你跳,嘿嘿),你对了吗?

最后一题,把典型的闭包和Function Invocation Patterns结合起来,你要毫无压力地挺住啊:

var value = 500;
var obj = function(){
    var value = 0;
    var getValue = function(){
        return this.value;
    };
    return function() {
        value++;
        alert( getValue(value) );
    }
}
obj()();

结果是多少呢?

————————————————–思考——————————————————–

正确结果是500,这次你对了吗?

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接《四火的唠叨》

分享到:

5 comments

  1. [...] 最后,还有一个小题目是关于Function Invocation Pattern的,我在这篇文章里有写到,就不单独贴出来了。 [...]

  2. [...] 最后,还有一个小题目是关于Function Invocation Pattern的,我在这篇文章里有写到,就不单独贴出来了。 [...]

  3. [...] 最后,还有一个小题目是关于Function Invocation Pattern的,我在这篇文章里有写到,就不单独贴出来了。 [...]

  4. hugh 说道:

    以我有限的JS经验来讲,这应该是属性和变量的区别。通过this调用的都是属性,所以会沿着prototype的路线往上搜索,最终找到了global变量;反之则是对变量的使用,沿着closure的路线往上搜索。
    个人浅见,仅供参考。

  5. [...] 文章系本人原创,转载请保持完整性并注明出自《四火的唠叨》 [...]

发表评论

电子邮件地址不会被公开。

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Preview on Feedage: