มีวิธีที่จะทำให้ฟังก์ชันใด ๆ ส่งออกคำสั่ง console.log เมื่อถูกเรียกใช้โดยการลงทะเบียน global hook ที่ใดที่หนึ่ง (นั่นคือโดยไม่ต้องแก้ไขฟังก์ชันจริง) หรือด้วยวิธีการอื่น ๆ ?
มีวิธีที่จะทำให้ฟังก์ชันใด ๆ ส่งออกคำสั่ง console.log เมื่อถูกเรียกใช้โดยการลงทะเบียน global hook ที่ใดที่หนึ่ง (นั่นคือโดยไม่ต้องแก้ไขฟังก์ชันจริง) หรือด้วยวิธีการอื่น ๆ ?
คำตอบ:
ต่อไปนี้เป็นวิธีเพิ่มฟังก์ชันทั้งหมดในเนมสเปซส่วนกลางด้วยฟังก์ชันที่คุณเลือก:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
return fn.apply(this, arguments);
}
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name);
});
ข้อเสียประการหนึ่งคือไม่มีฟังก์ชันใด ๆ ที่สร้างขึ้นหลังจากการโทรaugment
จะมีลักษณะการทำงานเพิ่มเติม
fn.apply(this, arguments);
เป็นreturn fn.apply(this, arguments);
return
ในฟังก์ชันด้านในสุด
สำหรับฉันนี่ดูเหมือนวิธีแก้ปัญหาที่หรูหราที่สุด:
(function() {
var call = Function.prototype.call;
Function.prototype.call = function() {
console.log(this, arguments); // Here you can do whatever actions you want
return call.apply(this, arguments);
};
}());
มีวิธีใหม่ในการใช้Proxyเพื่อให้บรรลุฟังก์ชันนี้ใน JS สมมติว่าเราต้องการมีconsole.log
เมื่อใดก็ตามที่มีการเรียกใช้ฟังก์ชันของคลาสเฉพาะ:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const foo = new TestClass()
foo.a() // nothing get logged
เราสามารถแทนที่การสร้างอินสแตนซ์คลาสของเราด้วยพร็อกซีที่แทนที่แต่ละคุณสมบัติของคลาสนี้ ดังนั้น:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const logger = className => {
return new Proxy(new className(), {
get: function(target, name, receiver) {
if (!target.hasOwnProperty(name)) {
if (typeof target[name] === "function") {
console.log(
"Calling Method : ",
name,
"|| on : ",
target.constructor.name
);
}
return new Proxy(target[name], this);
}
return Reflect.get(target, name, receiver);
}
});
};
const instance = logger(TestClass)
instance.a() // output: "Calling Method : a || on : TestClass"
ตรวจสอบว่าใช้งานได้จริงใน Codepen
โปรดจำไว้ว่าการใช้งานProxy
ทำให้คุณมีฟังก์ชันมากกว่าการบันทึกชื่อคอนโซล
วิธีนี้ใช้ได้กับ Node.jsด้วย
หากคุณต้องการบันทึกที่ตรงเป้าหมายมากขึ้นรหัสต่อไปนี้จะบันทึกฟังก์ชันการเรียกใช้สำหรับวัตถุเฉพาะ คุณยังสามารถปรับเปลี่ยนต้นแบบวัตถุเพื่อให้อินสแตนซ์ใหม่ทั้งหมดได้รับการบันทึกด้วย ฉันใช้ Object.getOwnPropertyNames แทนสำหรับ ... ในดังนั้นจึงทำงานกับคลาส ECMAScript 6 ซึ่งไม่มีวิธีการแจกแจง
function inject(obj, beforeFn) {
for (let propName of Object.getOwnPropertyNames(obj)) {
let prop = obj[propName];
if (Object.prototype.toString.call(prop) === '[object Function]') {
obj[propName] = (function(fnName) {
return function() {
beforeFn.call(this, fnName, arguments);
return prop.apply(this, arguments);
}
})(propName);
}
}
}
function logFnCall(name, args) {
let s = name + '(';
for (let i = 0; i < args.length; i++) {
if (i > 0)
s += ', ';
s += String(args[i]);
}
s += ')';
console.log(s);
}
inject(Foo.prototype, logFnCall);
นี่คือ Javascript บางส่วนที่แทนที่เพิ่ม console.log ให้กับทุกฟังก์ชันใน Javascript เล่นกับมันบนRegex101 :
$re = "/function (.+)\\(.*\\)\\s*\\{/m";
$str = "function example(){}";
$subst = "$& console.log(\"$1()\");";
$result = preg_replace($re, $subst, $str);
มันเป็นการ 'แฮ็กที่รวดเร็วและสกปรก' แต่ฉันพบว่ามีประโยชน์สำหรับการดีบัก หากคุณมีฟังก์ชั่นจำนวนมากระวังเพราะจะเพิ่มโค้ดจำนวนมาก นอกจากนี้ RegEx นั้นเรียบง่ายและอาจใช้ไม่ได้กับชื่อ / การประกาศฟังก์ชันที่ซับซ้อนมากขึ้น
คุณสามารถแนบฟังก์ชันของคุณเองกับ console.log สำหรับทุกสิ่งที่โหลดได้
console.log = function(msg) {
// Add whatever you want here
alert(msg);
}