자바 스크립트 : 기능 확장


94

내가 원하는 주된 이유는 초기화 기능을 확장하고 싶기 때문입니다.

이 같은:

// main.js

window.onload = init();
function init(){
     doSomething();
}

// extend.js

function extends init(){
    doSomethingHereToo();
}

그래서 PHP에서 클래스를 확장하는 것처럼 함수를 확장하고 싶습니다.

그리고 다른 파일에서도 확장하고 싶습니다. 예를 들어 원래 init 함수 main.jsextended.js.



답변:


103

실제로하려는 작업과 수행중인 컨텍스트에 대한 더 넓은 시야 를 통해 질문에 대한 문자적인 답변 보다 더 나은 답변을 드릴 수 있다고 확신 합니다.

그러나 여기에 문자 그대로 대답이 있습니다.

이러한 함수를 어딘가의 속성에 할당하는 경우 원래 함수를 래핑하고 대신 속성에 대체 항목을 넣을 수 있습니다.

// Original code in main.js
var theProperty = init;

function init(){
     doSomething();
}

// Extending it by replacing and wrapping, in extended.js
theProperty = (function(old) {
    function extendsInit() {
        old();
        doSomething();
    }

    return extendsInit;
})(theProperty);

함수가 아직 객체에 있지 않은 경우 위의 작업을 용이하게하기 위해 해당 함수를 배치하는 것이 좋습니다. 예를 들면 :

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {};

    publicSymbols.init = init;
    function init() {
    }

    return publicSymbols;
})();

// In extended.js
(function() {
    var oldInit = MyLibrary.init;
    MyLibrary.init = extendedInit;
    function extendedInit() {
        oldInit.apply(MyLibrary); // Use #apply in case `init` uses `this`
        doSomething();
    }
})();

그러나 거기에 같은 것을 할 수있는 더 나은 방법. 예를 들어 init기능 을 등록하는 수단을 제공 합니다.

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {},
        initfunctions = [];

    publicSymbols.init = init;
    function init() {
        var funcs = initFunctions;

        initFunctions = undefined;

        for (index = 0; index < funcs.length; ++index) {
            try { funcs[index](); } catch (e) { }
        }
    }

    publicSymbols.addInitFunction = addInitFunction;
    function addInitFunction(f) {
        if (initFunctions) {
            // Init hasn't run yet, rememeber it
            initFunctions.push(f);
        }
        else {
            // `init` has already run, call it almost immediately
            // but *asynchronously* (so the caller never sees the
            // call synchronously)
            setTimeout(f, 0);
        }
    }

    return publicSymbols;
})();

(위의 내용 중 대부분은 좀 더 간결하게 작성 될 수 있지만, 저는 publicSymbols평소 pubs또는 익명의 객체 리터럴 보다는 같은 명확한 이름을 사용하고 싶었습니다 . 익명 함수를 사용하려면 훨씬 더 간결하게 작성할 수 있지만, 저는 그렇지 않습니다. 익명의 기능을 많이 신경 쓰지 않습니다 .)


훌륭한 답변에 감사드립니다. 두 번째 예제의 문제는 확장중인 함수의 결과가 필요할 수 있다는 것입니다.
Gerhard Davids 2013

64

이 문제를 해결하는 방법에는 여러 가지가 있습니다. 목적이 무엇인지에 따라 다릅니다. 함수를 동일한 컨텍스트에서 실행하려는 경우 다음을 사용할 수 있습니다 .apply().

function init(){
  doSomething();
}
function myFunc(){
  init.apply(this, arguments);
  doSomethingHereToo();
}

최신으로 바꾸려면 init다음과 같이 표시됩니다.

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};

2
때로는 .call대신 방법을 원할 수 있습니다 .apply. StackOverflow 질문을 참조하십시오 .
MrDanA 2014-06-11

@Nick, 기존 함수를 확장하는 JavaScript 예제가 매우 유용하다는 것을 알았지 만 jQuery를 통해 동일한 작업이 어떻게 수행되는지 궁금합니다.
Sunil 2014

+1 감사합니다. 이것은 원래 js를 수정하지 않고 타사 플러그인을 패치하려는 경우 매우 편리합니다.
GFoley83

1
매개 변수를 예상하고 값을 반환하는 함수와 함께 사용하는 방법을 잘 모르겠습니다.
Gerfried

5

다른 메소드는 훌륭하지만 init에 첨부 된 프로토 타입 함수를 보존하지 않습니다. 이를 해결하기 위해 다음을 수행 할 수 있습니다 (Nick Craver의 게시물에서 영감을 얻음).

(function () {
    var old_prototype = init.prototype;
    var old_init = init;
    init = function () {
        old_init.apply(this, arguments);
        // Do something extra
    };
    init.prototype = old_prototype;
}) ();

5

다른 옵션은 다음과 같습니다.

var initial = function() {
    console.log( 'initial function!' );
}

var iWantToExecuteThisOneToo = function () {
    console.log( 'the other function that i wanted to execute!' );
}

function extendFunction( oldOne, newOne ) {
    return (function() {
        oldOne();
        newOne();
    })();
}

var extendedFunction = extendFunction( initial, iWantToExecuteThisOneToo );

0

이것은 매우 간단하고 간단합니다. 코드를보세요. 자바 스크립트 확장의 기본 개념을 파악하십시오.

먼저 자바 스크립트 기능을 확장 해 보겠습니다.

function Base(props) {
    const _props = props
    this.getProps = () => _props

    // We can make method private by not binding it to this object. 
    // Hence it is not exposed when we return this.
    const privateMethod = () => "do internal stuff" 

    return this
}

다음과 같은 방법으로 자식 함수를 생성하여이 함수를 확장 할 수 있습니다.

function Child(props) {
    const parent = Base(props)
    this.getMessage = () => `Message is ${parent.getProps()}`;

    // You can remove the line below to extend as in private inheritance, 
    // not exposing parent function properties and method.
    this.prototype = parent
    return this
}

이제 다음과 같이 Child 함수를 사용할 수 있습니다.

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

이와 같이 자바 스크립트 클래스를 확장하여 자바 스크립트 함수를 생성 할 수도 있습니다.

class BaseClass {
    constructor(props) {
        this.props = props
        // You can remove the line below to make getProps method private. 
        // As it will not be binded to this, but let it be
        this.getProps = this.getProps.bind(this)
    }

    getProps() {
        return this.props
    }
}

다음과 같이 Child 함수로이 클래스를 확장 해 보겠습니다.

function Child(props) {
    let parent = new BaseClass(props)
    const getMessage = () => `Message is ${parent.getProps()}`;
    return { ...parent, getMessage} // I have used spread operator. 
}

다시 비슷한 결과를 얻기 위해 다음과 같이 Child 함수를 사용할 수 있습니다.

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

Javascript는 매우 쉬운 언어입니다. 우리는 거의 모든 것을 할 수 있습니다. 행복한 JavaScripting ... 당신의 경우에 사용할 아이디어를 줄 수 있기를 바랍니다.


-1

extendFunction.js 사용

init = extendFunction(init, function(args) {
  doSomethingHereToo();
});

그러나 특정 경우에는 전역 온로드 기능을 확장하는 것이 더 쉽습니다.

extendFunction('onload', function(args) {
  doSomethingHereToo();
});

저는 실제로 귀하의 질문이 마음에 들었습니다. 다른 사용 사례에 대해 생각하게합니다.

자바 스크립트 이벤트의 경우 처리기를 추가하고 제거하고 싶지만 extendFunction의 경우 나중에 기능을 제거 하려면 어떻게해야 합니까? 확장 함수에 .revert 메서드를 쉽게 추가 할 수 있으므로 init = init.revert()원래 함수를 반환합니다. 분명히 이것은 꽤 나쁜 코드로 이어질 수 있지만 아마도 코드베이스의 외부 부분을 건드리지 않고도 무언가를 수행 할 수 있습니다.

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