External function calling for parent's "this" scope - scope

I have two .js files: root.js and external.js
root.js
import myExternalFunction from 'external.js'
class Parent {
constructor(){}
parentFunction = () => {
console.log('I was called from an external function using "this."')
}
}
external.js
export default myExternalFunction = () => {
this.parentFunction()
}
Currently I receive an error about it not being a function, or no access to 'this'.
How do I import external functions that want to use the 'this' scope from which they are being called?

How do I import external functions that want to use the 'this' scope from which they are being called?
It doesn't have anything to do with how you export/import the function.
There are two things you need to consider:
functions that want to receive their this value dynamically must not be arrow functions, so use
export default function myExternalFunction() {
this.parentFunction()
}
as usual, the function must be invoked in the right way to get the expected this value. There's no magic that passes the current this value in the scope of the call to the called function. You'll have to do something like
import myExternalFunction from 'external.js'
class Parent {
constructor(){
this.method = myExternalFunction;
this.parentFunction = () => {
console.log('I was called from an external function using "this."')
}
}
}
const example = new Parent;
example.method() // an invocation as a method
myExternalFunction.call(example); // explicit using `call`

Related

Jest mocking private members

In a Node/Express server, we use a repository that needs to be unit-tested using Jest.
//Private things
let products;
function loadProducts() {
if (!products)
products = fetchProductsFromSomeDbOrServiceOrWhatever()
}
function saveProducts() {
persistPrivateProductsToADbOrServiceOrWhatever()
}
// Exported/public things
export function read() {
loadProducts();
return products;
}
export function add(product) {
loadProducts();
products.push(product);
saveProducts();
}
We want to unit test like this:
import { read, add } from './productRepo';
it('can read products', () => {
expect(read().length).toBe(5);
});
it('can add a product', () => {
const oldNum = read().length;
add({id:0, name:'test prod', moreProps});
expect(read().length).toBe(oldNum+1)
});
You get the idea. It's not a class so we can't mess with the prototype.
Problem: How do I mock the private products and/or loadProducts and/or saveProducts so that it isn't reading from the actual data source?
Presumably these private functions call out to other pieces of functionality you've written yourself or imported from libraries.
function loadProducts() {
if (!products)
products = fetchProductsFromSomeDbOrServiceOrWhatever()
}
function saveProducts() {
persistPrivateProductsToADbOrServiceOrWhatever()
}
Let's take fetchProductsFromSomeDbOrServiceOrWhatever as the example. One basic architectural consideration to make the code properly encapsulated and testable is to put this functionality in a separate module. So I would expect an import at the head of the file:
import fetchProductsFromSomeDbOrServiceOrWhatever from './fetchProductsFromSomeDbOrServiceOrWhatever'
So in this case just mock it in your test file:
jest.mock('./fetchProductsFromSomeDbOrServiceOrWhatever');
If the functionality is not extracted into a separate module this makes your code less testable; this on its own is a good reason to refactor.
Note: the other replies on this thread are correct when they say that private functions of classes should not be tested, but I think that is a slightly different issue from the one you are asking.
First, start initializing products to an empty array, else tests are doomed to fail because of the null value. Also change the null check
Then parametrize your loader and saver functions so your functions can be testable. Last write tests for you loader and saver functions outside of this repo function.
// assummed imports
fetchProductsFromSomeDbOrServiceOrWhatever=()=>{}
persistPrivateProductsToADbOrServiceOrWhatever=()=>{}
//Private things
let products=[];
function loadProducts(loader) {
loader=loader || fetchProductsFromSomeDbOrServiceOrWhatever
if (products.length==0)
products = loader()
}
function saveProducts(saver) {
saver=saver || persistPrivateProductsToADbOrServiceOrWhatever
saver()
}
// Exported/public things
export function read(loader) {
loadProducts(loader);
return products;
}
export function add(product,loader,saver) {
loadProducts(loader);
products.push(product);
saveProducts(saver);
}
both exported functions can now use fetch/persist functions either by importing or as arguments.
Now the remaining is the mocking loader and saver function. saver function does not change anything so it can be null or empty. but if you want to check if it is called inside, then you need to mock it.
import {jest} from '#jest/globals'
import { read, add } from './productRepo';
it('can read products', () => {
loader=jest.fn().mockReturnValue([{id:7},{id:42}])
expect(read(loader).length).toBe(2);
expect(loader).toBeCalledTimes(1)
});
it('can add a product', () => {
loader=jest.fn().mockReturnValue([{id:7},{id:42}])
saver=jest.fn()
const oldNum = read(loader).length;
add({id:0, name:'test prod'},loader,saver);
expect(read(loader).length).toBe(oldNum+1)
expect(loader).toBeCalledTimes(0)
expect(saver).toBeCalledTimes(1)
});
There is a "gotcha" here. Since productRepo is imported once, loader is called in the first test but will not be called again in the second test since the first has already changed the products. Thus subsequent tests must take this into account when using non-class packages.
you must not get access to private properties or methodes anyway.
instead you can provide setter and getter for your properties.
for methodes I believe you can break it into some private parts and some public parts. private parts for your actual data source and public parts that can be used in test either.
I suggest implementing an initialize method on productRepo.js.
export function init(data) {
products = data
}
Then, you can init products with mocked data.
Also, if you can't change the file, you could use the rewire library, which lets you access non-exported functions and variables.

Executing a function knowing only its name in node

Let's assume I want to execute a function in node but I have only its name (a string).
If the function is defined in a package (which has been imported) I can do something like this
require("lodash")["add"](1, 2);
If the function is defined in another file and exported, I can do something like this
a-function.js
export function aFunction() {
return console.log("I am a function");
}
anotherJsFile.js
require("./a-function")["aFunction"]();
If the function thuogh is defined in the same file where I have the code that wants to call it, I have found only this way to make it work
export function anotherFunction() {
return console.log("I am another function");
}
// the null check guard is added to avoid an "object may be null" Typescript error
(this || {})["anotherFunction"]();
Is there a better solution to call a function by name when the function is defined in the same file?
You can stash your functions in an object that permits finding them by name.
const call = { foo }
function foo() {
console.log('foo was called.');
}
call['foo']()

How to call exported function inside the same file

I have faced a problem regarding calling an exported function inside the same file.
When I call it then the error shows the following.
UnhandledPromiseRejectionWarning: ReferenceError: findOrCreateMedia is not defined
where findOrCreateMedia is my function. How can I fix this?
Try this:
function functionName() { ... };
exports.functionName = functionName;
functionName();
This is happening because you are losing your this object reference inside your calling function.
For eg:
module.exports.a = function () {
return true
}
module.exports.b = function() {
return this.a();
}
here you will get the issue because when you call this.a(), it is referencing the this object of the b function.
To solve this your have to store your this object reference somewhere or use the arrow function, because the arrow function doesn't have there own this object so it will always reference the outer this object
To solve this, modify your function like this
module.exports.a = function () {
return true
}
module.exports.b = () => {
return this.a();
}
you can also ES6 import/export function:-
const someFunction = (){
...
}
export default someFuntion ( in the case on single function)
When you want to export multiple functions
export { someFunction1, someFunction2}.
Now the place where you want to import
import somFunction from 'filelocation' ( note in case of default exports you need to use the same name of the function)
In the case of multiple functions.You can change the function name but keep in mind the order of exports and imports.
import { myFunction1,myFunction2} from 'fileLocation'

Getting around async issue in node.js application command line

My application is a simple mysql client used from command line - it connects to database and makes few queries to get information from database. Mysql functionality is encapsulated in a class and problem is since calls to mysql server is async (understandably) - the code flow reaches end of application.
And I am unable to refer to 'this'(Mysql) inside a method of Mysql class.
How do I get around this problem ?
Below is my code.
//CLASS
function Mysql(config) {
//...
}
//METHOD
Mysql.prototype.getDbInfo = function (cbk) {
this.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(this.configVar);
});
}
module.exports = Mysql;
//CLASS OBJECT
var test = new Mysql(config);
//METHOD INVOKE
test.getDbInfo(function (err,results) {
//...
});
Every time that you jump into a callback function you are loosing the scope of the this object. There are different ways to work around it.
Assign this to another variable
The first solution is to assign the this object to another variable (e.g.: that, self). When you assign one variable to another and the first variable is an object then you keep the reference to the original object and you can use it within the callback. Something like that:
Mysql.prototype.getDbInfo = function (cbk) {
var self = this;
self.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(self.configVar);
});
}
Bind the this object to the function
You can bind the this object to the function and like that you set the this keyword set to the provided value (in your case the scope outside of showTables function). You can read the documentation of this and you will be able to understand more:
Mysql.prototype.getDbInfo = function (cbk) {
this.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(self.configVar);
}.bind(this));
}
Use es6 arrow functions
It is more or less the same solution like the first one. If you use a transpiler you will find out that it is translated like the first solution:
Mysql.prototype.getDbInfo = function (cbk) {
self.showTables((e,r) => {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(this.configVar);
});
}

user module - node js

i have a question regarding defining objects in modules.
lets say i have a module:
/*---obj----*/
function A (param){
this.parm=param;
function func(){
//do somthing
}
}
exports.func=func;
/*---file.js----*/
obj=require('obj');
function fileFunc(A){
A.func();//with out this line it works fine
A.param=2;
}
}
for some reason it does not recognize the function in the object A. it recognizes the object A and its different vars, but when it comes to executing the function it gives the msg:
TypeError: Object # has no method 'func'
i tried also to export the function in A by:
exports.A.func=A.func
or
exports.func=func
neither works..
does someone have a clue?
thanx
matti
The function you defined inside of A is local only to that function. What you want is
function A(param) {
this.param = param;
}
A.func = function() {
// do something
};
But if you are treating A as a constructor then you'll want to put that function in A's prototype
A.prototype.func = function() {
// do something
};

Resources