试图理解闭包.谁能告诉我这个密码

Trying to understand closures. Could somebody walk me through this code?

本文关键字:告诉我 密码 闭包      更新时间:2023-09-26

下面是一段摘自reddit帖子的javascript代码:

function Stream() {
    var data       = [],
        listeners  = [];
    function push( new_data ) {
        var result = data.push( new_data );
        callListeners( new_data, result );
        return result;
    }
    function addListener( listener ) {
        return listeners.push( listener );
    }
    function callListeners( ) {
        var length    = listeners.length,
            result    = [],
            action    = null;
        while ( length-- ) {
            action = listeners[ length ];
            result.push( action.apply( null, arguments) );
        }
        return result;
    }
    return {
        push : push,
        addListener: addListener
    }
}

var foo = Stream();
foo.addListener( function( new_data ) {
    alert( "added: " + new_data );
});
foo.push( "Hello World!" );

我认为在阅读了本教程之后,我对闭包有了一个微弱的理解,但是我就是不明白这段代码是如何工作的。当我试图在脑海中解析它时,我基本上被困在第6行:var result = data.push( new_data );

似乎data只是一个数组在那一点data.push( foo )是没有意义的。它不会无限递归吗? (strike -不知道有一个原生的push方法用于数组)下一行callListener被调用时带有两个参数,但下面的函数没有参数。

如果有人有几分钟的话,你能抓住我的手,像我这个无知的傻瓜一样给我讲讲这些代码吗?现在,我甚至不确定我是否理解目的地。

数组对象,它们有一个push()方法。没什么特别的。

callListeners()函数没有声明任何命名参数,但是JavaScript允许调用函数时使用比声明的参数更多的参数,并且完整的参数列表作为特殊名称arguments可用。callListeners()action.apply()调用中使用arguments来调用action函数,其参数列表与callListeners()本身相同。callListeners()的目的是你用一些参数调用它,它用这些参数调用listeners数组中的所有函数。

不过,这两件事都与闭包的使用无关。闭包发挥作用的地方是,Stream()返回的对象有两个方法,push()addListener(),可以"看到"相同的datalisteners数组,即使这些数组没有存储在调用方法的对象中。对Stream()的两次调用将返回两个对象,其方法看到不同的 datalisteners数组。
> function Stream() {
>     var data       = [],
>         listeners  = [];
> 
>     function push( new_data ) {
>         var result = data.push( new_data );

data是对外部函数中数据的引用。data.push(...)的返回值是添加new_data后数组的长度。

>         callListeners( new_data, result );
>         return result;
>     }

callListeners()是对以下面的名称声明的函数的调用。

>     function addListener( listener ) {
>         return listeners.push( listener );
>     }

这将listener添加到listeners,并返回listeners数组的新长度。

>     function callListeners( ) {
>         var length    = listeners.length,
>             result    = [],
>             action    = null;
>         while ( length-- ) {
>             action = listeners[ length ];
>             result.push( action.apply( null, arguments) );
>         }
>         return result;
>     }

上面的函数用传递给它的参数调用listeners数组中的所有侦听器。它以相反的顺序调用它们(即,最后添加的一个首先调用,然后第二个最后调用,依此类推),这有点不寻常。

>     return {
>         push : push,
>         addListener: addListener
>     }

返回一个具有以下属性的对象:push(其值是对名称为push的函数的引用)和addListener(其值是对名称为addListener的函数的引用)

> }
> 
> 
> var foo = Stream();

按照约定,名称以大写字母开头的函数是构造函数,应该用new操作符调用。不是构造函数,所以应该以小写s开头。

> foo.addListener( function( new_data )
> {
>     alert( "added: " + new_data ); });

调用foo.addListener(这是对上面调用Stream()时创建的"closed over"addListener函数的引用)并传递给它一个匿名函数。对返回值不做任何处理。

> foo.push( "Hello World!" );

调用(关闭)push函数并传递字符串"Hello World!"。然后push调用callListeners(),传递给它相同的字符串。然后callListeners调用侦听器数组中的每个函数(到目前为止只有一个,上面添加的匿名函数),将提供给push()的参数传递给它。"Hello World !").

所以结果是一个带有"added: Hello World!"的警告。

如果添加另一个侦听器,则调用foo.push()将使用提供的参数调用两个侦听器,但顺序与添加的顺序相反。

首先,push是Array类的一个方法:

通过添加给定元素并返回数组的新长度来改变数组。

因此,var result = data.push( new_data );只是将new_data添加到data之后,并将result设置为data中的新元素数。

callListeners位很棘手。action将是一个函数,它将通过在该函数上使用apply方法来调用(注意:函数在JavaScript中是对象)。你还会看到arguments,这是一个特殊的变量,它是:

类数组对象,与传递给函数的实参相对应。

所以,如果你想自己解包参数列表(比如在Perl中使用@_,或者在C或c++中使用变量参数列表),或者如果你只是计划将完整的参数列表传递给想要数组的其他人,那么你可以使用arguments。通常你会说:

var a = Array.prototype.slice.call(arguments);

显式地将arguments转换为实际数组。然而,在这种情况下,apply很乐意采用原始的arguments伪数组,因此不需要"切片强制转换"。

JavaScript数组有一个本地的push()方法。查看这篇文章…

http://www.hunlock.com/blogs/Mastering_Javascript_Arrays

…所以代码不是递归地调用function push( new_data ){},而是引用数组的原生push()方法。

如果它是递归的,它将改为var result = this.push( new_data );,这将使它成为无限的:)

我希望这对你有帮助。斯托伊