2011-08-01 3 views
3

개체 (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)) 

는 여기에 내가 CONSOLE.LOG를 통해 r.prop1(), 콘솔을 실행 할 때마다, 잘못된 멤버, "prop3"및 OBJ를 [반환

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

} 

불행하게도 구현하려고 시도한이다 member]는 "obj.prop3"을 반환합니다. 모두 잘못 됐어.

이것은 폐쇄와 관련이 있으며 새로 만든 멤버 멤버 함수가 범위를 벗어나는 방법과 관련이 있다고 생각합니다.

어떻게 수정합니까?

편집 : 데이비드 플래너의 자바 스크립트 : 확실한 가이드 8 장, 섹션 섹션 폐쇄에 마지막 예를 들어 내가 위에 쓴 그대로 반영 3에 거의 직접이 질문에 대한 대답. 내 문제를 이해하는 열쇠는 함수가 정의 된 것과 동일한 범위 체인에서 호출된다는 것입니다. 함수는 객체 및 관련 범위 체인입니다. 일명 클로저 야.

답변

6

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

이제 루프의 각 반복에 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)); 
     }; 
    } 
} 

이 호출에서 그것을 반환 함수가 this[ member ]에 할당하는 것을 제외하고는 동일합니다


은 동일한 개념에 다른 변화가있다. 그래도 같은 원리.


다른 사람들은 인생 (즉시 호출 함수 표현식) 대신 명명 된 기능을 사용하는 것을 선호합니다.

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에 글로벌 암시 방지 할 수 있습니다. @CMS에게 감사드립니다.

편집 : 변경 Array.prototype.slice(args,0)


Array.prototype.slice.call(args,0)에 편집 :

이 어떤 문제와 관련,하지만 당신이하고있는 모든 경우 arguments에 통과되지 않을 때 래퍼 원래 함수를 호출, 당신은 제거 할 수 있습니다 ...

args = Array.prototype.slice.call(args,0); 

는 그냥 원래 arguments 객체를 전달하지 :

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

배열로 변환 할 필요가 있습니다.

+0

또한, 'member' 변수를 선언하는 것을 잊지 마라. (for obj의 var 멤버)' – CMS

+0

@CMS : 잘 잡는다. 그건 적어도 * 당신이 저를 붙잡는 것을 잊어 버린 세 번째 시간입니다. o) 고정. – user113716

+0

원래의 예제에서 slice.call을 수정하고 var 멤버를 추가했습니다. 감사합니다. –