Javascript 对于中的闭包和函数创建问题

Javascript 对于中的闭包和函数创建问题,javascript,Javascript,我一直在尝试创建一个包装函数,它接受对象(obj)的所有函数属性,并用另一个函数(p())包装它们 这段代码说明了我的意思 //Setup code function p(input){ //do stuff return new_output; } obj = { prop1: function(){...}, prop2: function(){...}, prop3: function(){...} } //Here's the wrapper

我一直在尝试创建一个包装函数,它接受对象(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))
下面是我对实现的尝试

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) );
        }
    };

}
不幸的是,每当我运行r.prop1(),控制台就会通过console.log返回错误的成员“prop3”,而obj[member]返回“obj.prop3”。一切都错了

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

我该如何解决这个问题


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

for
循环中生成的函数引用的
成员变量与
成员变量相同

这是因为JavaScript没有块作用域,只有函数作用域。这是一个普遍的问题

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

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) );
        };
    }
}
现在循环中每次迭代中
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) );
        };
    }
}
这是相同的,只是它从调用返回一个要分配给
此[成员]
的函数。但原则是一样的


其他人更喜欢使用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 );
    }
}
…虽然我觉得不太清楚,效率也有点低


编辑:
var
添加到
member
变量中,以避免隐式全局变量。多亏了

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


编辑:

这与任何问题都没有关系,但是如果您所做的只是在包装器调用原始函数时传递
参数
,那么您可以摆脱

args = Array.prototype.slice.call(args,0);
只需传递原始的
参数
对象:

return p( fn.apply( fn,arguments ) );

无需将其转换为数组。

此外,不要忘记声明
成员
变量<代码>用于(obj中的var成员)
@CMS:捕捉良好。这至少是你第三次发现我忘了修好它;o) Fixed.I Fixed slice.call并在原始示例中添加了var成员。Thanks@Christian:不客气,尽管它是
slice.call
,而不是
slice.slice
;o) 现在是泰国的凌晨5点,让我休息一下。