Override JQuery functions (logging decorator) - javascript

I'd like to create a logging decorator around jQuery function but it is called only once (in initialization time). How to fix it? Please look at the code.
function makeLogging(f) {
function wrapper(...rest) {
console.log(f.name);
console.log(rest);
return f.apply(this, rest);
}
return wrapper;
}
const task = $('.task');
task.on = makeLogging(task.on);
task.on('click', () => console.log('hi'));
The click event does not display messages about the called function.

You are doing it a little bit wrong, if I caught the idea what you want to achieve. For functionality, you described, please try following:
task.on('click', makeLogging(() => console.log('hi')));
In your original code, you wrapped the functionality of on() function, but this on() function is not called as event handler - it only install actual event handler. That's why logging is called only once during installation of the handler.

Code example of answer
function makeLogging(f) {
function auxiliaryWrapper(x, rest) {
return () => {
console.log(f.name);
console.log(rest);
x();
}
}
function mainWrapper(...rest) {
const restWithWrap = rest.map(arg => {
if (typeof arg === 'function') {
return auxiliaryWrapper(arg,rest);
}
return arg;
});
console.log(restWithWrap);
return f.apply(this, restWithWrap);
}
return mainWrapper;
}
const task = $('.task');
task.on = makeLogging(task.on);
task.on('click', () => console.log('hi'));

Related

Reactjs function "is not a function"

I am trying to use an additional function on submit as I usually did but it shows it's not a function.
confirmSubmission = () => { // This doesn't work either: confirmSubmission() {
//doSomething();
}
onSubmission(survey, options) {
this.confirmSubmission(); // here it says Uncaught error: confirmSubmission is not a function
}
The function I used:
return (<MyCOmponent model={model} onComplete={this.onSubmission}/>);
Other code that I have within onComplete executes fine if confirmSubmission is not there.
I have a similar code with another function which works perfectly fine in the same component:
componentDidMount() {
this.loadData(); // This works
}
loadData() {
doSomething();
}
I am confused why it doesn't like the confirmSubmission call. Any ideas?
Try replacing
onSubmission(survey, options)
with an arrow function, it's the famous "this" issue
You must have forgotten to bind the this context to onSubmission
constructor(/*...*/) {
//..
this.onSubmission = this.onSubmission.bind(this)
}
or
return (<MyCOmponent model={model} onComplete={this.onSubmission.bind(this)}/>);

Question about ‘this’ inside higher-order-function's callback

I have a question regarding ‘this’ in JavaScript higher-order-function's callback.
I've been exploring the following code - the goal is conversion of a function that accepts a callback into a function returning a promise.
Source: https://javascript.info/promisify
A function that accepts a callback:
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(script);
}
Now, the author uses a higher-order-function that will accept the stated above function as a callback and do the promisification:
function promisify(f) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, result) {
if (err) {
return reject(err);
} else {
resolve(result);
}
}
args.push(callback);
f.call(this, ...args);
});
};
};
// usage:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise('path/script.js').then(...);
The thing that I don't understand is:
Why do we call the f function in this way:
f.call(this, ...args); ?
What will ‘this’ be in this case?
Why can't we just call it like this: f(...args); ?
I know that in order to track down what a callback’s ‘this’ is pointing to, you need to inspect the higher order function enclosing it...
But I can't get why in this case do we have to state the callback’s ‘this’ explicitly?
Thank you for the help!
Why do we call the f function in this way: f.call(this, ...args); ?
What will ‘this’ be in this case? Why can't we just call it like this: f(...args); ?
Let me answer the What will ‘this’ be in this case? part first:
We don't know (in general) and that is why .call is used, but I will get to that.
promisify is supposed to "seamlessly" wrap an existing function. That means that calling either f or promisify(f) should return the same result.
The value of this depends on how a function is called. promisify can't know how the new wrapper function is going to be called, nor does it know whether the wrapped function uses this or not. Therefore it needs to assume that this is going to be significant and has to call the wrapped function in a way that sets this correctly.
The only way to call a function and explicitly set the this value is via .call or .apply. If the function was called as f(...args) then this inside f would either be the global object or undefined.
Here is a simpler wrapper function that demonstrates the issue:
function wrapWithThis(f) {
return function(...args) {
f.call(this, ...args);
}
}
function wrapWithoutThis(f) {
return function(...args) {
f(...args);
}
}
function wrapMe() {
console.log(this.foo);
}
const obj = {
foo: 42,
withThis: wrapWithThis(wrapMe),
withoutThis: wrapWithoutThis(wrapMe),
};
obj.withThis();
obj.withoutThis();
Having said all that, specific to your example, given that loadScript doesn't use this, it wouldn't make a difference if f.call(this, ...) or f(...) was used.

Why is func undefined in this context?

So I have a function like
func()
{
const curVal = this.curVal;
const callAgain = () => { func(); };
Axios.get('somecontroller/someaction')
.then(response =>
{
const newVal = response.data.curVal;
if(curVal === newVal)
setTimeout(callAgain, 500);
else
// ....
})
.catch(response =>
{
// ...
});
}
and my browser is complaining about the line
const callAgain = () => { func(); };
saying that func is undefined. Any idea why? How can I fix?
You cannot define a function the way you posted.
However, you can for example use the function keyword to define your function:
function func() {
...
}
func(); // it works!
Edit:
According to your comment, this is a object method declaration. In order to make this work, you first need to make sure your browser supports this particular ES2015 feature or if not, you transpile it to valid ES5.
Then you should be able to access the function using this.func():
const callAgain = () => { this.func(); };
In case you are using func() e.g. as a callback for a DOM event, you also have to make sure that this is bound correctly in func, for example by explicitly binding it in the constructor:
constructor() {
...
this.func = this.func.bind(this);
}
Define the function using either of the following:
function func(){ ... }
Or...
var func = function(){ ... }
When you define it like this:
func() { ... }
JavaScript thinks you're trying to execute an existing function called func, and then run the block of code { ... }

JavaScript Promises : Deep nested context with bind(this)

Because I'm using a prototype that has functions calling other functions in the same prototype I have to refer to that method using this
The Problem this Created:
But because of that, I have to preserve a context to use this that has me forming very ugly .bind(this) walls.
Here is a simplified example I made for laughs.
Killmyself.prototype.fireLeMissles = function () {
return new Promise(function(resolve,reject) {
this.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
this.fireLeMissles().then(function(anotherResult){
promiseList.push(anotherResult)
})
},this);
Promise.all(promiseList).then(function(promiseItem){
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
},this);
});
resolve(result);
}.bind(this).catch(function(err){
console.log("Yea, life sucks sometimes.")
}));
}.bind(this));
}
Killmyself.prototype.another = function(){
//Other stuff
}
You can see because of calls to functions in the same prototype such as this.anotherFunction(... and this.fireLeMissles(... I had to do deep preservation of context,which now (in my much larger version of this) is making this code hard to work with.
Question:
Is this a "man up and get use to the harder aspects of JavaScript" thing - or do you seasoned developers see simple ways that deep binding like this could have been avoided?
If you are using ES6, you can benefit from arrow functions, which preserve the context.
var counter = function () {
this.count = 0;
setInterval( () => { // arrow function
console.log(this.count++); // context is preserved
}, 1000)
}
var counter = new counter();
So, your code would become something like:
Killmyself.prototype.fireLeMissles = function() {
return new Promise((resolve, reject) => {
this.anotherFunction(param).then(result => {
someList.forEach(item => {
this.fireLeMissles().then(anotherResult => {
promiseList.push(anotherResult)
});
});
Promise.all(promiseList).then(promiseItem => {
childPlacesIds.forEach(childPlaceId => {
//Do Other Stuff
});
});
resolve(result);
}).catch(err => {
console.log("Yea, life sucks sometimes.")
});
});
}
For ES5, you can just either use .bind just like the way you did or you can assign this to something else in the function with the desired context, then use that variable inside the inner functions.
Killmyself.prototype.fireLeMissles = function() {
var self = this; /// use `self` instead of `this` from now on.
return new Promise(function(resolve, reject) {
self.anotherFunction(param).then(function(result) {
someList.forEach(function(item) {
self.fireLeMissles().then(function(anotherResult) {
promiseList.push(anotherResult)
})
});
Promise.all(promiseList).then(function(promiseItem) {
childPlacesIds.forEach(function(childPlaceId) {
//Do Other Stuff
});
});
resolve(result);
}).catch(function(err) {
console.log("Yea, life sucks sometimes.")
});
});
}
for starters I do not understand you you need a new Promise.. here, like #loganfsmyth said, I would simply use arrow functions and reduce the complexity:
Killmyself.prototype.fireLeMissles = function (param) {
return this.anotherFunction(param)
.then(someList => {
var promiseList = someList.map( item => this.fireLeMissles(item));
return Promise.all(promiseList);
}).then(childPlacesIds => {
childPlacesIds.forEach(childPlacesId = {
// .... do something;
});
// return something.
}).catch(err => console.log("Yea, life sucks sometimes."));
}
P. S: I am not sure where this param, someList, childPlacesIds is coming from, and assumed that you are initializing that promiseList as empty array.
Mido's answer is good, I just wanted to provide an alternative take on it which I think would be useful to know - using promises for the proxies they are:
Killmyself.prototype.fireLeMissles = function () {
let fn = this.anotherFunction(param);
let others = fn.then(_ => someList.map(this.fireLeMissles, this));
let othersP = Promise.all(others);
othersP.then(/* do OtherStuff */);
return othersP; // or whatever its then returned
}
Of course, this gets even easier with a library like bluebird.

How can I test for equality to a bound function when unit testing?

I want to test that an argument passed to a function is a function reference but the function reference is being passed using bind().
Consider this code which is to be tested (shortened for brevity):
initialize: function () {
this.register(this.handler.bind(this));
}
And this unit test to check if register() was called with handler():
it('register handler', function () {
spyOn(bar, 'register');
bar.initialize();
expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);
});
The arg doesn't equal the function reference I guess due to the bound function using bind() - how can I test that the correct function reference is being passed while still using the bind() method on it?
Note: This isn't specific to jasmine, I just thought it was appropriate because of the methods being used.
Instead of
expect(bar.register.calls.argsFor(0)[0]).toEqual(bar.handler);
you can do
expect(Object.create(bar.handler.prototype) instanceof bar.register.calls.argsFor(0)[0])
.toBe(true);
or
expect(Object.create(bar.handler.prototype)).
toEqual(jasmine.any(bar.register.calls.argsFor(0)[0]));
This works because the internal [[HasInstance]] method of the bound function delegates to the [[HasInstance]] method of the original function.
This blog post has a more detailed analysis of bound functions.
this.handler.bind(this) creates completely a new function, therefore it is not equal to bar.handler.
See Function.prototype.bind().
You can pass bounded function as argument to your initialize function and then test it, e.g.:
var handler = bar.handler.bind(bar);
bar.initialize(handler);
expect(bar.register.calls.argsFor(0)[0]).toEqual(handler);
I've managed to keep the test and code and work around it.
I spy on the function reference with an empty anon func, then call it when spying on the register method - if the spy gets called, I know it's passed the correct reference.
it('register handler', function () {
spyOn(bar, 'handler').and.callFake(function(){}); // do nothing
spyOn(bar, 'register').and.callFake(function(fn){
fn();
expect(bar.handler).toHaveBeenCalled();
});
bar.initialize();
});
I thought I'd add another approach that, to me, is a bit less awkward.
given a class like:
class Bar {
public initialize() {
this.register(this.handler.bind(this));
}
private register(callback) {}
private handler() {}
}
the full spec might look like:
describe('Bar', () => {
let bar;
beforeEach(() => {
bar = new Bar();
});
describe('initialize', () => {
let handlerContext;
beforeEach(() => {
bar.handler = function() {
handlerContext = this;
};
bar.register = jest.fn(callback => {
callback();
});
bar.initialize();
});
it('calls register with the handler', () => {
expect(bar.register).toHaveBeenCalledWith(expect.any(Function));
});
it('handler is context bound', () => {
expect(handlerContext).toEqual(bar);
});
});
});
In my case (using jest) I just mocked the implementation of bind for the function I wanted and I tweaked it so that it returns the original function and not a bound copy of it.
Specifically here's what I tried and worked:
Code to be tested:
// module test.js
export const funcsToExecute = [];
function foo(func) {
funcsToExecute.push(func);
}
export function bar(someArg) {
// bar body
}
export function run(someArg) {
foo(bar.bind(null, someArg));
}
I wanted to assert that when run is called, funcsToExecute contains bar
So I wrote the test like this:
import * as test from 'test';
it('should check that "funcsToExecute" contain only "bar"', () => {
jest.spyOn(test.bar, 'bind').mockImplementation((thisVal, ...args) => test.bar);
test.run(5);
expect(test.funcsToExecute.length).toBe(1);
expect(test.funcsToExecute[0]).toBe(test.bar);
});
For your example, I suppose it would be something like this:
it('register handler', function () {
spyOn(bar, 'register');
spyOn(bar.handler, 'bind').mockImplementation((thisVal, ...args) => bar.handler);
bar.initialize();
expect(bar.register.calls.argsFor(0)[0]).toBe(bar.handler);
});
though I haven't tested it.

Categories

Resources