简体   繁体   中英

How can I replace (wrap) methods in new methods programmatically?

I have several methods that I need to wrap in new methods in basically the same manner. My first solution doesn't work, and I understand why, but I don't know if there is a simple solution to this problem or if it can't be done the way that I want to do it.

Here's an example. I have objects ac that have an onClick method. I need to execute some code before the onClick methods. I tried the following:

// objects a-c
a = {onClick : function () { console.log('a'); }};
b = {onClick : function () { console.log('b'); }};
c = {onClick : function () { console.log('c'); }};

// first try
var arr = [a, b, c]
for (var i = 0; i < arr.length; i++) {
    var oldOnClick = arr[i].onClick;
    arr[i].onClick = function () {
        // new code here
        oldOnClick();
    }  
}

// outputs 'c'
// what i want is to maintain a reference to the original method
// so in this case, execute my new code and output 'a'
a.onClick();

This doesn't work because when the new method is called, oldOnClick will point to the method from the last iteration and not the to method when it was assigned.

Is there a simple solution that I'm overlooking?

What you need is closure:

for(var i=0, l=arr.length; i<l; i++){
 (function(i){
   var oldOnclick = arr[i].onClick; 
   //etc.
 })(i);
}

Javascript binding rules are pretty odd. Really, javascript is pretty odd.

I don't know that I'd call this the way to fix it, but by introducing a sub-function you can get introduce another bind and thereby fix this particular problem.

Your (modified for quick-y Chrome hacking) code becomes:

a = {onClick : function () { alert('a'); }};
b = {onClick : function () { alert('b'); }};
c = {onClick : function () { alert('c'); }};

var arr = [a, b, c]
for (var i = 0; i < arr.length; i++) {
    var oldOnClick = arr[i].onClick;
    arr[i].onClick = bind(oldOnClick);
}

a.onClick();
b.onClick();
c.onClick();

function bind(oldFunc)
{
    return function () {
        //New Code
        oldFunc();
    }  
}

The above code throws up three alerts: a, b, c. Anything replacing '//New Code' will be run at the right time.

did you tried with some AOP framwork for Javascript?

for example using jQuery AOP plugin:

jQuery.aop.before( {target: String, method: 'replace'}, 
  function(regex, newString) { 
    alert("About to replace string '" + this + "' with '" + newString + 
          "' using regEx '" + regex + "'");
  }
);

check also here .

var a = {onClick : function () { console.log('a'); }};
var b = {onClick : function () { console.log('b'); }};
var c = {onClick : function () { console.log('c'); }};

var arr = [a, b, c];
for (var i = 0; i < arr.length; i++) {
    var oldOnClick = arr[i].onClick;
    arr[i].onClick = wrapHandler(oldOnClick);  
}

function wrapHandler(handler) {
    return function() {
        console.log("New stuff");
        handler();
    }
}

a.onClick(); // outputs "New stuff" then "a"
b.onClick(); // outputs "New stuff" then "b"
b.onClick(); // outputs "New stuff" then "c"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM