Orijinal belgeye başvururken bir JavaScript işlevini geçersiz kılma


160

Ben, bir işlevi var a()ben geçersiz kılmaya değil, aynı zamanda orijinal sahip a()bağlama göre bir sırayla gerçekleştirilebilir. Örneğin, bazen bir sayfa oluştururken şu şekilde geçersiz kılmak isteyeceğim:

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

ve bazen böyle:

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

Bunu original_a()aşırı sürüşten nasıl alabilirim a()? Hatta mümkün mü?

Lütfen bu şekilde aşırı sürmeye alternatifler önermeyin, çoğunu biliyorum. Özellikle bu şekilde soruyorum.

Yanıtlar:


179

Böyle bir şey yapabilirsiniz:

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

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

Bildirme original_aanonim işlevi içinde genel ad alanını doldurmasını tutar, ancak iç fonksiyonlarda kullanılabilir.

Nerdmaster'ın yorumlarda belirttiği gibi, sonuna eklediğinizden emin olun (). Dış işlevi çağırmak ve sonucu (iki iç işlevden biri) saklamak istiyorsunuz a, dış işlevi kendisi saklamayın a.


25
Orada benim gibi herhangi bir aptal için - sonunda "()" dikkat - bu olmadan, bu iç fonksiyonları değil, dış işlevi döndürür :)
Nerdmaster

@ Nerdmaster Bunu işaret ettiğiniz için teşekkürler. İnsanların fark etmesine yardımcı olmak için bir not ekledim.
Matthew Crumley

5
Vay be, bunu bir ad alanı olmadan yapmaya çalıştım ve bir yığın taşması var, lol.
gosukiwi

ilk ve son parantezler işlevden kaldırılırsa sonuç aynı olacağını düşünüyorum. Buradan (functi..ve gibi }). Sadece yapmak }();aynı sonucu üretecektir, çünkü (function{})()1. parantez kümesi kullanıldığından, atamanız gereken sadece anonim, işlev ifadesini beyan edemezsiniz. Ama () yaparak sadece bir işlev döndüren hiçbir şey tanımlamıyorsunuzdur.
Muhammed Umer

@MuhammadUmer İşlev ifadesinin çevresindeki parantezlerin gerekli olmadığı konusunda haklısınız. Onları dahil ettim, çünkü onlar olmadan, ilk çift çizgilere bakarsanız, (en azından bana), dış işlevi doğrudan atadığınız, açağırmamanız ve dönüş değerini saklamanız gibi görünüyor. Parantezler bana başka bir şey olduğunu bildiriyor.
Matthew Crumley

88

Vekil desen size yardımcı olabilir:

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

Yukarıdaki kod "proxy" değişkenini gizlemek için bir fonksiyona sarar. JQuery'nin setArray yöntemini bir kapanışta kaydeder ve üzerine yazar. Proxy daha sonra yönteme yapılan tüm çağrıları günlüğe kaydeder ve çağrıyı orijinaline devreder. Apply (this, arguments) komutunu kullanmak, arayanın orijinal ve proxy yöntem arasındaki farkı fark edemeyeceğini garanti eder.


10
aslında. 'uygula' ve 'argümanların' kullanımı bunu diğer cevaplardan çok daha sağlam kılar
Robert Levy

Bu modelin jQuery gerektirmediğine dikkat edin; örnek olarak sadece jQuery işlevlerinden birini kullanıyorlar.
Kev

28

Teşekkürler çocuklar proxy desen gerçekten yardımcı oldu ..... Aslında küresel bir işlev foo aramak istedim .. Bazı sayfalarda bazı kontroller yapmak gerekiyor. Ben de aşağıdakileri yaptım.

//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 bu gerçekten bana yardımcı oldu


3
Proxy modelini takip ederken , bazı durumlarda bu Cevaplayıcı kodunda bulunmayan bu ayrıntıyı uygulamak önemlidir: Bunun yerine org_foo(args), arayın org_foo.call(this, args). Yani korur thiswindow.foo aramalar normal (proxy değil) ne zaman olurdu olarak. Bu
Cevaba

10

Aşağıdaki gibi bir yapı kullanarak bir işlevi geçersiz kılabilirsiniz:

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

Örneğin:

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

Düzenleme: Yazım hatası düzeltildi.


+1 Bu, diğer proxy kalıp örneklerinden çok daha okunabilir!
Mu Mind

1
... ancak yanılmıyorsam, bu sıfır bağımsız değişken işlevleriyle veya en azından önceden belirlenmiş sayıda bağımsız değişkenle sınırlıdır. Okunabilirliği kaybetmeden rasgele sayıda argümana genellemenin bir yolu var mı?
Mu Mind

@MuMind: evet, ancak proxy kalıbını doğru kullanmak - cevabımı görün .
Dan Dascalescu

4

Rasgele argümanlar iletmek:

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
iddialar original_aolsa söz olmaz mı?
Jonathan.

2

@Matthew Crumley'nin verdiği cevap, eski 'a' işlevini döndürülen işlevin yürütme bağlamına kapatmak için hemen çağrılan işlev ifadelerini kullanmaktır. Bence bu en iyi cevaptı, ama kişisel olarak, 'a' işlevini argüman olarak IIFE'ye geçirmeyi tercih ederim. Bence daha anlaşılır.

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

1

Yukarıdaki örnekler , işlev geçersiz kılma işlemine doğru bir şekilde uygulanmaz thisveya argumentsdoğru şekilde aktarılmaz. Alt çizgi _.wrap () mevcut işlevleri sarar, uygular thisve argumentsdoğru şekilde geçer . Bkz. Http://underscorejs.org/#wrap


0

Başka biri tarafından yazılmış bazı kod vardı ve kodda bulamadım bir işleve bir satır eklemek istedim. Bu yüzden bir geçici çözüm olarak geçersiz kılmak istedim.

Hiçbir çözüm benim için işe yaramadı.

Benim durumumda işe yarayan şey:

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

0

Benzer bir senaryo için küçük bir yardımcı oluşturdum çünkü sık sık birkaç kütüphaneden fonksiyonları geçersiz kılmam gerekiyordu. Bu yardımcı bir "ad alanı" (işlev kabı), işlev adı ve geçersiz kılma işlevini kabul eder. Belirtilen ad alanındaki orijinal işlevi yenisiyle değiştirir.

Yeni işlev, orijinal işlevi ilk argüman olarak kabul eder ve orijinal işlev argümanları geri kalanı olarak kabul eder. Bağlamı her zaman koruyacaktır. Geçersiz ve geçersiz işlevleri de destekler.

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

Örneğin Bootstrap ile kullanım:

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

Yine de hiçbir performans testi oluşturmadım. Muhtemelen senaryolara bağlı olarak önemli olabilecek veya olmayabilecek bazı istenmeyen ek yükler ekleyebilir.


0

Bence en iyi cevaplar okunamaz / sürdürülemez ve diğer cevaplar bağlamı düzgün bir şekilde bağlamaz. Her iki sorunu da çözmek için ES6 sözdizimini kullanan okunabilir bir çözüm.

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

Ancak ES6 sözdizimini kullanmak, kullanımı oldukça kısıtlı hale getirir. ES5'te çalışan bir sürümünüz var mı?
11'de eitch

0

Böylece cevabım, orijinal nesneye işaret eden _this değişkenini kullanmama izin veren bir çözüm oldu. "Square" in yeni bir örneğini oluşturuyorum ancak "Square" 'in bu boyuttan yaratmasından nefret ediyorum. Özel ihtiyaçlarımı karşılaması gerektiğini düşündüm. Ancak bunu yapmak için ben kare bu.height, this.GetVolume () gibi zaten mevcut diğer işlevleri çağıran bu işlevin iç ile güncellenmiş bir "GetSize" işlevi olması gerekiyordu. Ama bunu yapabilmek için bunu çılgın bir hack yapmadan yapmam gerekiyordu. İşte benim çözümüm.

Başka bir Nesne başlatıcı veya yardımcı işlevi.

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

Diğer nesnede işlev.

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

0

Her koşulda çalışıp çalışmayacağından emin değilim, ancak bizim durumumuzda, describeJest'teki işlevi geçersiz kılmaya çalışıyorduk, böylece adı ayrıştırabilir ve bütünü atlayabilirizdescribe bazı ölçütleri karşıladıysa bloğu .

Bizim için işe yarayanlar:

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

Burada kritik olan iki şey:

  1. Ok işlevi kullanmıyoruz () => .

    Ok işlevleri referansı değiştirir thisve dosya olması gerekir this.

  2. Kullanımı this.describeve this.describe.skipbunun yerine sadece bir describeve describe.skip.

Yine, kimseye değer verdiğinden emin değiliz ama aslında Matthew Crumley'in mükemmel cevabından kurtulmaya çalıştık, ancak yöntemimizi bir işlev haline getirmek ve koşullu olarak ayrıştırmak için paramları kabul etmek gerekiyordu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.