对于In与闭包和函数创建问题

Chr*_*sse 3 javascript

我一直在尝试创建一个包装器函数,它接受一个对象(obj)的所有函数属性,并用另一个函数(p())包装它们.

这段代码说明了我的意思.

//Setup code
function p(input){
    //do stuff
    return new_output;
}
obj = {
    prop1: function(){...},
    prop2: function(){...},
    prop3: function(){...}
 }

//Here's the wrapper function
r = new R(obj);

//Expected behaviour
r.prop1(a1,a2); //Just like saying p(obj.prop1(a1,a2))
Run Code Online (Sandbox Code Playgroud)

这是我对实施的尝试

function R (obj) {
    for (var member in obj) {
            //Mirrors obj's members
        this[member] = function (args) {
            var fn,inner_member = member;
                    //Convert to array for 'apply'
            args = Array.prototype.slice.call(args,0);

            fn = obj[member];

                    //Returns unexpected values, poo...
            console.log(inner_member);
            return p( fn.apply(fn,args) );
        }
    };

}
Run Code Online (Sandbox Code Playgroud)

不幸的是,每当我运行r.prop1()时,控制台通过console.log返回错误的成员,"prop3"和obj [member]返回"obj.prop3".一切都错了.

我认为这与闭包以及新创建的成员函数如何超出其范围有关.

我该如何解决?

编辑: David Flanagan的Javascript:The Definitive Guide在第8章第3节中直接回答了这个问题.那部分是关于闭包的,最后一个例子反映了我上面写的内容.理解我的问题的关键是函数在定义的同一范围链中调用.函数是对象和相关的范围链.AKA,关闭.

use*_*716 6

member您在for循环中生成的函数引用的变量是同一个member变量.

这是因为JavaScript 没有块范围,只有函数范围.这是一个常见问题.

一种解决方案是在循环中调用函数,传入以member使其成为新变量范围的一部分.

function R (obj) {

// --------v---- use var to declare variables in order to avoid implicit globals
    for (var member in obj) {      // ...thanks to @CMS for the reminder.
        createMember( member );
    }

    function createMember( mem ) {
            //Mirrors obj's members
        this[ mem ] = function (args) {
            var fn,inner_member = mem;
                    //Conver to array for 'apply'

   // Don't forget to use .call-----------v
            args = Array.prototype.slice.call(args,0);

            fn = obj[ mem ];

            console.log(inner_member);
            return p( fn.apply(fn,args) );
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,member循环中每次迭代的值作为参数传递给单独的函数调用,每次都会创建一个新的变量作用域.

因为每个新函数都是每个唯一变量作用域的一部分,所以每个函数都引用一个不同的mem变量(或参数).


同一概念还有其他变体:

function R (obj) {
    for (var member in obj) {
        this[ member ] = createMember( member );
    }

    function createMember( mem ) {
            //Mirrors obj's members
        return function (args) {
            var fn,inner_member = mem;
                    //Conver to array for 'apply'

   // Don't forget to use .call-----------v
            args = Array.prototype.slice.call(args,0);

            fn = obj[ mem ];

            console.log(inner_member);
            return p( fn.apply(fn,args) );
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

这是相同的,除了它从要分配的调用返回一个函数this[ member ].同样的原则.


其他人更喜欢使用IIFE (立即调用的函数表达式)而不是命名函数.

function R (obj) {
    for (var member in obj) {
        (function( mem ) {
                //Mirrors obj's members
            this[ mem ] =  function (args) {
                var fn,inner_member = mem;
                    //Conver to array for 'apply'

   // Don't forget to use .call----------------v
                args = Array.prototype.slice.call(args,0);

                fn = obj[ mem ];

                console.log(inner_member);
                return p( fn.apply(fn,args) );
            };
        })( member );
    }
}
Run Code Online (Sandbox Code Playgroud)

......虽然我认为它不太清楚,效率也低一点.


编辑:添加varmember变量以避免隐式全局.感谢@CMS.

编辑:更改Array.prototype.slice(args,0)Array.prototype.slice.call(args,0)


编辑:

这与任何问题无关,但是如果你所做的只是传递arguments包装器调用原始函数的时候,你可以摆脱......

args = Array.prototype.slice.call(args,0);
Run Code Online (Sandbox Code Playgroud)

然后传递原始arguments对象:

return p( fn.apply( fn,arguments ) );
Run Code Online (Sandbox Code Playgroud)

无需将其转换为数组.