I'm working on a pet project, a little front-end library for students. It reads variables/code in a JS file and tests it, outputting some panels. The code itself roughly follows the Jest framework.
My problem is that I'm trying to create a function that watches the execution of other functions, counts them, and lets me access the count.
function watchFunction(funcName){
let originalFunction = window[funcName];
let counter = 0;
// Wrap the function, counts when called
window[funcName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
This seems to work for methods like Number() or parseInt(), but I don't know how I would go about accessing methods like Math.floor(), or prototype methods like Array.prototype.map().
I've tried passing in the function reference instead of using window["funcNameString"], but that doesn't seem to work.
Does anyone have suggestions or tips for wrapping functions or watching functions like this?
EDIT:
It appears a solution was found!
function watchFunction(obj, fName) {
let counter = 0;
const originalFunction = obj[fName];
obj[fName] = (...args) => {
counter++;
return originalFunction.bind(obj)(...args);
};
return {
removeWatcher: () => (obj[fName] = originalFunction),
resetCount: () => (counter = 0),
getCount: () => counter,
};
}
Example of use:
// Array.prototype.push
const arrayPushWatcher = watchFunction(Array.prototype, "push");
let arr = [];
// 0
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 1
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 2
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arrayPushWatcher.removeWatcher();
arr.push(1);
// 2 (stopped counting)
console.log("Array.prototype.push", arrayPushWatcher.getCount());
How to watch for any function call
Is that what you want? I can also write a block for this function so that it determines whether an object has been passed in or a string. If string -> run this function on window as a property "objectThatStoresFunction".
I've tried playing around with the Function.prototype, but it doesn't really work. So the function turned out a bit more complex.
This code below works both with functions / objects on window Array.prototype.map (Prototype / Class functions)
function watchFunction(objectThatStoresFunction, functionName) {
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName];
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction(...args);
}
return {
getCount: () => {
return counter
}
}
}
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log(mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log(mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log(mathRoundWatcher.getCount());
function watchFunction(objectThatStoresFunction, functionName, optionalOriginalFunction) {
const self = this;
if (optionalOriginalFunction) {
objectThatStoresFunction = this.window;
functionName = optionalOriginalFunction.name;
}
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName] || optionalOriginalFunction;
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction.bind(self)(...args);
}
return {
// should it remove the watcher or reset the count?
reset: () => objectThatStoresFunction[functionName] = originalFunction,
getCount: () => {
return counter;
}
}
}
const arrayMapWatcher = watchFunction(Array.prototype, 'map');
// 0
console.log('Array.prototype.map', arrayMapWatcher.getCount());
[-99].map(() => {});
// 1
console.log('Array.prototype.map', arrayMapWatcher.getCount());
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log('Math.round', mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log('Math.round', mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log('Math.round', mathRoundWatcher.getCount());
const alertWatcher = watchFunction(null, null, window.alert);
// 0
console.log('window.alert', alertWatcher.getCount());
// 1
window.alert('1');
console.log('window.alert', alertWatcher.getCount());
// 2
alert('2')
console.log('window.alert', alertWatcher.getCount());
// reset the alertWatcher counter
alertWatcher.reset();
This code above breaks the stacksnippets.com when used with Array.prototype.map for some reason, please see this JsFiddle link:
https://jsfiddle.net/ctbjnawz/3/
Do you mean a method of an instance or object? One way is to create a new function. e.g
function WatchInstanceMethods(instance, functionName){
let originalFunction = window[instance][funcName];
let counter = 0;
window[instance][functionName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
although adding support for chaining methods will get difficult with more nested methods but you can pass a string for functionName name and split it to have each layer of calling instance for function and repeat the logic above.
Related
Try to use functional programming to create an object with external functions to reduce memory usage.
The function is
//increment no of test cases
function incrNoOfTestCases(inputObj){
let hits = inputObj.hits;
console.log(`function says: ${hits}`)
return {...inputObj, hits: (hits || 0) + 1};
}
The creator function is
const test = function(testDecription){
let state = {hits:0};
state.getState = ()=> testDecription;
state.incHits = () => state = incrNoOfTestCases(state);
state.getHits = () => state.hits || 0;
return state;
}
When I do the following test, I can change the hits by assigning a property with to the function.
test1.incHits().hits=10; //mutable!!
console.log(test1.getHits()); //gives 10
console.log(test1.incHits().hits); //gives function says: 10 and then 11
test1.hits=20; //immutable
console.log(test1.getHits()); //gives 10
I tried various alternatives, finally came up with declaring the function to increment the testcases in the creator function. I am looking for an explanation why the property is mutable not for a working case.
In the first version the function was
function incrNoOfTestCases(inputObj){
return {...inputObj, hits: (inputObj.hits || 0) + 1};
}
In this case I also expected the inputObj.hits not to be mutable by incrNoOfTestCases.hits, but wasn't either.
It seems JavaScript firstly assigns incrNoOfTestCases.hits to state before executing the function. Is this correct? Can you explain why?
There is nothing functional about this code. In functional programming you don't want small logical units to handle their state independently. That's OOP. Using a closure is just the same as using a class if you mutate the value.
This is more functional although it probably doesn't work the way you would like.
const Test = (description, hits = 0) => ({
getState: () => description,
incHits: () => Test(description, hits + 1),
getHits: () => hits
})
const test1 = Test('description')
const test2 = test1.incHits(); // incHits returns a new instance of Test
console.log(test2.getHits())
And this would have done the same thing
class Test {
constructor(description, hits = 0) {
this.description = description;
this.hits = hits;
}
static of (description) { return new Test(description) }
getState () { return this.description}
incHits () { return new Test(this.description, this.hits + 1); }
getHits () { return this.hits }
}
const test1 = Test.of('description');
const test2 = test1.incHits();
Yet another way to do it
const Test = (description, hits = 0) => ({ description, hits, type: 'Test' });
export const getState = ({ description }) => description;
export const incHits = ({ description, hits }) => Test(description, hits + 1);
export const getHits = ({ hits }) => hits;
export const of = (description) => Test(description);
import * from './Test'
const test1 = Test.of('description');
const test2 = Test.incHits(test1);
I have an array of objects. Each object has a method that should update a boolean property in the same object called 'found'.
When I call the function, the property does not update. I am not sure why.
I thought that the 'found' property would be accessible but it isn't??
I have created a minimal version of the problem here:
https://codepen.io/sspboyd/pen/XWYKMrv?editors=0011
const gen_s = function () { // generate and return the object
let found = false;
const change_found = function () {
found = true;
};
const update = function () {
change_found();
};
return {
change_found,
found,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].found); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].found);
When your change_found function changes the value of found, it's changing the value pointed to by your let found variable, but the object returned by your gen_s function still points to the old value.
You can fix your code using the 'holder' pattern, like this:
const gen_s = function () { // generate and return the object
let foundHolder = {value: false};
const change_found = function () {
foundHolder.value = true;
};
const update = function () {
change_found();
};
return {
change_found,
foundHolder,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].foundHolder.value); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].foundHolder.value);
Or even better, use a class:
class S {
constructor() { this.found = false; }
change_found() { this.found = true; }
update() { this.change_found(); }
}
const s_arr = [];
s_arr.push(new S());
console.log(s_arr[0].found);
s_arr.forEach(s => s.update());
console.log(s_arr[0].found);
I have a module that I'm exporting. I need one function to call another function. Here's a simplified version of what I'm trying to do.
module.exports = {
isEven: (number) => {
return (number%2 == 0)
},
isTenEven: () => {
return isEven(10)
}
}
The code above throws isEven is not defined when moduleName.isTenEven() is called.
It makes sense why it fails. But how would you rewrite it? (While maintaining the singleton pattern)
Define the functions first, then export them:
const isEven = (number) => number % 2 === 0
const isTenEven = () => isEven(10)
module.exports = {
isEven,
isTenEven
}
The object is only used to group the functions together. There's nothing really OO about it so define the functions separately. Construct the object at the end.
const isEven = number => number % 2 === 0;
const isTenEven = () => isEven(10);
module.exports = { isEven, isTenEven };
Maybe just do this? Define then export.
const isEven = number => number % 2 === 0;
module.exports = {
isEven,
isTenEven: () => isEven(10)
};
Just to add one more solution to the mix. You don't have to define the function elsewhere. Since the object declaration is complete before the function gets called, you can refer to it via module.exports or via exports like this:
module.exports = exports = {
isEven: (number) => {
return (number%2 === 0)
},
isTenEven: () => {
return exports.isEven(10)
}
}
If you were doing this in a lot of methods, you could define a shorter variable name for the exported object and refer to it.
If you can afford Babel or a version of Node.js that supports import/export statements you could also do:
export const isEven = num => num % 2 === 0;
export const isTenEven = () => isEven(10);
Inside JS object literal using this refers to the object itself so you can have:
module.exports = {
isEven: (number) => {
return (number%2 == 0)
},
isTenEven: function () {
return this.isEven(10)
}
}
I'm creating a module that accepts a data set and an integer n and recursively fills that dataset with n products at a time, after the first call, the function loses its scope and errors out. Why, And what's the best practice for fixing this?
Code:
function ProductFactory(){
var bigArr = [0,1,2,3,4,5,6,7,8,9];
var smallArr = [1,2,3,4];
return {
getProductList: getProductList,
getAllProducts: getAllProducts
};
function getProductList(start, size){ return start < 5 ? bigArr : smallArr }
function getAllProducts(batchSizeRequested, dataSet) {
var startPage = dataSet.length / batchSizeRequested;
var productBatch = this.getProductList(startPage, batchSizeRequested);
dataSet = dataSet.concat(productBatch);
if (productBatch.length === batchSizeRequested)
getAllProducts(batchSizeRequested, dataSet);
}
}
var productGetter = new ProductFactory();
productGetter.getAllProducts(10, []);
1) First of all you shouldn't call getProductList using this, in this case you can just call it as it is, because getProductList is not a function that was assigned directly to this object. It is just a closure that uses local variables in it's code. If you want to call function using this, you should assign it using this, for example this.getProductList = function() {}
2) I don't think there are other scoping problems except redundant this, but I found another issue, though.
You are not actually return anything from your function, plus recursive call does not have an exit point.
Fixed code looks like this.
function ProductFactory(){
var bigArr = [0,1,2,3,4,5,6,7,8,9];
var smallArr = [1,2,3,4];
return {
getProductList: getProductList,
getAllProducts: getAllProducts
};
function getProductList(start, size){ return start < 5 ? bigArr : smallArr }
function getAllProducts(batchSizeRequested, dataSet) {
var startPage = dataSet.length / batchSizeRequested;
var productBatch = getProductList(startPage, batchSizeRequested);
dataSet = dataSet.concat(productBatch);
if (productBatch.length === batchSizeRequested) {
return getAllProducts(batchSizeRequested, dataSet);
} else {
return dataSet;
}
}
}
var productGetter = ProductFactory();
var products = productGetter.getAllProducts(10, []);
console.log(products)
The typical approach to a function call like this is to assign an external value to this (typically called self):
function ProductFactory(){
...
var self = this;
function getAllProducts(batchSizeRequested, dataSet) {
...
getAllProducts.apply(self, [batchSizeRequested, dataSet]);
}
}
In this case, however, please try to remember that you have defined a closure function getAllProducts that is only privately accessible internal to the constructor. Instead you should probably do:
function ProductFactory(){
...
var self = this;
this.getAllProducts = function(batchSizeRequested, dataSet) {
...
self.getAllProducts(batchSizeRequested, dataSet);
}
}
I'm working on simple evolutionary AI. I need to generate an anonymous function dynamically. For it I have a list of conditions and actions:
var conditions = [
function () { return enemyNear(), },
function () { return mySpeed() > 5; },
function () { return 1 === 1; }];
var actions = [
function () { return alert('walk'); },
function () { return alert('jump'); }
function () { return alert('attack'); } ]
The code chooses one of each to generate a new function:
condition = conditions [Math.floor(Math.random()*conditions .length)];
actions = conditions [Math.floor(Math.random()*actions .length)];
Provided it the chosen condition is enemyNear() and the chosen action is walk(), how can I generate the simple anonymous function?
behavior = function() {
if(enemyNear()) {
walk();
}
}
I can change the way the arrays are saved if needed. How can this be done?
All characters behaviors are called inside a loop like this:
for(i=0,i<chars.length,i++) {
chars[i].behavior.call();
}
The simplest way would be to only put functions inside the arrays:
var conditions = [
enemyNear,
function () { return mySpeed() > 5; },
function () { return 1 === 1;
}];
var actions = [walk, attack, jump];
Then you could define behave as something like:
var behave = function(condition, action) {
if(condition()) {
action();
}
}
And use it like for example:
behave(conditions[2], actions[1]);
Here, 2 and 1 could be a randomly generated number like this:
var getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var actRandomly = function (conditions, actions) {
behave(
conditions[getRandomInt(0, conditions.length -1)],
actions[getRandomInt(0, actions.length -1)]
);
};
Call it like:
actRandomly(conditions, actions);
Of course this only presents the idea, and is not neatly organized.
For the fun of it, I created a Basic jsFiddle Demo Version.
Note: Always use var when you are defining a variable. Don't pollute the global scope.
First, inside of the arrays you will need to have a reference to the condition and action function. Right now, you are calling them, so they basically are the same as a useless array like:
conditions = [true, false, true];
actions = [undefined, undefined, undefined];
I would correct it to something like this:
var conditions = [enemyNear,
function() { return mySpeed() > 5 },
function(){ return true; }];
var actions = [walk, attack, jump];
And then you can create a function that generates a behavior:
function generateBehavior(){
var condition = conditions[Math.floor(Math.random() * conditions.length)];
var action = actions[Math.floor(Math.random() * actions.length)];
return function() {
if(condition()) {
action();
}
}
}
You can see it in action on this JSFiddle demo.
Change your arrays from this:
conditions = [enemyNear(), mySpeed()>5, 1=1];
To this:
conditions = [
function() { return enemyNear() }, // or just enemyNear
function() { return mySpeed() > 5 },
function() { return 1 == 1 } // 1 = 1?
];
With your current code, your functions are being called and conditions becomes an array of the outputs of those functions.
condition = [
enemyNear,
function() { return mySpeed() > 5; },
function() { return 1 == 1; }
];
You need to store your functions in the array, not call them, otherwise you are storing the result of the functions in the array.
function enemyNear() {}
function walk() {}
conditions = [enemyNear]
actions = [walk]
behaviour = function() {
if(conditions[randomNumber]()) {
actions[randomNumber]();
}
}
Simple. Use eval()
var behavior;
eval("behavior = function() { if (enemyNear()) walk(); }");
behavior();