การเพิ่มโค้ดลงในฟังก์ชันจาวาสคริปต์โดยทางโปรแกรม


115

ฉันกำลังพยายามปรับแต่งไลบรารี JS ที่มีอยู่โดยไม่ต้องแก้ไขโค้ด JS ดั้งเดิม โค้ดนี้โหลดไฟล์ JS ภายนอกสองสามไฟล์ที่ฉันสามารถเข้าถึงได้และสิ่งที่ฉันต้องการทำคือเปลี่ยนหนึ่งในฟังก์ชันที่มีอยู่ในไฟล์ต้นฉบับโดยไม่ต้องคัดลอกและวางสิ่งทั้งหมดลงในไฟล์ JS ที่สอง
ตัวอย่างเช่นขีด จำกัด ปิด JS อาจมีฟังก์ชันดังนี้:

var someFunction = function(){
    alert("done");
}

ฉันต้องการที่จะสามารถต่อท้ายหรือต่อท้ายโค้ด JS บางส่วนในฟังก์ชันนั้นได้ เหตุผลหลักก็คือใน JS ที่ไม่สามารถแตะต้องได้เดิมฟังก์ชั่นนั้นค่อนข้างใหญ่มากและถ้า JS นั้นได้รับการอัปเดตฟังก์ชันที่ฉันเขียนทับด้วยจะล้าสมัย

ฉันไม่แน่ใจว่าเป็นไปได้ทั้งหมด แต่ฉันคิดว่าจะตรวจสอบ


3
คำตอบแรกนี้จะช่วยคุณได้
DarkAjax

คุณต้องการฟังก์ชั่นเรียกกลับหรือไม่?
sinemetu1

คำตอบ:


221

หากsomeFunctionมีให้ใช้งานทั่วโลกคุณสามารถแคชฟังก์ชันสร้างของคุณเองและให้คุณเรียกมัน

ถ้าเป็นต้นฉบับ ...

someFunction = function() {
    alert("done");
}

คุณจะทำสิ่งนี้ ...

someFunction = (function() {
    var cached_function = someFunction;

    return function() {
        // your code

        var result = cached_function.apply(this, arguments); // use .apply() to call it

        // more of your code

        return result;
    };
})();

นี่คือซอ


สังเกตว่าฉันใช้.applyเพื่อเรียกฟังก์ชันแคช สิ่งนี้ช่วยให้ฉันรักษามูลค่าที่คาดหวังไว้thisและส่งผ่านข้อโต้แย้งใด ๆ ที่ส่งผ่านมาเป็นอาร์กิวเมนต์แต่ละรายการโดยไม่คำนึงถึงจำนวนข้อโต้แย้ง


10
คำตอบนี้ควรเป็นคำตอบอันดับต้น ๆ - รักษาการทำงานของฟังก์ชันที่เป็นปัญหา ... +1 จากฉัน
Reid

17
+1 สำหรับใช้apply: นี่คือคำตอบเดียวว่ามันแก้ปัญหา
lonesomeday

2
@minitech: อะไร ... คุณไม่คุ้นเคยกับfuncitonคำหลักของ JavaScript ? ;) ขอบคุณสำหรับการแก้ไข

1
@gdoron: ฉันเพิ่งเพิ่มความคิดเห็น เว้นแต่ฉันแค่สับสนจริงๆตอนนี้ผมไม่ทราบว่าของเบราว์เซอร์ใด ๆ .apply()ที่จะไม่ยอมรับข้อโต้แย้งที่เกิดขึ้นจริงเป็นวัตถุหาเรื่องสอง แต่ถ้าเป็นวัตถุคล้ายอาร์เรย์เช่นวัตถุ jQuery ก็ใช่เบราว์เซอร์บางตัวจะส่งข้อผิดพลาด

2
คำตอบนี้ช่วยแก้ปัญหาในการใช้ออบเจ็กต์อินสแตนซ์เพื่อควบคุมวิดีโอ YouTube แต่ละรายการที่ฝังผ่าน YouTube Iframe API คุณไม่รู้มานานแค่ไหนแล้วที่ฉันพยายามแก้ไขความจริงที่ว่า API ต้องการให้เรียกกลับเป็นฟังก์ชันเดียวทั่วโลกเมื่อฉันพยายามสร้างคลาสเพื่อจัดการข้อมูลวิดีโอแต่ละรายการด้วยวิธีโมดูลาร์ที่ไม่ซ้ำใคร คุณคือฮีโร่ของฉันในวันนี้
Carnix

32

ก่อนอื่นให้เก็บฟังก์ชันจริงไว้ในตัวแปร ..

var oldFunction = someFunction;

จากนั้นกำหนดของคุณเอง:

someFunction = function(){
  // do something before
  oldFunction();
  // do something after
};

9
หากฟังก์ชันของคุณเป็นวิธีการที่คุณจะต้องใช้applyเพื่อเรียกมัน ดูคำตอบของ am
hugomg

มันใช้งานได้สำหรับฉัน แต่จำเป็นต้องแทนที่someFunctionด้วยwindow.someFunctionรหัสด้านบน สาเหตุที่ฟังก์ชั่นของฉันถูกประกาศภายใน$(document).ready()ตัวจัดการjquery
Nelson

10

คุณสามารถสร้างฟังก์ชันที่เรียกรหัสของคุณจากนั้นเรียกใช้ฟังก์ชัน

var old_someFunction = someFunction;
someFunction = function(){
    alert('Hello');
    old_someFunction();
    alert('Goodbye');
}

6

ฉันไม่ทราบว่าคุณสามารถอัปเดตฟังก์ชันได้หรือไม่ แต่ขึ้นอยู่กับว่ามีการอ้างอิงอย่างไรคุณสามารถสร้างฟังก์ชันใหม่แทนได้:

var the_old_function = someFunction;
someFunction = function () {
    /* ..My new code... */
    the_old_function();
    /* ..More of my new code.. */
}

5

นอกจากนี้ หากคุณต้องการเปลี่ยนบริบทเฉพาะที่คุณต้องสร้างฟังก์ชันใหม่ ตัวอย่างเช่น:

var t = function() {
    var a = 1;
};

var z = function() {
    console.log(a);
};

ตอนนี้

z() // => log: undefined

แล้ว

var ts = t.toString(),
    zs = z.toString();

ts = ts.slice(ts.indexOf("{") + 1, ts.lastIndexOf("}"));
zs = zs.slice(zs.indexOf("{") + 1, zs.lastIndexOf("}"));

var z = new Function(ts + "\n" + zs);

และ

z() // => log: 1

แต่นี่เป็นเพียงตัวอย่างที่ง่ายที่สุด ยังต้องใช้เวลาอีกมากในการจัดการข้อโต้แย้งความคิดเห็นและการคืนค่า นอกจากนี้ยังมีหลุมพรางมากมาย
toString | ฝาน | indexOf | lastIndexOf | ฟังก์ชั่นใหม่


1

รูปแบบพร็อกซี (ตามที่ผู้ใช้ 1106925 ใช้) สามารถใส่ไว้ในฟังก์ชันได้ สิ่งที่ฉันเขียนไว้ด้านล่างนี้ใช้ได้กับฟังก์ชันที่ไม่ได้อยู่ในขอบเขตสากลและแม้แต่ในต้นแบบ คุณจะใช้มันดังนี้:

extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

ในตัวอย่างด้านล่างคุณสามารถเห็นฉันใช้ฟังก์ชันเพื่อขยาย test.prototype.doIt ()

// allows you to prepend or append code to an existing function
function extender (container, funcName, prepend, append) {

    (function() {

        let proxied = container[funcName];

        container[funcName] = function() {
            if (prepend) prepend.apply( this );
            let result = proxied.apply( this, arguments );
            if (append) append.apply( this );
            return result;
        };

    })();

}

// class we're going to want to test our extender on
class test {
    constructor() {
        this.x = 'instance val';
    }
    doIt (message) {
        console.log(`logged: ${message}`);
        return `returned: ${message}`;
    }
}

// extends test.prototype.doIt()
// (you could also just extend the instance below if desired)
extender(
    test.prototype, 
    'doIt', 
    function () { console.log(`prepended: ${this.x}`) },
    function () { console.log(`appended: ${this.x}`) }
);

// See if the prepended and appended code runs
let tval = new test().doIt('log this');
console.log(tval);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.