Is it possible to edit a JavaScript function after the page has loaded?
I want to edit a function dynamically after Loading.
You can't edit a function, but you can replace it, e.g.:
var myFunc = function() { return "Hello World"; };
myFunc = function() { return "Goodbye"; };
javascript functions are objects, so can be replaced by setting a new value. Is this what you mean?
Unless you are trying to hack some code that doesn't belong to you, the better solution is to write a more flexible initial javascript function who's behavior can be adapted based on conditions (parameters passed, environment, other state, data fetched from other sources, etc...). Then, the one function you have can be written once initially to handle all your different circumstances.
You can even use design patterns such as passing in callback functions that can be used to adapt the behavior at runtime. If you desire many callbacks, you can pass in an object that has a number of different optional methods and call those during your function. In this way you can significantly alter the behavior of the main function without ever changing it's code by passing in different callback functions.
For example, let's assume we have a parsing function that takes some tagged data structure as input and returns an array of results. We want to be able to modify the behavior of this parsing function by passing in callbacks. So, we write the parsing function to take a callback object. That callback object contains one or more methods (all of which are optional) and a state variable that is passed to each callback. Anyone who has worked with ajax or any asynchronous networking in Javascript will recognize the callback object concept. Here's some pseudo code for such a process that shows how the callback object can be used. A real function would obviously be a lot more involved than this one, but it hopefully illustrates the concept:
function parseMyData(data, callbacks) {
var output = []; // output we accumulate
var currentTag;
callbacks = callbacks || {}; // make the callbacks object optional
// do any preprocessing that the caller specified
if (callbacks.preProcessData) {
data = callbacks.preProcessData(data, callbacks.state);
}
[[code to parse to the first tag in the data (after doing so currentTag contains the tag we just parsed)]]
// give our callback object the opportunity to do something to this tag or return null to skip it
if (callbacks.preProcessTag {
currentTag = callbacks.preprocessTag(currentTag, callbacks.state);
}
if (currentTag) {
[[code here for the default processing of the tag that will push results into the output array]]
}
return(output);
}
If you want to add an action to the existing function you can "hijack" it by putting it in a temporary variable and calling it within your overwritten function. E.g.
// The original function.
function sayName(name) {
alert(name);
}
// Temporary variable for original function.
var __sayHello = sayName;
// Overwrite the original function, adding extra actions.
sayName = function(name) {
alert('Hello');
// Call the original function using its temporary variable.
__sayHello(name);
}
// Call the overwritten function.
sayName('Bob');
How to edit a function - 101.
If we reconsider that editing a function at runtime is not absolutely changing the guts of the function, but rather changing what the function guts are digesting, then I would say functions are already built to do just that.
example 1 - The guts cannot be changed.
function one(){
return true;
}
// one() => true;
// Note: We could still change the output without changing the guts.
// !one() => false;
example 2 = The guts can be created to digest a dynamic call.
function one(payload){
return payload;
}
// one(true) => true;
// one(false) => false;
// one('whatever I want to feed the guts to digest');
// => 'whatever I want to feed the guts to digest';
These are quite simple examples, but since you did not provide any real examples as to what you are trying to do, we have to assume you are attempting normal patterns of programming.
Considering NORMAL patterns of programming, it wouldn't be the function itself that needs to change, rather how you are calling it.
example 3 - Give the choice of which function to the caller.
function firstChoice(payload){
return http.post(payload);
}
function secondChoice(choice){
return `Do something else with ${choice}`;
}
// And where you make the choice, perhaps after a click event handler...
function onClick(choice, payload){
choice ? firstChoice(payload) : secondChoice(choice);
}
Functions are supposed to be small bricks with which you build logic. Give them something small to do, then select between them based on your logic.
To answer your question, in my opinion, assuming normal programming needs...
"Is it possible to edit a JavaScript function after the page has loaded?" YES.
Use arguments in your function definition to add the dynamic ability to suit your needs.
Related
I'm adapting to using Protractor as a UI automation framework, having used Selenium with Java, Ruby and Groovy extensively in the past. I'm new to Javascript so I'm not sure if some of my old tricks are transferrable.
One of the most useful things I came up with in Ruby and Groovy was a "multiwait" library that would allow me to wait for one of set of mutually exclusive events to occur. Given a map in which the keys are simple descriptions of events, and the values are chunks of executable code, the function would simply loop through each of the possible events, and when an event returned true, the function would return the key for that event. If none of the events returned true within a given timeout, an Exception would be thrown summarizing the events and the time that it waited for. There was also a special event, "nothing," for situations in which an action might give rise to a warning or error event if something was wrong with the data, but would usually elicit no response at all if the data was good. If "nothing" was one of the events, then instead of throwing an Exception at the end of the timeout, the function would return the key for the "nothing" event.
I am now trying to re-implement this method in Javascript, but I'm very new to the language and I'm not sure of the best way to go about it.
So I know that in Javascript I can store functions as variables. Should I be using functions as my event values in this Hash Table? Will they present any problems with scope, or should everything work as long as each function is able to see the variables that it uses?
If someone could run through a simple example with me that'd be very helpful. Let's say that I have a page object with a method called getColor which retrieves some information from the DOM. In this case let's say that it will always return the String red. I want to create a method in a separate file that will accept a Hash Table of events, something like this:
var WaitForEvent = require('../../waitForEvent');
var wait = new WaitForEvent();
function getColor() {
return 'red';
}
var outcomes = {};
outcomes['Data accepted'] = function () { getColor() == 'green' };
outcomes['Data rejected'] = function () { getColor() == 'red' };
var result = wait.waitFor(outcomes);
expect(result).toBe('Data accepted');
So, that's a rough idea of how I want to set up the parameters for the method itself. Would functions like that work in this context? Could a waitForEvent function loop through the functions, testing each one to see which one returned true first, and then return the key describing the event that occurred? Or do I need to go about this in a different way?
Assuming it would work, what would it look like in the method itself, as I'm looping through the values of the Hash Table which are themselves nameless functions? What's the proper way to execute such a function and check what it returns?
One thing I'm already aware of in Selenium, which I'd like to avoid, is Selenium's ExpectedConditions with the OR condition chaining conditions together. I'd rather something more flexible than that.
Thanks!
Let's say you have (I'm translating these to es6):
const getColor = () => 'red';
outcomes['Data rejected'] = () => getColor() === 'red';
Now you can do:
let keys = Object.keys(outcomes).filter(k => outcomes[k]())
and keys[0] will be 'Data rejected' (instead of undefined)
I'm not sure if that answers your question though. To "wait" for it you would need to put it in a async function with some setTimeouts.
I want to overwrite a function, which is a method of a prototype of an Object. This method uses this internally. How can I overwrite that function but still keep this defined as the same object / value, as it has been in the original function?
The reasons I want to keep it are the following:
The original code supports extensions. I'd rather prefer writing an extension and add that to the code as intended by the authors of the original code, than changing the original code and having to build or install a modified version.
I need always perform some action before the original function is called. So I actually want to keep the original functionality, but want to add something before it. I think this would be "decorating" the function, like a decorator in Python.
The scope in code where I overwrite the function is not the same as where it is defined originally.
Here is some example code:
//original functions
var original_execute_cells = Jupyter.Notebook.prototype.execute_cells;
// decorating functions
function decorated_execute_cells(cell_indices) {
console.log('EXTENSION: running decorated execute_cells function');
return original_execute_cells(cell_indices);
}
// overwrite original functions
Jupyter.Notebook.prototype.execute_cells = decorated_execute_cells;
However, when I do that, I get a TypeError telling me that this is not defined, in the first line of the original function, which uses this.
The original source code of the function I am trying to overwrite can be found on github:
Notebook.prototype.execute_cells = function (indices) {
if (indices.length === 0) {
return;
}
var cell;
for (var i = 0; i < indices.length; i++) {
cell = this.get_cell(indices[i]);
cell.execute();
}
this.select(indices[indices.length - 1]);
this.command_mode();
this.set_dirty(true);
};
I now did it with bind. The return statement becomes:
return original_execute_cells.bind(Jupyter.notebook)(cell_indices);
Where Jupyter.notebook is in my specific case the object this related to, when the Notebook object was build.
However, what #siebetman mentioned in the comment could also work and might even be more flexible in some situations. Also the link siebetman provided seems very useful in order to understand Javascript's this better.
Why not just bind this?
var original_execute_cells = Jupyter.Notebook.prototype.execute_cells;
Jupyter.Notebook.prototype.execute_cells = function(cell_indices) {
console.log('EXTENSION: running decorated execute_cells function');
return original_execute_cells.bind(this)(cell_indices);
};
Apologies - I have no idea to how to describe this. Example:
function OutputNumber(number) {
this.outputThisInstead = function (otherNumber) {
console.log(otherNumber);
}
console.log(number);
}
Desired usage:
new OutputNumber(1);
Console output: 1
new OutputNumber(1).outputThisInstead(2);
Console output: 2
Naturally, 1 will always be written to the console, irrespective of what else is called on the object.
I'm after this particular syntax, as well as the behaviour - attaching a function onto the initialisation. It feels impossible since the object must be constructed before any function is called on it, but is this achievable any other way?
It would be possible with a time delay (e.g., in a browser environment, setTimeout or similar) and a flag. Not desirable, but possible.
Without that, no, you can't base the action of the constructor on something that hasn't happened yet. You'd have to instead pass something into the constructor to let it know what was going on.
Browser example (again, I don't recommend this):
function OutputNumber(number) {
var handle = 0;
this.outputThisInstead = function (otherNumber) {
if (handle) {
clearTimeout(handle);
handle = 0;
}
console.log(otherNumber);
}
handle = setTimeout(function() {
console.log(number);
}, 0);
}
From your comment on the question:
This is the end of a sequence of chaining objects/functions, that I'm experimenting with. For example:
Assert.that(1).is.not(2).because('output this message if fails');
Here not(2) returns an object on which because can optionally be called. The behaviour of the object would depend on because being called.
Rather than have the behavior of an earlier function in the chain depend on a later function in the chain, I'd probably add an .end() at the end of something:
Assert.that(1).is.not(2).because('output this message if fails').end();
end would output whatever message/messages was/were stored by the previous functions. No need for black magic. Obviously this suffers from the fact that people could fail to put the .end() on, but you need some kind of trigger that it's okay to do the output, if you want the output to change based on an optional subsequent function call.
Not possible. By the time you do new OutputNumber(1) the function has already been called. A chained method will have no access to its preceding call.
It's possible to declare outputThisInstead as "static":
function OutputNumber(number) {
console.log(number);
}
OutputNumber.outputThisInstead = function (otherNumber) {
console.log(otherNumber);
}
new OutputNumber(1); //1
OutputNumber.outputThisInstead(2); //2
But if you want to create an object with the new operator the function will always log the number parameter.
You can also achieve similar behavior to the one you want with partial apply of the function (here). This is also called Currying or Schönfinkeling. The idea is that you can fill the function's parameters one after another and when the full set of parameters is available the function is being executed. You can see a currying example here.
I'm currently in the process of building out a VERY simple Observer class for a project I'm working on. I have successfully implemented the subscribe, unsubscribe, and notify methods. Everything works exactly as expected when using "regular" functions (i.e: var f = function()).
However, when I pass an anonymous function to the subscribe method and then try to unsubscribe passing the "same" anonymous function it (as expected) doesn't remove the function from my array (they are different, after all).
Here's my subscribe and unsubscribe methods:
this._subscribers = {};
subscribe: function(type, callback) {
if ( isUndefined(this._subscribers[type]) ) {
this._subscribers[type] = [];
}
this._subscribers[type].push(callback);
},
unsubscribe: function(type, callback) {
if ( this._subscribers[type] instanceof Array ) {
var index = this._subscribers[type].indexOf(callback);
if ( index >= 0 ) {
this._subscribers[type].splice(index, 1);
}
}
},
And here's the code I'm testing with:
var o = new gaf.events.Observable();
o.subscribe('testEvent', function(event) { alert('Got It!'); });
o.notify('testEvent');
// Correct alerts 'Got It!'
o.unsubscribe('testEvent', function(event) { alert('Got It!'); });
o.notify('testEvent')
// Incorrectly alerts 'Got It!'
I know I could using an object (i.e.: _subscribers[event] = {}) and then when something subscribes I could add a new property equal to the callback and the value equal to the callback. This will cause Javascript to convert the callback to the string. I could then look it up (provided the methods passed in sub/unsub are exactly the same) using that string.
However, this is a mobile project and I'm very leery about storing strings that could be hundreds of characters long as properties as we could end up with a lot of subscribers.
Are there any other ways of doing this? Are there any SMALL (tiny, even) hashing libraries I can use to maybe hash the string value of the function and use that as the property? Would it be better to store the string value of the callback (so I can compare against it) in the array (rather then the actual callback) and use eval() on it?
EDIT
First, thanks all for the replies!
Per all the questions about "Why even pass anonymous" functions -
There really is no reason one COULDN'T use named functions. In fact, I agree with everyone that named functions are going to be the better solution. I'm simply gathering information and looking for a solution so that I can build out an implementation that handles the most scenarios as best as possible.
The other reason for this is what happens if a user (co-worker) of this Observable class passes it an anonymous function and then unsubscribes. That function won't actually be unsubscribed and therefore won't be cleaned up. I have a thing against orphaned data :)
Maybe another question I should as is, is it possible to test if the callback is anonymous or not? I'm going to assume no but doesn't hurt to ask.
There is nothing wrong with storing the entire string; premature optimization is evil.
However, this sounds like an incredibly bad idea.
If someone changes the function, but forgets to change the unsubscribed copy, the code will be subtly broken with no warning whatsoever.
Instead, you can require the user to store the anonymous function in a variable if they want to unsubscribe from it.
Alternatively, you can pass an optional name with each subscriber, then unsubscribe by that name.
the clients that use the Observer should store the reference to the function.
var obsCallback = function() {
}
o.subscribe('test', obsCallback);
....
o.unsubscribe('test', obsCallback);
in other words, keep a reference to the function around...
Perhaps a better solution is to modify the code using your library
var f = function() { alert('Got It!'); };
o.subscribe('testEvent', f);
o.notify('testEvent');
o.unsubscribe('testEvent', f);
o.notify('testEvent');
You could even return the function from the subscribe method
var f = o.subscribe('testEvent', function() { alert('Got It!'); });
// ...
then if you want to store a hash or some other identifier for subscribed functions, it is opaque to the calling code meaning that you just use the returned value to unsubscribe and the library hides the implementation detail.
What is the reason for passing in anonymous functions rather than named ones, or keeping references that you can use for unsubscribing later?
Alternatively you could allow for an optional 'id' argument but this would require unnecessarily complex bookkeeping to avoid duplicates.
I'm building a concert calendar that's very heavy on javascript (using jQuery). I'm having trouble synchronizing various events throughout the app, and I'm looking for suggestions for how to do this.
An example of a simple use case:
User clicks a month
Calendar skips to that month
An example of a more complex use case:
User selects an artist
Calendar determines the date of that artist's first show
Calendar skips to that month
Calendar highlights that artist's show(s)
One occasional problem is that the new month isn't yet rendered by the time I try to highlight the artist's show(s). Thus, the show isn't highlighted even though these functions are called in order. Obviously, using setTimeout() is pretty hacky, and not guaranteed to work.
So first, a simple question -- would it ever be possible (even in Chrome) for the following function to run out of sequence?
function steps(){
stepOne(); //TAKES 30 SECONDS
stepTwo(); //TAKES < 1 SECOND
}
Second, a related simple question:
If placed at the end of a function, will a JS callback ALWAYS run after
everything else in the given function has finished running?
If so, I could nest each function as a callback of its previous function. But that would likely become unwieldy, once you consider all the different use cases.
Here's an approach I'm considering:
1) Allow each function an optional callback parameter. If it's present,
call it at the very end of the function.
2) During a UI refresh, create an array, and stack any functions to be
called within this array.
3) Once this "script" is completed, iterate through the array, calling
each function in the order it was added, using the next function
as the previous function's callback.
I imagine this would ensure that all the functions are called in order.
Another approach is to attach event listeners using
$(document).bind("listener.name", fnCallback);
And then calling
$(document).trigger("listener.name");
Whenever that event occurs.
However, I'm guessing this would be kind of unwieldy as well, considering different events might need to call different sets of functions depending on the use case. I could always call
$(document).unbind("listener.name");
before adding new events to it, but again -- I'm leaning toward creating sort of a master "script" as I suggested in the first approach.
Hopefully this isn't too vague -- any feedback? Any experience with complex UIs that had to synchronize various events?
Thanks very much,
Michael
Your approach #1 is the best way, and the most natural using jQuery. Most functions that act on the user interface and do something accept a callback function parameter, which gets called after the function has executed.
Where you are doing things not implemented in jQuery following the same pattern will make your code more readable. dominic's answer is a good terse example:
function steps(){
stepOne(stepTwo);
}
function stepOne(callback){
var AsyncDone = function() {
//any Synchronus Things here
callback();
}
someAsyncFunction( params, AsyncDone );
}
Try passing in the function to be called back to as an argument
function steps(){
stepOne(stepTwo);
}
function stepOne(callback){
var AsyncDone = function() {
//any Synchronus Things here
callback();
}
someAsyncFunction( params, AsyncDone );
}
The last approach, of using custom events, is the best approach in my humble opinion.
Design various components and events and let them interact with each other.
E.g. Let's say you have a Calendar object.
var calendar = function()
{
var pub = {};
pub.highlightRow = function(row) {};
pub.getRowByContent = function(content) { };
pub.selectMonth = function()
{
//your code to actually select month.
//Once all the necessary DOM work is done, fire the
//monthSelected event (namespacing the event so as to avoid the clash with other events).
$(document).trigger('calendar:monthSelected');
};
return pub;
}();
Now your artist search method may look like,
function searchArtist(artistName)
{
$(document).bind('calendar:monthSelected'), function()
{
calendar.highlightRow(calendar.getRowByContent(artistName));
}
);
calendar.selectMonth(getShowMonthByArtist(artistName));
}
You can use javascript's then() method to synchronize the functions and have them execute in the order that you want, like so:
function steps() {
stepOne().then(function() { stepTwo(); })
}