There is a function in a javascript library I want to override for my own use case, I want to use most of the code from the function but add some extra functionality. In my console I can access the function by doing, somelibrary.CharCounter.prototype.count this function takes an argument of text.
I have tried the following,
somelibrary.CharCounter.prototype.count = (function(_super, text) {
return function(_super, apply) {
console.log("overwriting", text);
return _super.apply(this, arguments);
};
})(somelibrary.CharCounter.prototype.count)
On the above I get the the console.log as I would expect, but I also get this error,
Uncaught typeerror: _super.apply is not a function
Obvioulsy I am doing something wrong, all I want to do is ovveride the function so it returns something different to it's original method.
You have to pass _super to the outer function only and make the inner function accept the "super" arguments:
Array.prototype.slice = (function(_super) {
return function(x, y) {
console.log("overwriting", x, y);
return _super.apply(this, arguments);
};
})(Array.prototype.slice);
console.log([1,2,3,4,5,6].slice(1,3))
To avoid repetitions, you might want to define a generic function:
function override(obj, method, fn) {
let prev = obj[method]
obj[method] = function (...args) {
return fn.call(
this,
prev.bind(this),
...args,
)
}
}
override(Array.prototype, 'slice', function (_super, x, y) {
console.log("overwriting", x, y);
return _super(x, y + 1)
})
console.log([1,2,3,4,5,6].slice(1,3))
You can use a Proxy for this. Here is a demo on proxying Math.sin:
Math.sin = new Proxy(Math.sin, {
apply: function(original, thisArg, args) {
console.log(`Executing ${original.name}(${args.join()})`);
return original.apply(thisArg, args);
}
});
console.log(Math.sin(2));
Maybe slightly more readable solution:
somelibrary.CharCounter.prototype.foo = (function () {
var _super = somelibrary.CharCounter.prototype.foo;
return function (text) {
console.log("overwriting", text);
return _super.apply(this, arguments);
}
})();
It is also possible to define generic extendFunction() function for overriding functions:
function extendFunction(superFunc, overridingFunc) {
return function () {
overridingFunc.call(this, superFunc, arguments);
}
}
somelibrary.CharCounter.prototype.foo = extendFunction(
somelibrary.CharCounter.prototype.foo,
function (superFunc, args) {
console.log("overwriting", args);
return superFunc.apply(this, args);
}
);
Related
The code that I am practicing with is where I have a function called InReverse. This function accepts a function as an argument and returns a function. When the function returned is invoked, it reverses the order of the arguments.
When the returned functions are returned:
const catDog = ('cat', 'dog') => returns ('dog cat')
What I have rewritten out so far is:
function inReverse (func) {
return function (...arguments) {
return arguments.map((element) => {
element.reverse();
});
}
}
Any guidance would be appreciated!
You need to simply call the input function inside the newly created anonymous function.
For example this works:
function inReverse(f) {
return function () {
let args = [...arguments].reverse();
return f.apply(this, args);
}
}
So for example if you have subtract function like this:
function subtract(a, b) {
return a-b;
}
subtract(1, 10); will be -9 as expected.
and inReverse(subtract)(1, 10) will be 9 as expected.
Not sure why you're using map, just call reverse right on the arguments array. Also you weren't calling func:
function inReverse(func) {
return function(...args) {
return func(...args.reverse());
};
}
(Notice that arguments is a reserved identifier in strict mode, you should name your parameter for something else)
Not sure, why you are using map method, just use reverse method in your function right on the arguments. Reference : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse?retiredLocale=id
Just reverse the arguments, and use func.apply(this, arr)
function inReverse(func) {
return function() {
var args = Array.prototype.slice.call(arguments)
args = args.reverse();
return func.apply(this, args);
}
}
function div(a, b) {
return a / b
}
console.log(div(1, 2))
console.log(inReverse(div)(1, 2))
Just because of curiosity I wanted to make a bounded function with this particular approach :
var fn = function(a, b, c) {
return a.call.apply(a.bind, arguments)
}
var boundFn = function(a, b, c) {
fn.apply(null, arguments)
}
function unboundedFn() {
console.log(this, arguments)
}
var boundedFn = boundFn(unboundedFn, x, y);
So I'm trying to understand what a.call.apply(a.bind, arguments) do exactly ?
If You have a function like this:
function unboundedFn() {
console.log(this, arguments)
}
You can use unboundedFn.call(thisObj, arg1, arg2) or unboundedFn.apply(thisObj, [arg1, arg2]) to run it, but changing what this means inside. Both call and apply do the same, and only difference is way of passing arguments.
Becouse call, apply and also bind are methods, you can run for example unboundedFn.call.call.call.call.apply.apply.apply.apply(), but it doesn't seem to have to much sense.
In your example return a.call.apply(a.bind, arguments) is equal to return a.bind.call(...arguments), which is equal to a.bind(...arguments.slice(1)), so whole fn function can be simplified to:
function fn(a,b,...args){
return a.bind(b, ...args);
}
How to detect that getX is called, to trigger another function?
Limitations: module can be modified only after its declaration, because this object is received from a script loaded from a remote website. The goal is to tiger
A. This script is loaded from another website (<script src="//remote.com/script.js">)
var module = {
x: 42,
getX: function() {
return this.x;
}
}
B. This script is on my website <script>...</script>
when_getX_isExecuted function() {
console.log('getX was executed');
}
Use Object.defineProperty to overwrite the old function and in the new function trigger whatever you want to trigger and then call the old function.
var module = {
x: 42,
getX: function() {
return this.x;
}
};
function listenToFunction(object, propertyName, callBack) {
var oldProperty = object[propertyName];
if (typeof oldProperty !== "function") throw Error(`object.${propertyName} is not a function`);
Object.defineProperty(object, propertyName, {
value: function() {
var params = [].slice.call(arguments);
var result = oldProperty.apply(object, params);
callBack(object, propertyName, params, result);
return result;
}
});
}
console.log(module.getX()); // will not trigger
listenToFunction(module, "getX", function(object, propertyName, params, result) {
console.log(`The function '${propertyName}' was called on the object ${JSON.stringify(object)}, with arguments ${JSON.stringify(params)}. The result was ${JSON.stringify(result)}`);
});
console.log(module.getX()); // will trigger
console.log(module.getX(1, 2, 3)); // will trigger
Simply override the function and add a call to your when_getX_isExecuted function. Here is an example. Hope it helps
function when_getX_isExecuted () {
console.log('getX was executed');
}
var module = {
x: 42,
getX: function () {
return this.x;
}
}
//override the function getX()
module.getX = function () {
when_getX_isExecuted ();
return this.x;
}
//test by calling getX()
var x = module.getX ();
console.log (x)
I think you could use prototype to pimp up code that's imported from third party.
Prototype
I have this spec from Jasmine.js which tests a once function. I'm not sure how to implement such a function though.
/* Functions that decorate other functions. These functions return a version of the function
with some changed behavior. */
// Given a function, return a new function will only run once, no matter how many times it's called
describe("once", function() {
it("should only increment num one time", function() {
var num = 0;
var increment = once(function() {
num++;
});
increment();
increment();
expect(num).toEqual(1);
});
});
I don't quite understand what should I do here. I know I should make a function once(myFunction) {} but other than that, I am stuck. I figure out this has something to do with closures, still can't my head around it.
If you prefer not to use UnderscoreJS, you can implement a simpler "once" function yourself like this:
var once = function (func) {
var result;
return function () {
if (func) {
result = func.apply(this, arguments);
func = null;
}
return result;
}
};
When you pass your function as the argument to this once function (as the parameter as 'func'), it returns a function that can only be called once.
It accomplishes this feat, in short, by creating a results variable and assigning that variable the results of calling your function with its supplied arguments--but only the first time it is run. Otherwise, when the function is invoked subsequent times, it will never enter your if statement (because the func variable was set to null in the first invocation) and the value referenced by the results variable (set during the first invocation and accessed via closure) will be returned.
Copied from the UnderscoreJS source:
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
http://underscorejs.org/docs/underscore.html
Very, very minimal
const once = fn => (...args) => {
if (!fn) return;
fn(...args);
fn = null;
};
(Old school version)
function once(fn) {
return function() {
if (!fn) return;
fn.apply(null, arguments);
fn = null;
}
}
I've just wrote this function for answer on SO:
function ngWrap($scope, fn) {
return function() {
var args = [].slice.call(arguments);
return $scope.$apply(function() {
fn.apply(null, args);
});
};
}
With a better version:
function ngWrap($scope, fn) {
return function() {
var args = [].slice.call(arguments);
if ($scope.$$phase) {
fn.apply(null, args);
} else {
return $scope.$apply(function() {
fn.apply(null, args);
});
}
};
}
that can be use to shorten this:
socket.on('myevent', function(arg){$scope.$apply(function(){trigger_fn(arg)})})
into:
socket.on('myevent', ngWrap($scope, trigger_fn));
Does this function have a name (the first one) or maybe you can write simple version of it using underscore (that can use just $scope.$apply.bind($scope) as argument) or maybe lisp have a function that word that way?
Does this function have a name
Not that I know, but I could think of "lifting fn to the Angular $scope" or so.
you can write simple version of it using underscore
Yes, what you're doing there is basic function composition, so you can use Underscore's compose function:
function ngWrap($scope, fn) {
return _.compose($scope.$apply.bind($scope), fn);
}