I want to avoid inserting console.log() statements in every method of a JavaScript class, but I want to know which members are called and which aren't by running the code and capturing debug output.
Is there any kind of hook or handler I can use, or a debugging library perhaps, so I can just modify the class or an instance in one place, and then see which members are called via the console (or similar)?
The class has a lot of members, so this would be a useful time saver for me! As well as enable me to easily turn logging on and off more easily.
My first Q.. thanks :)
You can wrap all the functions on the instance. For instance, assuming obj is the object you want to watch:
function wrapObjectFunctions(obj, before, after) {
var key, value;
for (key in obj) {
value = obj[key];
if (typeof value === "function") {
wrapFunction(obj, key, value);
}
}
function wrapFunction(obj, fname, f) {
obj[fname] = function() {
var rv;
if (before) {
before(fname, this, arguments);
}
rv = f.apply(this, arguments); // Calls the original
if (after) {
after(fname, this, arguments, rv);
}
console.log( /*...*/ );
return rv;
};
}
}
(arguments in the above, if you're not familiar with it, is a magic pseudo-array provided by JavaScript which contains the arguments that the function was called with. I know it looks like pseudo-code, but it isn't.)
Live Example:
function Thing(name) {
this.name = name;
}
Thing.prototype.sayName = function () {
console.log("My name is " + this.name);
};
var t = new Thing("Fred");
console.log("Before wrapping:");
t.sayName(); // My name is Fred, with no extra logging
console.log("----");
wrapObjectFunctions(
t,
function(fname) {
console.log("LOG: before calling " + fname);
},
function(fname) {
console.log("LOG: after calling " + fname);
}
);
console.log("After wrapping:");
t.sayName(); // My name is Fred, with no extra logging
function wrapObjectFunctions(obj, before, after) {
var key, value;
for (key in obj) {
value = obj[key];
if (typeof value === "function") {
wrapFunction(obj, key, value);
}
}
function wrapFunction(obj, fname, f) {
obj[fname] = function() {
var rv;
if (before) {
before(fname, this, arguments);
}
rv = f.apply(this, arguments); // Calls the original
if (after) {
after(fname, this, arguments, rv);
}
console.log(/*...*/);
return rv;
};
}
}
Related
I would like to know if there is a way to be notified when any object function is called, in javascript.
For example, I would like to do something like the following:
If I have an object like this:
myObject = {
functionOne: function(argumentOne) {
// do some stuff
},
functionTwo: function() {
// do some stuff
}
}
And add a listener (or anything else to track the actions taken place on this object):
myObject.addEventListener('onFunctionCall', functionHasBeenCalled);
When I call:
myObject.functionOne('hello');
The listener handler to fire with information about the called function:
functionHasBeenCalled(calledFunctionData) {
console.log(calledFunctionData.functionName + ' has been called');
console.log('with argument: ' + calledFunctionData.functionArgument);
}
And the console output to be:
functionOne has been called
with argument: hello
Maybe there is another way, to implement this, not with an event listener, but I have no clue.
Thanks!
One approach could be to use a Proxy to create "tracked" objects where you intercept any method invocations:
function trackMethodCalls(obj) {
const handler = {
// anytime we do obj.someMethod
// we actually return the interceptedMethod instead
get(target, propKey, receiver) {
const method = target[propKey];
// we only do something special if we're working with a function
// on the object. If the property isn't a function we can just return
// it as normal.
if (typeof method !== 'function') {
return method;
}
return function interceptedMethod(...args) {
const result = method.apply(this, args);
console.log(
`${propKey}(${args.join(",")}) = ${JSON.stringify(result)}`
);
return result;
};
}
};
return new Proxy(obj, handler);
}
const obj = {
val: 2,
double(x) {
return this.val * x;
}
};
const trackedObj = trackMethodCalls(obj);
trackedObj.double(4);
If you want to mutate an object rather than instrumenting it via a Proxy, you should just directly overwrite its methods:
function addTrackedMethods(obj) {
for (const [methodName, method] of Object.entries(obj).filter(
([, method]) => typeof method === "function"
)) {
obj[methodName] = function interceptedMethod(...args) {
const result = method.apply(this, args);
console.log(
`${methodName}(${args.join(",")}) = ${JSON.stringify(result)}`
);
return result;
};
}
}
const obj = {
val: 2,
double(x) {
return this.val * x;
}
};
addTrackedMethods(obj);
obj.double(4);
You can't do that without getting between the object and the thing calling it, or modifying the object. There isn't any "tap into this interaction" that doesn't involve one or the other.
Here's each:
Getting between them
If you can get between the object and the thing calling it, you can create a new object to do that, with functions duplicating the functions on the target object that call it. Here's a simple example:
const original = {
value: 42,
doSomething() {
console.log(`original doSomething: this.value is ${this.value}`);
},
doSomethingElse() {
console.log(`original doSomethingElse: this.value is ${this.value}`);
}
};
const insertion = {};
for (const key of Object.keys(original)) {
const func = original[key];
if (typeof func === "function") {
insertion[key] = function(...args) {
console.log("insertion " + key + " [before]");
const thisArg = this === insertion ? original : this;
const result = Reflect.apply(func, thisArg, args);
console.log("insertion " + key + " [after]");
return result;
};
}
}
// Here's the code calling the object's methods;
// note we've gotten in the way by giving the code
// `insertion` rather than `original`:
insertion.doSomething();
insertion.doSomethingElse();
You can also do that with a Proxy, though it's more complicated and the complication doesn't buy you anything in this case.
Note that this will only catch calls make through insertion, for obvious reasons. That means if doSomething calls doSomethingElse, in the above you'd intercept the call to doSomething but not the call to doSomethingElse.
Modifying the object
You can do it by replacing the object's methods, like this:
const original = {
value: 42,
doSomething() {
console.log(`original doSomething: this.value is ${this.value}`);
},
doSomethingElse() {
console.log(`original doSomethingElse: this.value is ${this.value}`);
}
};
for (const key of Object.keys(original)) {
const func = original[key];
if (typeof func === "function") {
original[key] = function(...args) {
console.log(key + " [before]");
const result = Reflect.apply(func, this, args);
console.log(key + " [after]");
return result;
};
}
}
// Here's the code calling the object's methods
original.doSomething();
original.doSomethingElse();
Since this modifies the object itself, you'd see all the calls.
Is there a way to make any function output a console.log statement when it's called by registering a global hook somewhere (that is, without modifying the actual function itself) or via some other means?
Here's a way to augment all functions in the global namespace with the function of your choice:
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);
});
One down side is that no functions created after calling augment will have the additional behavior.
As to me, this looks like the most elegant solution:
(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 Method to log Function calls
There is a new way using Proxy to achieve this functionality in JS.
assume that we want to have a console.log whenever a function of a specific class is called:
class TestClass {
a() {
this.aa = 1;
}
b() {
this.bb = 1;
}
}
const foo = new TestClass()
foo.a() // nothing get logged
we can replace our class instantiation with a Proxy that overrides each property of this class. so:
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"
check that this actually works in Codepen
Remember that using Proxy gives you a lot more functionality than to just logging console names.
Also this method works in Node.js too.
If you want more targeted logging, the following code will log function calls for a particular object. You can even modify Object prototypes so that all new instances get logging too. I used Object.getOwnPropertyNames instead of for...in, so it works with ECMAScript 6 classes, which don't have enumerable methods.
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);
Here's some Javascript which replaces adds console.log to every function in Javascript; Play with it on Regex101:
$re = "/function (.+)\\(.*\\)\\s*\\{/m";
$str = "function example(){}";
$subst = "$& console.log(\"$1()\");";
$result = preg_replace($re, $subst, $str);
It's a 'quick and dirty hack' but I find it useful for debugging. If you have a lot of functions, beware because this will add a lot of code. Also, the RegEx is simple and might not work for more complex function names/declaration.
You can actually attach your own function to console.log for everything that loads.
console.log = function(msg) {
// Add whatever you want here
alert(msg);
}
related to How can I call a javascript constructor using call or apply?
but not the same, I'm trying to apply the SO answers to John Resig's forcing a constructor when not called properly.
function User(first, last){
if ( !(this instanceof User) )
// the line I want to replace, or remove the redundancy from:
return new User(first, last);
this.name = first + " " + last;
}
var name = "Resig";
var user = User("John", name);
assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );
The target line of code means every time the constructor changes, someone has to remember to change the internal self-call arguments. I've already had a project trip over this issue 3 times in the last 3 days.
All the examples in the linked question talk about passing the constructor, but what is the constructor in this case? It's not even finished being defined yet.
but so far all attempts do not pass the test, or throw a stackoverflow.
How do I make sure the constructor being called results in something that responds properly to instanceof User even when called without the new keyword, while eliminating the repetition of argument parameters?
Some options for you, all using Object.create:
Option 1:
function User(first, last){
var rv;
if ( !(this instanceof User) ) {
// They called us without `new`: Create an object backed by `User.prototype`:
rv = Object.create(User.prototype);
// Now, call this function applying the arguments
User.apply(rv, arguments);
// Return the object
return rv;
}
// Normal constructor stuff
this.name = first + " " + last;
}
Of course, all of that logic doesn't have to be repeated for every constructor function you create, you can use a helper function:
function constructWith(obj, ctor, args) {
if (obj instanceof ctor) {
return null;
}
obj = Object.create(ctor.prototype);
ctor.apply(obj, args);
return obj;
}
then
function User(first, last){
var rv;
if ((rv = constructWith(this, User, arguments)) != null) {
return rv;
}
// Normal constructor stuff
this.name = first + " " + last;
}
Option 2: Don't use this much:
function User(first, last){
var rv;
if (this instanceof User) {
// They (probably) used `new`, all is good, use `this`
rv = this;
} else {
// They didn't use `new`, create an object backed by `User.prototype`
rv = Object.create(User.prototype);
}
// ...use `rv`, not `this`, from here on
rv.name = first + " " + last;
// This is important for the case where they didn't use `new`, and harmless
// in the case where they did.
return rv;
}
As you can see, this is a lot simpler, but if you really like your syntax highlighting (seriously, I have a client to whom it really matters that this jumps out), etc...
And of course, you can wrap that up in a helper:
function useOrConstruct(obj, ctor) {
return obj instanceof ctor ? obj : Object.create(ctor.prototype);
}
Then
function User(first, last){
var rv = useOrConstruct(this, User);
// ...use `rv`, not `this`, from here on
rv.name = first + " " + last;
// This is important for the case where they didn't use `new`, and harmless
// in the case where they did.
return rv;
}
Option 3: constructOMatic
Of course, if we're going to define helpers, maybe we should go whole-hog:
function User() {
return constructOMatic(this, User, arguments, function(first, last) {
this.name = first + " " + last;
});
}
...where constructOMatic is:
function constructOMatic(obj, ctor, args, callback) {
var rv;
if (!(obj instanceof ctor)) {
obj = Object.create(ctor.prototype);
}
rv = callback.apply(obj, args);
return rv !== null && typeof rv === "object" ? rv : obj;
}
Now, you can use this to your heart's content within the callback. That fiddling with rv vs. obj in the return at the end is to emulate the behavior of new (the result of a new expression is the object created by the new operator unless the constructor function returns a non-null object reference, in which case that takes precedence).
Object.create is an ES5 feature found on all modern browsers, but the single-argument version of it used above can be shimmed for out-of-date browsers:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw "The two-argument version of Object.create cannot be shimmed.";
}
function ctor() { }
ctor.prototype = proto;
return new ctor; // Yes, you really don't need () (but put them on if you prefer)
};
}
Copy and paste are very easy and the code is clean. You need not to change it.
If you accept eval, you can do it like this:
function User(first, last){
if ( !(this instanceof arguments.callee) ) {
var name = arguments.callee.name;
var param = [].map.call(arguments,function(e,i){return 'arguments['+i+']';});
return eval('new '+name+'('+ param +')');
}
this.name = first + " " + last;
}
//test
var user1 = User("John", "Resig");
var user2 = new User("John", "Resig");
Without eval, you can do it like this:
function instantiate(C,a){
switch(a.length){
case 0: return new C();
case 1: return new C(a[0]);
case 2: return new C(a[0],a[1]);
case 3: return new C(a[0],a[1],a[2]);
case 4: return new C(a[0],a[1],a[2],a[3]);
default : throw("too many arguments");
}
}
function User(first, last){
if ( !(this instanceof arguments.callee) ) {
return instantiate(arguments.callee, arguments);
}
this.name = first + " " + last;
}
//test
var user1 = User("John", "Resig");
var user2 = new User("John", "Resig");
In ECMAScript 6, you can use the spread operator to apply a constructor with the new keyword to an array of arguments:
"use strict";
function User(first, last){
if ( !(this instanceof User) ) {
return new User(...arguments);
}
this.name = first + " " + last;
}
Say i have a constructor, and some instance methods, like
function MyClass(name) {
this.name = name || '';
}
MyClass.prototype = {
constructor: MyClass,
isEmptyName: function() {
return this.name === '';
}
}
Now i can write
var myClass = new MyClass('Ben');
myClass.isEmptyName();
which would return false. Now if i make another method what would also return a Boolean
MyClass.prototype = {
constructor: MyClass,
isEmptyName: function() {
return this.name === '';
}
longerThan: function(len) {
return this.name.length > len;
}
}
i would like to chain these methods like this (somehow, thats my question :) )
myClass.isEmptyName().and.longerThan(2);
Just omit now the '.and.' part. I want the upper statement to finally return a value
false && true -> false
Or a more realistic sample:
myClass.notEmptyName().and.longerThan(4);
To summarize my problem i would say, i want my methods return a boolean value if they are called 'directly' myClass.notEmptyName() should return true, but work like i wrote in the samples, otherwise.
Other libraries do this somehow, but i can't guess how, npm's should is a good example:
user.should.have.property('pets').with.lengthOf(4);
user.pets.should.be.instanceof(Array).and.have.lengthOf(4);
Thanks
That's not possible. A method can't return either a boolean or be chainable depending on how it's used later on, because it doesn't know how it will be used later on.
You can chain methods that validate the object in different ways, but you need to get the result at the end if you want to use it in an expression, either by reading a property or calling a method:
function MyClass(name) {
this.name = name;
this.and = this;
}
MyClass.prototype = {
value: true,
isNotEmpty: function() {
this.value = this.value && this.name.length > 0; return this;
},
isLongerThan: function(len) {
this.value = this.value && this.name.length > len; return this;
},
evaluate: function() {
return this.value;
}
};
console.log(new MyClass('Adam').isLongerThan(2).evaluate());
console.log(new MyClass('Bob').isNotEmpty().and.isLongerThan(3).evaluate());
Demo: http://jsfiddle.net/Guffa/62e8dLwL/
Edit:
To allow evaluation more than once, you would reset the value in the evaluate method:
evaluate: function() {
var v = this.value;
this.value = true;
return v;
}
Sure, you can do that. We will define a new intermediate status object, called ChainResult, which remembers the underlying object, the current value, and a pending operation (a function to use to combine the next test). We give this object a valueOf method, so that when JS tries to evaluate it as a primitive, it "looks" like it has a value. To make this work, it turns out that ChainResult actually needs to be a function, and so we hang the necessary properties off the function.
function ChainResult(obj, val) {
function x() { }
x.obj = obj;
x.val = val;
x.op = null;
// the `valueOf` function spits out the current value when the object is evaluated
x.valueOf = function() { return this.val; };
// the test functions combine the results with the current value
// using the current operation as set by a preceding `and` or `or`
x.isEmptyName = function() {
x.val = x.op(x.val, x.obj._isEmptyName());
return this;
};
x.isLongerThan = function(len) {
x.val = x.op(x.val, x.obj._isLongerThan(len));
return this;
};
// we implement `and` and `or` via getters which set the operation
// on the ChainResult object, and return `this` so we can keep chaining
Object.defineProperties(x, {
and: {
get: function() { x.op = function(a,b) { return a && b; }; return x; }
},
or: {
get: function() { x.op = function(a,b) { return a || b; }; return x; }
}
});
return x;
}
The MyClass definition needs a bit of tweaking:
function MyClass(name) {
this.name = name || '';
}
MyClass.prototype = {
constructor: MyClass,
// we implement the testers as pseudo-private functions
_isEmptyName: function() { return this.name === ''; },
_isLongerThan: function(len) { return this.name.length > len; },
// when the public tester functions are invoked directly on the object
// (when they are the first link in the chain), we construct and return a
// ChainResult object with the initial value set correctly
isEmptyName: function() { return ChainResult(this, this._isEmptyName()); },
isLongerThan: function(len) { return ChainResult(this, this._isLongerThan(len)) }
};
Flow:
new MyClass('Bob') // create MyClass object
.isEmptyName() // create ChainResult object with value `false`
.or // remember `or` operation in ChainResult object
.isLongerThan(2) // update value of ChainResult object
; // JS tries to convert to scalar, calls valueOf
// true
This needs to be bullet-proofed and tightened up, but you get the idea.
i want my methods return a boolean value if they are called 'directly' myClass.notEmptyName() should return true
Your methods are always called directly on the instance, and would always need to return a primitive boolean value. By that, the context (myClass) is lost and you cannot have an and method (or property getter) on the result.
I would recommend you to have a look at functional programming, partial application and currying, which helps a lot with fluent interfaces like this. Given
function and(fn1, fn2) {
return function(val) {
return fn1(val) && fn2(val);
};
}
function or(fn1, fn2) {
return function(val) {
return fn1(val) || fn2(val);
};
}
function hasEmptyName: function(val) {
return val.name === '';
}
function hasNameLongerThan: function(len) {
return function(val) {
return val.name.length > len;
};
}
you could write
and(hasEmptyName, hasNameLongerThan(2))(myClass);
Making these functions methods of anything is complicated however. Maybe something like this:
function method(name) {
var args = Array.prototype.slice.call(arguments, 1);
return function(instance) {
return instance[name].apply(instance, args);
};
}
Function.prototype.and = function (fn2) {
var fn1 = this;
return function(val) {
return fn1(val) && fn2(val);
};
}
Function.prototype.or = function (fn2) {
var fn1 = this;
return function(val) {
return fn1(val) || fn2(val);
};
}
Object.defineProperty(Object.prototype, "test", function(pred) {
return pred(this);
});
Now you could write
myClass.test(method("notEmptyName").and(method("longerThan", 4)));
This is an answer (better call it to outcome) for my own question:
Finally i came out with another solution based on the responses in this thread (Thanks guys!) because the original problem can not be solved, since the javascript runtime can't find out wether to return a value, or return itself (the object) when chained. Explanation is messy, sorry :(
Check out my lib, where i have methods like:
check(value).isString() etc..
Originally i wanted to chain these like check(value).isString().and.not.empty() but this, in this way, can not be done. (Challenge me)
Finally i created tokens for chaining, so instead of
check(value).isString().and.not.empty()
I can write
check(value).isstring.and.not.empty.test()
Not nice, but still something.
check.js
For review, visit checkjs on my github repo.
Note: README is outdated.
If you use promises, you could write functions that return values and can be chained. Mind you, these are all asynchronous.
Find this JS plugin: Kris Owal's Q, or if you like to use a JS library, they usually contain deferred objects and promises.
How could I get a callback whenever new properties are set on a Javascript Object..?
I.e. I don't know which properties are going to be set, but want a callback for any properties that are set.
What I want is
var obj = {};
obj.a = "something"; // triggers callback function
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
Is this possible?
You can use the new defineProperty:
function onChange(propertyName, newValue){
...
}
var o = {};
Object.defineProperty(o, "propertyName", {
get: function() {return pValue; },
set: function(newValue) { onChange("propertyName",newValue); pValue = newValue;}});
But depends on the browser version you need to support.
Edit: Added snippet on Jsfiddle, works in IE10. http://jsfiddle.net/r2wbR/
The best thing to do it is a setter function, you don't need a getter,
var obj = {};
setKey(obj, 'a', "something"); // triggers callback function
function setKey(obj, key, val){
obj[key] = val;
callback(obj, key, val);
}
function callback(obj, key, val) {
console.log(key + " was set to " + val + " on ", obj);
}
Do it as generic as you can, don't do a different function for all keys
Try it here
The best idea is to have setter and getter methods. But according to your previous implementation one could still alter your object properties without using setter and getter. Therfore you should make you senstive variables privat. Here is a brief example:
var Person = (function() {
function Person() {};
var _name;
Person.prototype.setName = function(name) {
_name = name;
};
Person.prototype.getName = function() {
return _name;
};
return Person;
})();
var john = new Person();
john.getName();
// => undefined
john.setName("John");
john.getName();
// => "John"
john._name;
// => undefined
john._name = "NOT John";
john.getName();
// => "John"
Unfortunately, JavaScript won't let you know when a property is changed. Many times I've wished it would, but since it wouldn't I've had to find a workaround. Instead of setting the property directly, set it via a setter method which triggers the callback function (and possible use a getter method to access the property too) like this:
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
var obj = {};
obj.setA=function(value){
obj.a=value;
callback(obj,'a',value);// triggers callback function
}
obj.getA=function(){
return obj.a;
}
obj.setA("something");
jsFiddle: http://jsfiddle.net/jdwire/v8sJt/
EDIT: Another option if you want to completely prevent changing the property without a callback:
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
var obj={};
(function(){
var a=null;
obj.setA=function(value){
a=value;
callback(obj,'a',value);// triggers callback function
}
obj.getA=function(){
return a;
}
})()
console.log("a is "+obj.getA());// a is null
obj.setA("something"); // Set a to something
console.log("a is now "+obj.getA()); // a is now something
obj.a="something else"; // Set obj.a to something else to show how a is only accessible through setA
console.log("a is still "+obj.getA()); // a is still something
jsFiddle: http://jsfiddle.net/jdwire/wwaL2/