Extend $.mobile.changePage to accept more options? - javascript

I would like to extend $.mobile.changePage to accept more options such as adding a callback function for when the page finishes loading as well as more options for the AJAX call like contentType. Is there a way to do this without changing the source code? If not, I am willing to change the source code for educational purposes, but could not find it in the jQuery Mobile GitHub: https://github.com/jquery/jquery-mobile . Thanks for any helps or guidance.

One of the more exciting parts of JavaScript is that ability to redefine any function using a technique which is commonly referred to as Monkey Patching. (as an aside ES5 provides a new freeze method which allows developers to prevent such modifications.)
Here's an example of a JavaScript MonkeyPatch which allows us to modify the behaviour of a function without editing it's source:
// A namespace object.
var Example = {};
// Sums two values.
Example.sum = function (a, b) {
return a + b;
}
// Usage:
var result = Example.sum(1, 2);
Say we wanted to add logging to the sum method, we could just add a console.log line to the function, but we can also monkey patch it:
// Store a reference to the current 'Example.sum' function.
var originalSum = Example.sum;
// Now redeclare Example.sum...
Example.sum = function (a, b) {
// Call the originalSum function first...
var result = originalSum(a, b);
// Now add some logging...
console.log("Example.sum(" + a + ", " + b + ") yields " + result);
return result;
};
Now when Example.sum is called, not only will we get the result as before, but a console message will also be written. With this in mind, you can monkey patch the $.mobile.changePage method in the same way:
var originalChangePage = $.mobile.changePage;
// Redefine `changePage` so it accepts a 'complete' function in the options
// object which will be invoked when the page change is complete.
$.mobile.changePage = function (to, options) {
if (typeof options.complete === "function") {
$(body).one("pagechange", function (event) {
options.complete(event);
});
}
originalChangePage(to, options);
};

Related

Javascript log attribute access and function calls [duplicate]

Can I override the behavior of the Function object so that I can inject behavior prior t every function call, and then carry on as normal? Specifically, (though the general idea is intriguing in itself) can I log to the console every function call without having to insert console.log statements everywhere? And then the normal behavior goes on?
I do recognize that this will likely have significant performance problems; I have no intention of having this run typically, even in my development environment. But if it works it seems an elegant solution to get a 1000 meter view on the running code. And I suspect that the answer will show me something deeper about javascript.
The obvious answer is something like the following:
var origCall = Function.prototype.call;
Function.prototype.call = function (thisArg) {
console.log("calling a function");
var args = Array.prototype.slice.call(arguments, 1);
origCall.apply(thisArg, args);
};
But this actually immediately enters an infinite loop, because the very act of calling console.log executes a function call, which calls console.log, which executes a function call, which calls console.log, which...
Point being, I'm not sure this is possible.
Intercepting function calls
Many here have tried to override .call. Some have failed, some have succeeded.
I'm responding to this old question, as it has been brought up at my workplace, with this post being used as reference.
There are only two function-call related functions available for us to modify: .call and .apply. I will demonstrate a successful override of both.
TL;DR: What OP is asking is not possible. Some of the success-reports in the answers are due to the console calling .call internally right before evaluation, not because of the call we want to intercept.
Overriding Function.prototype.call
This appears to be the first idea people come up with. Some have been more successful than others, but here is an implementation that works:
// Store the original
var origCall = Function.prototype.call;
Function.prototype.call = function () {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, lets do it by ourselves.
console.log("Calling",
Function.prototype.toString.apply(this, []),
"with:",
Array.prototype.slice.apply(arguments, [1]).toString()
);
// A trace, for fun
console.trace.apply(console, []);
// The call. Apply is the only way we can pass all arguments, so don't touch that!
origCall.apply(this, arguments);
};
This successfully intercepts Function.prototype.call
Lets take it for a spin, shall we?
// Some tests
console.log("1"); // Does not show up
console.log.apply(console,["2"]); // Does not show up
console.log.call(console, "3"); // BINGO!
It is important that this is not run from a console. The various browsers have all sorts of console tools that call .call themselves a lot, including once for every input, which might confuse a user in the moment. Another mistake is to just console.log arguments, which goes through the console api for stringification, which in turn cause an infinite loop.
Overriding Function.prototype.apply as well
Well, what about apply then? They're the only magic calling functions we have, so lets try that as well. Here goes a version that catches both:
// Store apply and call
var origApply = Function.prototype.apply;
var origCall = Function.prototype.call;
// We need to be able to apply the original functions, so we need
// to restore the apply locally on both, including the apply itself.
origApply.apply = origApply;
origCall.apply = origApply;
// Some utility functions we want to work
Function.prototype.toString.apply = origApply;
Array.prototype.slice.apply = origApply;
console.trace.apply = origApply;
function logCall(t, a) {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, do it ourselves.
console.log("Calling",
Function.prototype.toString.apply(t, []),
"with:",
Array.prototype.slice.apply(a, [1]).toString()
);
console.trace.apply(console, []);
}
Function.prototype.call = function () {
logCall(this, arguments);
origCall.apply(this, arguments);
};
Function.prototype.apply = function () {
logCall(this, arguments);
origApply.apply(this, arguments);
}
... And lets try it out!
// Some tests
console.log("1"); // Passes by unseen
console.log.apply(console,["2"]); // Caught
console.log.call(console, "3"); // Caught
As you can see, the calling parenthesis go unnoticed.
Conclusion
Fortunately, calling parenthesis cannot be intercepted from JavaScript. But even if .call would intercept the parenthesis operator on function objects, how would we call the original without causing an infinite loop?
The only thing overriding .call/.apply does is to intercept explicit calls to those prototype functions. If the console is used with that hack in place, there will be lots and lots of spam. One must furthermore be very careful if it is used, as using the console API can quickly cause an infinite loop (console.log will use .call internally if one gives it an non-string).
I am getting SOME results and no page crashes with the following :
(function () {
var
origCall = Function.prototype.call,
log = document.getElementById ('call_log');
// Override call only if call_log element is present
log && (Function.prototype.call = function (self) {
var r = (typeof self === 'string' ? '"' + self + '"' : self) + '.' + this + ' (';
for (var i = 1; i < arguments.length; i++) r += (i > 1 ? ', ' : '') + arguments[i];
log.innerHTML += r + ')<br/>';
this.apply (self, Array.prototype.slice.apply (arguments, [1]));
});
}) ();
Only tested in Chrome version 9.xxx.
It is certainly not logging all function calls, but it is logging some!
I suspect only actual calls to 'call' intself are being processed
Only a quick test, but it seems to work for me.
It may not be useful this way, but I'm basically restoring the prototype whilst in my replacement's body and then "unrestoring" it before exiting.
This example simply logs all function calls - though there may be some fatal flaw I've yet to detect; doing this over a coffee break
implementation
callLog = [];
/* set up an override for the Function call prototype
* #param func the new function wrapper
*/
function registerOverride(func) {
oldCall = Function.prototype.call;
Function.prototype.call = func;
}
/* restore you to your regular programming
*/
function removeOverride() {
Function.prototype.call = oldCall;
}
/* a simple example override
* nb: if you use this from the node.js REPL you'll get a lot of buffer spam
* as every keypress is processed through a function
* Any useful logging would ideally compact these calls
*/
function myCall() {
// first restore the normal call functionality
Function.prototype.call = oldCall;
// gather the data we wish to log
var entry = {this:this, name:this.name, args:{}};
for (var key in arguments) {
if (arguments.hasOwnProperty(key)) {
entry.args[key] = arguments[key];
}
}
callLog.push(entry);
// call the original (I may be doing this part naughtily, not a js guru)
this(arguments);
// put our override back in power
Function.prototype.call = myCall;
}
usage
I've had some issues including calls to this in one big paste, so here's what I was typing into the REPL in order to test the above functions:
/* example usage
* (only tested through the node.js REPL)
*/
registerOverride(myCall);
console.log("hello, world!");
removeOverride(myCall);
console.log(callLog);
You can override Function.prototype.call, just make sure to only apply functions within your override.
window.callLog = [];
Function.prototype.call = function() {
Array.prototype.push.apply(window.callLog, [[this, arguments]]);
return this.apply(arguments[0], Array.prototype.slice.apply(arguments,[1]));
};
I found it easiest to instrument the file, using an automatic process. I built this little tool to make it easier for myself. Perhaps somebody else will find it useful. It's basically awk, but easier for a Javascript programmer to use.
// This tool reads a file and builds a buffer of say ten lines.
// When a line falls off the end of the buffer, it gets written to the output file.
// When a line is read from the input file, it gets written to the first line of the buffer.
// After each occurrence of a line being read from the input file and/or written to the output
// file, a routine is given control. The routine has the option of operating on the buffer.
// It can insert a line before or after a line that is there, based on the lines surrounding.
//
// The immediate case is that if I have a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// I want to change that to a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`compareNums: function (a, c, d) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// We are trying to figure out how a set of functions work, and I want each function to report
// its name when we enter it.
//
// To save time, options and the function that is called on each cycle appear at the beginning
// of this file. Ideally, they would be --something options on the command line.
const readline = require('readline');
//------------------------------------------------------------------------------------------------
// Here are the things that would properly be options on the command line. Put here for
// speed of building the tool.
const frameSize = 10;
const shouldReportFrame = false;
function reportFrame() {
for (i = frame.length - 1; i >= 0; i--) {
console.error(`${i}. ${frame[i]}`); // Using the error stream because the stdout stream may have been coopted.
}
}
function processFrame() {
// console.log(`******** ${frame[0]}`);
// if (frame[0].search('console.log(\`arguments.callee = \$\{arguments.callee.toString().substr(0,100)\}\`);') !== -1) {
// if (frame[0].search('arguments.callee') !== -1) {
// if (frame[0].search(/console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/) !== -1) {
var matchArray = frame[0].match(/([ \t]*)console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/);
if (matchArray) {
// console.log('******** Matched');
frame.splice(1, 0, `${matchArray[1]}console.log('${frame[1]}');`);
}
}
//------------------------------------------------------------------------------------------------
var i;
var frame = [];
const rl = readline.createInterface({
input: process.stdin
});
rl.on('line', line => {
if (frame.length > frameSize - 1) {
for (i = frame.length - 1; i > frameSize - 2; i--) {
process.stdout.write(`${frame[i]}\n`);
}
}
frame.splice(frameSize - 1, frame.length - frameSize + 1);
frame.splice(0, 0, line);
if (shouldReportFrame) reportFrame();
processFrame();
// process.stdout.write(`${line}\n`); // readline gives us the line with the newline stripped off
});
rl.on('close', () => {
for (i = frame.length - 1; i > -1; i--) {
process.stdout.write(`${frame[i]}\n`);
}
});
// Notes
//
// We are not going to control the writing to the output stream. In particular, we are not
// going to listen for drain events. Nodejs' buffering may get overwhelmed.
//

need help understanding closures usage in this code

Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...

Force missing parameters in JavaScript

When you call a function in JavaScript and you miss to pass some parameter, nothing happens.
This makes the code harder to debug, so I would like to change that behavior.
I've seen
How best to determine if an argument is not sent to the JavaScript function
but I want a solution with a constant number of typed lines of code; not typing extra code for each function.
I've thought about automatically prefixing the code of all functions with that code, by modifying the constructor of the ("first-class") Function object.
Inspired by
Changing constructor in JavaScript
I've first tested whether I can change the constructor of the Function object, like this:
function Function2 () {
this.color = "white";
}
Function.prototype = new Function2();
f = new Function();
alert(f.color);
But it alerts "undefined" instead of "white", so it is not working, so I've don't further explored this technique.
Do you know any solution for this problem at any level? Hacking the guts of JavaScript would be OK but any other practical tip on how to find missing arguments would be OK as well.
If a function of yours requires certain arguments to be passed, you should check for those arguments specifically as part of the validation of the function.
Extending the Function object is not the best idea because many libraries rely on the behavior of defaulting arguments that are not passed (such as jQuery not passing anything to it's scoped undefined variable).
Two approaches I tend to use:
1) an argument is required for the function to work
var foo = function (requiredParam) {
if (typeof requiredParam === 'undefined') {
throw new Error('You must pass requiredParam to function Foo!');
}
// solve world hunger here
};
2) an argument not passed but can be defaulted to something (uses jQuery)
var foo = function (argumentObject) {
argumentObject = $.extend({
someArgument1: 'defaultValue1',
someArgument2: 'defaultValue2'
}, argumentObject || {});
// save the world from alien invaders here
};
As others have said, there are many reasons not to do this, but I know of a couple of ways, so I'll tell you how! For science!
This is the first, stolen from Gaby, give him an upvote! Here's a rough overview of how it works:
//example function
function thing(a, b, c) {
}
var functionPool = {} // create a variable to hold the original versions of the functions
for( var func in window ) // scan all items in window scope
{
if (typeof(window[func]) === 'function') // if item is a function
{
functionPool[func] = window[func]; // store the original to our global pool
(function(){ // create an closure to maintain function name
var functionName = func;
window[functionName] = function(){ // overwrite the function with our own version
var args = [].splice.call(arguments,0); // convert arguments to array
// do the logging before callling the method
if(functionPool[functionName].length > args.length)
throw "Not enough arguments for function " + functionName + " expected " + functionPool[functionName].length + " got " + args.length;
// call the original method but in the window scope, and return the results
return functionPool[functionName].apply(window, args );
// additional logging could take place here if we stored the return value ..
}
})();
}
}
thing(1,2 ,3); //fine
thing(1,2); //throws error
The second way:
Now there is another way to do this that I can't remember the details exactly, basically you overrride Function.prototype.call. But as it says in this question, this involves an infinite loop. So you need an untainted Function object to call, this is done by a trick of turning the variables into a string and then using eval to call the function in an untainted context! There's a really great snippet out the showing you how from the early days of the web, but alas I can't find it at the moment. There's a hack that's required to pass the variables properly and I think you may actually lose context, so it's pretty fragile.
Still, as stated, don't try and force javascript to do something against its nature, either trust your fellow programmers or supply defaults, as per all the other answers.
You can imitate something like Python’s decorators. This does require extra typing per function, though not extra lines.
function force(inner) {
return function() {
if (arguments.length === inner.length) {
return inner.apply(this, arguments);
} else {
throw "expected " + inner.length +
" arguments, got " + arguments.length;
}
}
}
var myFunc = force(function(foo, bar, baz) {
// ...
});
In general this sounds like a bad idea, because you’re basically messing with the language. Do you really forget to pass arguments that often?
You could use the decorator pattern. The following decorator allows you to specify minimum and maximum number of arguments that need to be passed and an optional error handler.
/* Wrap the function *f*, so that *error_callback* is called when the number
of passed arguments is not with range *nmin* to *nmax*. *error_callback*
may be ommited to make the wrapper just throw an error message.
The wrapped function is returned. */
function require_arguments(f, nmin, nmax, error_callback) {
if (!error_callback) {
error_callback = function(n, nmin, nmax) {
throw 'Expected arguments from ' + nmin + ' to ' + nmax + ' (' +
n + ' passed).';
}
}
function wrapper() {
var n_args = arguments.length;
console.log(n_args, nmin, nmax);
console.log((nmin <= 0) && (0 <= nmax));
if ((nmin <= n_args) && (n_args <= nmax)) {
return f.apply(this, arguments);
}
return error_callback(n_args, nmin, nmax);
}
for (e in f) {
wrapper[e] = f[e];
}
return wrapper;
}
var foo = require_arguments(function(a, b, c) {
/* .. */
}, 1, 3);
foo(1);
foo(1, 2);
foo(1, 2, 3);
foo(1, 2, 3, 4); // uncaught exception: Expected arguments from 1 to 3 (4 passed).
foo(); // uncaught exception: Expected arguments from 1 to 3 (0 passed).

Can I override the Javascript Function object to log all function calls?

Can I override the behavior of the Function object so that I can inject behavior prior t every function call, and then carry on as normal? Specifically, (though the general idea is intriguing in itself) can I log to the console every function call without having to insert console.log statements everywhere? And then the normal behavior goes on?
I do recognize that this will likely have significant performance problems; I have no intention of having this run typically, even in my development environment. But if it works it seems an elegant solution to get a 1000 meter view on the running code. And I suspect that the answer will show me something deeper about javascript.
The obvious answer is something like the following:
var origCall = Function.prototype.call;
Function.prototype.call = function (thisArg) {
console.log("calling a function");
var args = Array.prototype.slice.call(arguments, 1);
origCall.apply(thisArg, args);
};
But this actually immediately enters an infinite loop, because the very act of calling console.log executes a function call, which calls console.log, which executes a function call, which calls console.log, which...
Point being, I'm not sure this is possible.
Intercepting function calls
Many here have tried to override .call. Some have failed, some have succeeded.
I'm responding to this old question, as it has been brought up at my workplace, with this post being used as reference.
There are only two function-call related functions available for us to modify: .call and .apply. I will demonstrate a successful override of both.
TL;DR: What OP is asking is not possible. Some of the success-reports in the answers are due to the console calling .call internally right before evaluation, not because of the call we want to intercept.
Overriding Function.prototype.call
This appears to be the first idea people come up with. Some have been more successful than others, but here is an implementation that works:
// Store the original
var origCall = Function.prototype.call;
Function.prototype.call = function () {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, lets do it by ourselves.
console.log("Calling",
Function.prototype.toString.apply(this, []),
"with:",
Array.prototype.slice.apply(arguments, [1]).toString()
);
// A trace, for fun
console.trace.apply(console, []);
// The call. Apply is the only way we can pass all arguments, so don't touch that!
origCall.apply(this, arguments);
};
This successfully intercepts Function.prototype.call
Lets take it for a spin, shall we?
// Some tests
console.log("1"); // Does not show up
console.log.apply(console,["2"]); // Does not show up
console.log.call(console, "3"); // BINGO!
It is important that this is not run from a console. The various browsers have all sorts of console tools that call .call themselves a lot, including once for every input, which might confuse a user in the moment. Another mistake is to just console.log arguments, which goes through the console api for stringification, which in turn cause an infinite loop.
Overriding Function.prototype.apply as well
Well, what about apply then? They're the only magic calling functions we have, so lets try that as well. Here goes a version that catches both:
// Store apply and call
var origApply = Function.prototype.apply;
var origCall = Function.prototype.call;
// We need to be able to apply the original functions, so we need
// to restore the apply locally on both, including the apply itself.
origApply.apply = origApply;
origCall.apply = origApply;
// Some utility functions we want to work
Function.prototype.toString.apply = origApply;
Array.prototype.slice.apply = origApply;
console.trace.apply = origApply;
function logCall(t, a) {
// If console.log is allowed to stringify by itself, it will
// call .call 9 gajillion times. Therefore, do it ourselves.
console.log("Calling",
Function.prototype.toString.apply(t, []),
"with:",
Array.prototype.slice.apply(a, [1]).toString()
);
console.trace.apply(console, []);
}
Function.prototype.call = function () {
logCall(this, arguments);
origCall.apply(this, arguments);
};
Function.prototype.apply = function () {
logCall(this, arguments);
origApply.apply(this, arguments);
}
... And lets try it out!
// Some tests
console.log("1"); // Passes by unseen
console.log.apply(console,["2"]); // Caught
console.log.call(console, "3"); // Caught
As you can see, the calling parenthesis go unnoticed.
Conclusion
Fortunately, calling parenthesis cannot be intercepted from JavaScript. But even if .call would intercept the parenthesis operator on function objects, how would we call the original without causing an infinite loop?
The only thing overriding .call/.apply does is to intercept explicit calls to those prototype functions. If the console is used with that hack in place, there will be lots and lots of spam. One must furthermore be very careful if it is used, as using the console API can quickly cause an infinite loop (console.log will use .call internally if one gives it an non-string).
I am getting SOME results and no page crashes with the following :
(function () {
var
origCall = Function.prototype.call,
log = document.getElementById ('call_log');
// Override call only if call_log element is present
log && (Function.prototype.call = function (self) {
var r = (typeof self === 'string' ? '"' + self + '"' : self) + '.' + this + ' (';
for (var i = 1; i < arguments.length; i++) r += (i > 1 ? ', ' : '') + arguments[i];
log.innerHTML += r + ')<br/>';
this.apply (self, Array.prototype.slice.apply (arguments, [1]));
});
}) ();
Only tested in Chrome version 9.xxx.
It is certainly not logging all function calls, but it is logging some!
I suspect only actual calls to 'call' intself are being processed
Only a quick test, but it seems to work for me.
It may not be useful this way, but I'm basically restoring the prototype whilst in my replacement's body and then "unrestoring" it before exiting.
This example simply logs all function calls - though there may be some fatal flaw I've yet to detect; doing this over a coffee break
implementation
callLog = [];
/* set up an override for the Function call prototype
* #param func the new function wrapper
*/
function registerOverride(func) {
oldCall = Function.prototype.call;
Function.prototype.call = func;
}
/* restore you to your regular programming
*/
function removeOverride() {
Function.prototype.call = oldCall;
}
/* a simple example override
* nb: if you use this from the node.js REPL you'll get a lot of buffer spam
* as every keypress is processed through a function
* Any useful logging would ideally compact these calls
*/
function myCall() {
// first restore the normal call functionality
Function.prototype.call = oldCall;
// gather the data we wish to log
var entry = {this:this, name:this.name, args:{}};
for (var key in arguments) {
if (arguments.hasOwnProperty(key)) {
entry.args[key] = arguments[key];
}
}
callLog.push(entry);
// call the original (I may be doing this part naughtily, not a js guru)
this(arguments);
// put our override back in power
Function.prototype.call = myCall;
}
usage
I've had some issues including calls to this in one big paste, so here's what I was typing into the REPL in order to test the above functions:
/* example usage
* (only tested through the node.js REPL)
*/
registerOverride(myCall);
console.log("hello, world!");
removeOverride(myCall);
console.log(callLog);
You can override Function.prototype.call, just make sure to only apply functions within your override.
window.callLog = [];
Function.prototype.call = function() {
Array.prototype.push.apply(window.callLog, [[this, arguments]]);
return this.apply(arguments[0], Array.prototype.slice.apply(arguments,[1]));
};
I found it easiest to instrument the file, using an automatic process. I built this little tool to make it easier for myself. Perhaps somebody else will find it useful. It's basically awk, but easier for a Javascript programmer to use.
// This tool reads a file and builds a buffer of say ten lines.
// When a line falls off the end of the buffer, it gets written to the output file.
// When a line is read from the input file, it gets written to the first line of the buffer.
// After each occurrence of a line being read from the input file and/or written to the output
// file, a routine is given control. The routine has the option of operating on the buffer.
// It can insert a line before or after a line that is there, based on the lines surrounding.
//
// The immediate case is that if I have a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// I want to change that to a set of lines like this:
//
// getNum: function (a, c) {
// console.log(`getNum: function (a, c) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
// console.log(`arguments.length = ${arguments.length}`);
// for (var i = 0; i < arguments.length; i++) { console.log(`arguments[${i}] = ${arguments[i] ? arguments[i].toString().substr(0,100) : 'falsey'}`); }
// var d = b.isStrNum(a) ? (c && b.isString(c) ? RegExp(c) : b.getNumRegx).exec(a) : null;
// return d ? d[0] : null
// },
// compareNums: function (a, c, d) {
// console.log(`compareNums: function (a, c, d) {`);
// console.log(`arguments.callee = ${arguments.callee.toString().substr(0,100)}`);
//
// We are trying to figure out how a set of functions work, and I want each function to report
// its name when we enter it.
//
// To save time, options and the function that is called on each cycle appear at the beginning
// of this file. Ideally, they would be --something options on the command line.
const readline = require('readline');
//------------------------------------------------------------------------------------------------
// Here are the things that would properly be options on the command line. Put here for
// speed of building the tool.
const frameSize = 10;
const shouldReportFrame = false;
function reportFrame() {
for (i = frame.length - 1; i >= 0; i--) {
console.error(`${i}. ${frame[i]}`); // Using the error stream because the stdout stream may have been coopted.
}
}
function processFrame() {
// console.log(`******** ${frame[0]}`);
// if (frame[0].search('console.log(\`arguments.callee = \$\{arguments.callee.toString().substr(0,100)\}\`);') !== -1) {
// if (frame[0].search('arguments.callee') !== -1) {
// if (frame[0].search(/console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/) !== -1) {
var matchArray = frame[0].match(/([ \t]*)console.log\(`arguments.callee = \$\{arguments.callee.toString\(\).substr\(0,100\)\}`\);/);
if (matchArray) {
// console.log('******** Matched');
frame.splice(1, 0, `${matchArray[1]}console.log('${frame[1]}');`);
}
}
//------------------------------------------------------------------------------------------------
var i;
var frame = [];
const rl = readline.createInterface({
input: process.stdin
});
rl.on('line', line => {
if (frame.length > frameSize - 1) {
for (i = frame.length - 1; i > frameSize - 2; i--) {
process.stdout.write(`${frame[i]}\n`);
}
}
frame.splice(frameSize - 1, frame.length - frameSize + 1);
frame.splice(0, 0, line);
if (shouldReportFrame) reportFrame();
processFrame();
// process.stdout.write(`${line}\n`); // readline gives us the line with the newline stripped off
});
rl.on('close', () => {
for (i = frame.length - 1; i > -1; i--) {
process.stdout.write(`${frame[i]}\n`);
}
});
// Notes
//
// We are not going to control the writing to the output stream. In particular, we are not
// going to listen for drain events. Nodejs' buffering may get overwhelmed.
//

How to observe value changes in JS variables

Im wondering if someone might be able to help me with something that i think it fairly straight forward:
Essentially i want to extend the prototypes of all datatypes (including intrinsic types), to allow some kind of custom functions, consider:
var x = "some string";
var y = 101;
x = "some other value";
y++;
x.onChange();
y.onChange();
This is the basic idea im after, but really what i want is to actually have the onChange (in this example) to be different so a new function for the actual variable (rather than a stardard prototype extension), ie:
x.onChange = function() {
alert("x.onChange");
}
y.onChange = function() {
alert("y.onChange");
}
This doesnt seem to work but i must be missing something quite simple no? I mean surely i can extend all object and types and add on new functions... no?
Any help would be greatly appreciated!
I might be tempted to approach this not by trying to add methods to existing types, but to create an object that can wrap a primative type. I would call this "observing" a value, and might implement it something like this:
function observable(v){
this.value = v;
this.valueChangedCallback = null;
this.setValue = function(v){
if(this.value != v){
this.value = v;
this.raiseChangedEvent(v);
}
};
this.getValue = function(){
return this.value;
};
this.onChange = function(callback){
this.valueChangedCallback = callback;
};
this.raiseChangedEvent = function(v){
if(this.valueChangedCallback){
this.valueChangedCallback(v);
}
};
}
This can then be used to observe changes in any value (so long as that value is then changed only by methods on the observable class - a small detraction IMO).
Something like this would work with the above code:
var obs = new observable(123);
obs.onChange(function(v){
alert("value changed to: " + v);
});
// the onChange callback would be called after something like obs.setValue(456);
Live example here --> http://jsfiddle.net/MeAhz/
Extend the object prototype:
Object.prototype.foo = function() { alert('hello world'); };
var a = 1;
a.foo();
The standard DEPRECATED way : Object.observe()
The Object.observe() method was used for asynchronously observing the
changes to an object. It provided a stream of changes in the order in
which they occur. However, this API has been deprecated and removed
from browsers.
let myObservdObject = Object.observe( { a : 'foo' }, e=>console.log('change!', e) );
myObservdObject.a = 'bee';
// callback gets executed
// and prints 'changed! in console, with the change event data
But proxies arrived to the Standard (ES6) an Object.Observe became deprecated and, in consecuence, unsupported by the browsers.
Proxies are the new way to observe... but implement a generic observer requires a more complex implementation, in comparsion with the way Object.observe used to provide us.
Observe value changes with third party libraries
You can find arround many implementations based in proxies.
Some of them implement the Observer pattern, wich forces you to set or get the values using specific methods :
Observe :
https://www.npmjs.com/package/observe
// define your object
var object = {a:'bee'};
// generate an observer
var observer = observe(object);
// declare the onchange event handler
observer.on( 'change', change=> console.log(change) );
// ready!
// set the value of 'a' and see how the callback is executed...
observer.set('a', 'foo')
// get the new value
observer.get('a') // returns 'foo'
Other libraries instead, let you interact with your variables using a more natural way:
WatchJS :
https://github.com/melanke/Watch.JS/
// define your object
var object = {a:'bee'};
// generate an observer and declare de hadler
watch(object , "a" , e=>console.log(e) );
// ready!
// set the value of 'a' and see how the callback is executed...
object.a = 'foo';
// get the new value
object.a // returns 'foo'
My own apprach : deep-observer
All the implementaions have their own caveats, and none of them was working for my purposes, so i had to implement my own approach.
The result is a highly customizable Observer method with a really small footprint ( <100 bytes gziped)
Deep-observer : https://www.npmjs.com/package/deep-observer
// create an observable object
const myObserved = new Observer( { a : 'bee' } , e=>console.log(e) ),
// perform a modification
myObserved.a = 'foo';
// console : { action:'update', oldValue:'bee', object:{a:'foo'}, name:'a' }
myObserved.a; // returns 'foo'

Categories

Resources