2011-02-18 2 views
15

콜백 메서드에 함수 범위를 전달하려고합니다. 내가 겪고있는 문제는 개체 범위를 가져 오는 것이고, 원래 함수에서 매개 변수와 로컬 변수에 대한 액세스를 제공하지 않는다는 것입니다. "this"에 대한 나의 이해는 외에도 과 현재 컨텍스트 (창 또는 일부 오브젝트)를 의미합니다. [리차드 콘 포드 (Richard Cornford)의 우수 사례 "실행 컨텍스트"섹션에서 http://jibbering.com/faq/notes/closures/을 인용하십시오. 나는 또한 자바 스크립트의 변수가 함수 범위를 가지고 있다는 것을 이해한다. (함수 안에서 선언 된 경우, 함수 내에서만 접근 할 수있다.)범위를 콜백 함수/바인딩에 전달

새로운 환경에서 나는 이전 고용주에게 많이했던 패턴을 비동기식 메서드 호출, 콜백 처리기 지정 및 현재 범위 통과, 사용 가능 상태로 만들기 위해 코드를 작성하려고합니다. 콜백 메소드에서. 현재 환경에서이 사실을 발견하지 못했습니다. (공개 : 내가 이전 환경에서 ExtJS를 사용하고 있었는데 ... 내가 프레임 워크를 사용하여 너무 아늑한 것처럼 느껴지 게하여 무슨 일이 일어 났는지에 대한 가정을 만들어 냈습니다.)

간단한 테스트 코드는 내가 무엇을하려고 노력하는지, 어떤 것이 작동하지 않는지를 보여줍니다.

function myHandler(data, ctx) { 
    console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

여기서 문제는 MySvcWrap.doWork에 전달 된 'this'가이 경우 전역 개체라는 점입니다. 제 의도는 함수의 실행 컨텍스트를 myHandler에 전달하는 것입니다.

나는 무엇을 시도했다. '이'나는 작동하는 물체, 예를 통과 대신하는 경우 :

function myHandler(data, ctx) { 
    console.log('myHandler(): this.bar: ' + this.bar); // <- no prob: this.bar 
    console.log(JSON.stringify(data)); 
} 

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, {bar: bar}); // scope object is just object 
} 

나는 여기에 머리 클럽 날에 사람이 필요합니다 ... '이'내 첫 번째 경우에, 물론 통과 할 때 이것은 전역 적 범위입니다. (전역 적으로 정의 된 함수입니다) ... 제 문제는 범위를 전달할 때 해당 컨텍스트에서 로컬로 정의 된 변수와 매개 변수에 액세스 할 때 생각한 것입니다 ... 이전에 이해 한 것을 흔들고 있습니까? JS? 이 작업을 수행하는 방법은 무엇입니까?

답변

16

은 BTW, 코드의 범위에 대한 몇 마디 :

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, this); // this here points to window object and not to the function scope 
} 

은 그래서 쓰기와 동일합니다 : 당신이 세계적으로 조회 함수를 정의하기 때문에

function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler, window); 
} 

너무입니다. 안에 doWork 함수를 쓸 때 은 MySvcWrap 개체를 가리 킵니다 (해당 함수는 해당 개체 내부에 정의되어 있기 때문에). 콜백 함수가 bar 변수를 볼 수있는 경우

이, 그것은 당신이 콜백으로 익명 함수를 보내이 경우 그

MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', 
     function (data, ctx) { 
      console.log('myHandler(): bar: ' + bar); 
      console.log(JSON.stringify(data)); 
     }, 
     this); // scope object is this 
} 

lookup(); 

처럼 동일한 범위에서 정의되어야한다, 그것은 조회 함수 내에서 정의된다 그것 때문에 로컬 변수에 접근 할 수있다. 하나 개의 기능은 지역에 액세스해야하는 이유 한편

function lookup() { 
    var bar = 'food'; // local var 
    var myHandler = function(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    }; 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

:

myHandler(): bar: food 
{"colors":["red","green"],"name":"Jones","what":"thang"} 

당신은 조회 함수 내에서 myHandler가 정의 할 수 있습니다, 쉽게 지원할 수 있도록 내 콘솔이 CAE에서 저를 보여줍니다 다른 함수의 변수? 어쩌면 다시 디자인 할 수 있습니다 ...

(function() { 
    var bar = 'food'; 

    function myHandler(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    } 
    MySvcWrap = { 
     doWork: function(p1, callback, scope) { 
      var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
      if (callback) { 
       callback.call(scope||this,result, scope); 
      } 
     } 
    } 
    MySvcWrap.doWork('thang', myHandler, this); 
    } 
)(); 

결과는 동일하지만 조회 : (그냥 선언하고 한 번 그 기능을 실행하면 작동합니다) 코드의 작업을 할

또 하나의 방법은, 대신 조회의를 익명 함수를 사용하는 것입니다 함수를 더 이상 ...

그리고 더 많은 아이디어가 작동하도록 ... 사실 당신은 바 변수가 정의 된 동일한 범위에서 콜백 처리기를 정의해야하므로 약간 까다로울 수 있지만 대신 :

function myHandler(bar) { // actually in this case better to call it createHandler 
    return function(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); 
     console.log(JSON.stringify(data)); 
    } 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler(bar), this); // scope object is this 
} 

그리고 몇 자원은 자바 스크립트 범위 지정 및 폐쇄에 대한 읽기 :

  1. Explaining JavaScript scope and closures
  2. Picking up Javascript - Closures and lexical scoping
  3. JavaScript: Advanced Scoping & Other Puzzles - 주제에 대한 아주 좋은 프리젠 테이션
+0

... this 확신은 데모 용으로 window 아니다 만드는 것을 범위를 볼 수 있습니다 핸들러 접근법. –

2
이 같은 구조 된 경우

이 코드는 작동합니다 이.

MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback.call(scope||this,result, scope); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 

    function myHandler(data, ctx) { 
     console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
     console.log(JSON.stringify(data)); 
    } 

    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

myHandler 함수는 lookup의 로컬 변수에 액세스 할 수 있으므로 closure로 둘러 쌀 수 있습니다.

같은 코드를 구조화하여 동일한 결과를 얻으려고합니다.

function myHandler(data, bar, ctx) { 
    console.log('myHandler(): bar: ' + bar); // <- prob: bar undefined 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback) { 
     var result = {colors: ['red', 'green'], name:'Jones', what: p1}; 
     if (callback) { 
      callback(result); 
     } 
    } 
} 
function lookup() { 
    var bar = 'food'; // local var 
    MySvcWrap.doWork('thang', myHandler.bind(this, bar)); // callback function is bound to the scope 
} 

lookup(); 

범위를 전달하는 대신 lookup 메서드 내에서 bind를 사용합니다. bind를 사용하면 해당 범위에서 전달하려는 로컬 변수를 추가 할 수도 있습니다.

물론 이전 버전의 브라우저에서는 바인드를 사용할 수 없으므로 프레임 워크를 통해 추가하거나 필요없는 경우 메서드를 추가하는 많은 코드 스 니펫 중 하나를 사용해야합니다.

0

나는 내 대답에 약간 빠르며, 맥심이 아래의 의견에 대해 말한대로 감사의 말을 전하고 싶다. 나는 일하러 가기 전에 빨리 게시하려고 노력한다. P 당신은 var하지만 this를 사용하지 않는 것 (lookup의 범위를 벗어납니다) 함수 myHandler에서 기능 lookup 변수에 액세스 할 수 바인딩을 사용하려는 경우

내가 내 첫 번째 대답에서 얻을려고했던 것은 대신.

var을 사용하면 다른 답변이 아래

가하는 방법에 대한 업데이트 된 예이다 증명 (그리고 아마도 최적의 경로입니다) 한 방법이다 lookup's 개인 범위와 그 안에 중첩 된 모든 기능에에만 액세스 할 수 있도록 것입니다 myHandler를 조회 범위 밖으로 완전히 벗어나는 myHandler에 조회 범위를 전달합니다. 다행히도 이것으로 빛을 비춰 줄 수 있기를 바랍니다.

"문제는 내가 원래의 기능에서 매개 변수와 로컬 변수에 대한 액세스를 제공하지 않는 객체 범위를 얻고 있다는 것입니다."까다로운 얻을 수 있습니다 내 이전 대답 this에 대한 의견에 명시된 당신이 내 첫 번째 예제에서했던 것처럼 조심하지 경우 global 범위에 물건을 추가 끝낼 수 있듯이

가. :(그래서 추가 한 hack'ish 체크 비트 내가 매개 변수를 좋아 Live Example

function myHandler(data, ctx) { 
    console.log('myHandler(): bar: ' + this.bar); // <- must use `this` 
    console.log('myHandler(): privateBar: ' + this.privateBar); // <- undefined because it's privately scoped 
    console.log(JSON.stringify(data)); 
} 
MySvcWrap = { 
    doWork: function(p1, callback, scope) { 
     var result = { 
      colors: ['red', 'green'], 
      name: 'Jones', 
      what: p1 
     }; 
     if (callback) { 
      callback.call(scope || this, result, scope); 
     } 
    } 
} 

function lookup() { 
    if(this == window) return lookup.call(lookup); // <- my hack'ish check 

    var privateBar = 'private food'; // private local var 
    this.bar = 'food'; // public local var 
    MySvcWrap.doWork('thang', myHandler, this); // scope object is this 
} 

lookup(); 

console.log('global space - bar: ' + this.bar); 
+1

'var bar = this.bar = 'food';'-이 경우 window 객체에'bar' 변수를 생성합니다. 그래서이 변수를 lookup 함수 밖에서 선언하고 여기에서 정의하는 것과 같거나' bar = 'food'', 나는'var'를 사용하지 않는다는 것을 의미합니다 ... 이제이 코드는 매우 까다 롭습니다. 그리고 변수'bar'도 이제는 전역 적이라는 사실을 숨 깁니다 – Maxym