원본을 참조하면서 JavaScript 함수 재정의


160

a()재정의하려는 함수가 있지만 a()컨텍스트에 따라 순서대로 원본 을 수행 해야합니다 . 예를 들어, 때때로 페이지를 생성 할 때 다음과 같이 재정의하고 싶습니다.

function a() {
    new_code();
    original_a();
}

때로는 이렇게 :

function a() {
    original_a();
    other_new_code();
}

original_a()오버 라이딩 내에서 어떻게 얻을 수 a()있습니까? 가능합니까?

이런 식으로 오버 라이딩에 대한 대안을 제안하지 마십시오. 나는이 방법에 대해 구체적으로 묻고 있습니다.

답변:


179

다음과 같이 할 수 있습니다.

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

original_a익명 함수 내부에 선언 하면 전역 네임 스페이스가 복잡해지지 않지만 내부 함수에서 사용할 수 있습니다.

의견에 언급 된 Nerdmaster와 마찬가지로 끝에를 포함해야합니다 (). 당신은 외부 함수를 호출하고 저장할 결과를 (두 개의 내부 기능 중 하나)에 a,에 외부 함수 자체를 저장하지 a.


25
나 자신과 같은 어떤 멍청이라도-마지막에 "()"에주의를 기울이십시오-그것 없이는 내부 함수가 아닌 외부 함수를 반환합니다 :)
Nerdmaster

@Nerdmaster 지적 해 주셔서 감사합니다. 사람들이 알아 차릴 수 있도록 메모를 추가했습니다.
Matthew Crumley

5
와우, 네임 스페이스 없이이 작업을 시도했지만 스택 오버플로가 발생했습니다.
gosukiwi

첫 번째와 마지막 괄호가 함수에서 제거되면 결과가 동일하다고 생각합니다. 여기에서처럼 (functi..하고 }). 그냥 일을 }();같이 동일한 결과를 생성 할 (function{})()괄호 1 세트가 방금 선언 질수 있기 때문에, 익명, 당신이 가지고있는 기능의 표현을 할당하는 데 사용됩니다. 그러나 ()를 수행하면 함수를 반환하는 것을 정의하지 않습니다.
Muhammad Umer

@MuhammadUmer 함수 표현식 주위의 괄호는 필요하지 않습니다. 그것들이 없으면 그것들이 포함되어 있기 때문에 첫 번째 몇 줄을 보면 외부 함수를 직접 할당하고 a호출하지 않고 반환 값을 저장하는 것처럼 보입니다 (적어도 나에게는) . 괄호는 다른 일이 있다는 것을 알려줍니다.
Matthew Crumley

88

프록시 패턴은 당신을 도울 수 있습니다

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

위의 코드는 "프록시"변수를 숨기는 함수로 코드를 래핑합니다. jQuery의 setArray-method를 클로저에 저장하고 덮어 씁니다. 그런 다음 프록시는 모든 호출을 메소드에 기록하고 호출을 원본에 위임합니다. apply (this, arguments)를 사용하면 호출자가 원래 메소드와 프록시 된 메소드의 차이점을 알 수 없습니다.


10
과연. '적용 (apply)'과 '인수 (arguments)'를 사용하면 다른 답변보다 훨씬 강력 해집니다
Robert Levy

이 패턴에는 jQuery가 필요하지 않습니다. jQuery의 함수 중 하나만 예로 사용합니다.
Kev

28

고마워 프록시 패턴이 실제로 도움이되었습니다 .... ... 실제로 전역 함수 foo를 호출하고 싶었습니다 .. 특정 페이지에서 몇 가지 확인을해야합니다. 그래서 나는 다음을 수행했다.

//Saving the original func
var org_foo = window.foo;

//Assigning proxy fucnc
window.foo = function(args){
    //Performing checks
    if(checkCondition(args)){
        //Calling original funcs
        org_foo(args);
    }
};

Thnx 이것은 정말 나를 도와


3
프록시 패턴을 따를 때 ,이 응답 코드에없는이 세부 사항을 구현하는 것이 중요한 경우가 있습니다. 대신 org_foo(args), call org_foo.call(this, args). thiswindow.foo가 정상적으로 호출 될 때와 같이 유지 됩니다 (프록시되지 않음). 이 답변
cellepo

10

다음과 같은 구문을 사용하여 함수를 재정의 할 수 있습니다.

function override(f, g) {
    return function() {
        return g(f);
    };
}

예를 들면 다음과 같습니다.

 a = override(a, function(original_a) {
      if (condition) { new_code(); original_a(); }
      else { original_a(); other_new_code(); }
 });

편집 : 오타가 수정되었습니다.


+1 이것은 다른 프록시 패턴 예제보다 훨씬 더 읽기 쉽습니다!
Mu Mind

1
...하지만 실수하지 않으면 이것은 인수가없는 함수 또는 적어도 미리 결정된 수의 인수로 제한됩니다. 가독성을 잃지 않고 임의의 수의 인수로 일반화 할 수있는 방법이 있습니까?
Mu Mind

@ MuMind : 예,하지만 프록시 패턴을 올바르게 사용하십시오- 내 대답을 참조하십시오 .
Dan Dascalescu

4

임의의 인수 전달 :

a = override(a, function(original_a) {
    if (condition) { new_code(); original_a.apply(this, arguments) ; }
    else { original_a.apply(this, arguments); other_new_code(); }
});

1
논쟁이 original_a아닌가?
조나단.

2

@Matthew Crumley가 제공하는 대답은 즉시 호출 된 함수 표현식을 사용하여 이전 'a'함수를 반환 된 함수의 실행 컨텍스트로 닫는 것입니다. 나는 이것이 최선의 대답이라고 생각하지만 개인적으로는 IIFE에 인수로 함수 'a'를 전달하는 것을 선호합니다. 더 이해하기 쉽다고 생각합니다.

   var a = (function(original_a) {
        if (condition) {
            return function() {
                new_code();
                original_a();
            }
        } else {
            return function() {
                original_a();
                other_new_code();
            }
        }
    })(a);

1

위의 예제 는 함수 재정의에 올바르게 적용 this되거나 arguments올바르게 전달 되지 않습니다 . 밑줄 _.wrap ()은 기존 함수를 래핑 this하고 arguments올바르게 적용 하고 전달 합니다. 참조 : http://underscorejs.org/#wrap


0

다른 사람이 작성한 코드가 있었고 코드에서 찾을 수없는 함수에 줄을 추가하고 싶었습니다. 따라서 해결 방법으로 재정의하고 싶었습니다.

그래도 해결책은 없었습니다.

내 경우에 효과가 있었던 것은 다음과 같습니다.

if (typeof originalFunction === "undefined") {
    originalFunction = targetFunction;
    targetFunction = function(x, y) {
        //Your code
        originalFunction(a, b);
        //Your Code
    };  
}

0

종종 여러 라이브러리의 함수를 재정의해야했기 때문에 비슷한 시나리오를위한 작은 도우미를 만들었습니다. 이 도우미는 "네임 스페이스"(함수 컨테이너), 함수 이름 및 재정의 함수를 허용합니다. 참조 된 네임 스페이스의 원래 기능을 새로운 네임 스페이스로 대체합니다.

새 함수는 원래 함수를 첫 번째 인수로, 원래 함수 인수를 나머지로 허용합니다. 매번 컨텍스트를 유지합니다. void 및 non-void 기능도 지원합니다.

function overrideFunction(namespace, baseFuncName, func) {
    var originalFn = namespace[baseFuncName];
    namespace[baseFuncName] = function () {
        return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
    };
}

예를 들어 부트 스트랩 사용 :

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
    // ... do stuff before base call
    baseFn(obj);
    // ... do stuff after base call
});

그래도 성능 테스트를 만들지 않았습니다. 시나리오에 따라 원치 않는 오버 헤드를 추가 할 수 있으며 크게 또는 그렇지 않을 수 있습니다.


0

내 의견으로는 최고 답변을 읽거나 유지할 수 없으며 다른 답변은 컨텍스트를 올바르게 바인딩하지 않습니다. 다음은 이러한 문제를 해결하기 위해 ES6 구문을 사용하는 읽기 쉬운 솔루션입니다.

const orginial = someObject.foo;
someObject.foo = function() {
  if (condition) orginial.bind(this)(...arguments);
};

그러나 ES6 구문을 사용하면 사용이 다소 제한됩니다. ES5에서 작동하는 버전이 있습니까?
eitch

0

그래서 내 대답은 결국 원래 객체를 가리키는 _this 변수를 사용할 수있는 솔루션이었습니다. "Square"의 새 인스턴스를 만들지 만 "Square"가 크기를 생성하는 방식을 싫어했습니다. 나는 그것이 나의 특정한 필요를 따라야한다고 생각했다. 그러나 그렇게하려면 사각형에 이미 this.height, this.GetVolume ()과 같은 사각형에 이미 존재하는 다른 함수를 호출하는 해당 함수의 내부로 업데이트 된 "GetSize"함수를 갖도록 사각형이 필요했습니다. 그러나 그렇게하려면 미친 해킹 없이이 작업을 수행해야했습니다. 그래서 여기 내 해결책이 있습니다.

다른 객체 이니셜 라이저 또는 도우미 기능.

this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
  this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons =  this.updateToolbarButtons(viewer);

다른 객체의 기능.

updateToolbarButtons = function(viewer) {
  var _viewer = viewer;
  return function(width, height){ 
blah blah black sheep I can refer to this.anything();
}
};

0

모든 상황에서 작동하는지 확실하지 않지만 우리의 경우 describeJest 에서 함수 를 재정 의하여 이름을 구문 분석하고 describe일부 기준을 충족하는 경우 전체 블록을 건너 뛸 수있었습니다.

우리를 위해 일한 것은 다음과 같습니다.

function describe( name, callback ) {
  if ( name.includes( "skip" ) )
    return this.describe.skip( name, callback );
  else
    return this.describe( name, callback );
}

여기서 중요한 두 가지 :

  1. 우리는 화살표 기능을 사용하지 않습니다 () =>.

    화살표 함수는에 대한 참조를 변경 this하며이를 파일이되어야합니다 this.

  2. 의 사용 this.describethis.describe.skip대신의 describedescribe.skip.

다시 말하지만, 그것이 누구에게나 가치가 있는지 확실하지는 않지만 우리는 원래 Matthew Crumley의 훌륭한 답변을 피 하려고 시도했지만 조건부에서 구문을 분석하기 위해 메소드를 함수로 만들고 매개 변수를 수락해야했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.