Assigning a name to an anonymous function (.name vs .displayName) - javascript

I am running a react native project, where this function
const f = function() {};
has an undefined name, not inferred.
Keeping the anonymous definition, is this code:
f.name = "f";
console.log(f.name); // f
an anti-pattern? Should I use .displayName instead?
Update (real code, the described one is a simpler version):
const MyComponent = forwardRef(({ userId }, ref) => {
useTrackScreenView(MyComponent.name, {
user_id: userId,
});
return JSX...
});
console.log(MyComponent.name) // undefined
//
// MyComponent.name = "MyComponent"; anti-pattern?
// or
// MyComponent.displayName = "MyComponent";
//

Using the #ts-check directive indicates that the Function's .name property is read-only.
So, the way to go is using .displayName.

Related

Service Layer Unit testing with Sinon.js - Error "x" is not a constructor

I have followed the medium guide :
https://medium.com/#tehvicke/integration-and-unit-testing-with-jest-in-nodejs-and-mongoose-bd41c61c9fbc trying to develop a test suite. My code is exactly like his but I am having the TypeError: Tournament is not a constructor
I put some code, so you can see what I am trying to do.
TournamentService.js
const createTournament = (Tournament) => (tournamentObj) => {
const {name, creator} = tournamentObj;
const newTournament = new Tournament({name, creator});
return newTournament.save();
};
TournamentService.test.js
const TournamentService = require("../TournamentService");
const sinon = require("sinon");
describe("create Tournament test", () => {
it("creates a tournament", () => {
const save = sinon.spy();
console.log("save ", save);
let name;
let creator;
const MockTournamentModel = (tournamentObject) => {
name = tournamentObject.name;
creator = tournamentObject.creator;
return {
...tournamentObject,
save,
};
};
const tournamentService = TournamentService(MockTournamentModel);
const fintoElemento = {
name: "Test tournament",
creator: "jest",
};
tournamentService.createTournament(fintoElemento);
const expected = true;
const actual = save.calledOnce;
expect(actual).toEqual(expected);
expect(name).toEqual("Test tournament");
});
});
I've found the error, the problem was that I was trying to create the MockTournamentModel with an arrow function, instead you should use a classic function (or some package that re-compile into a classic function)
the keyword new does a few things:
It creates a new object. The type of this object is simply object.
- It sets this new object's internal, inaccessible, [[prototype]] (i.e.
__proto__) property to be the constructor function's external, accessible, prototype object (every function object automatically has
a prototype property).
It makes the this variable point to the newly created object.
It executes the constructor function, using the newly created object
whenever this is mentioned.
It returns the newly created object, unless the constructor function
returns a non-null object reference. In this case, that object
reference is returned instead.
The arrow function do not have this, arguments or other special names bound at all.
That's why it did not work with an arrow function.. hope this will help someone else avoid my mistake!
https://zeekat.nl/articles/constructors-considered-mildly-confusing.html
Arrow Functions and This

Calling a method that was assigned to a variable in a pre update hook

So I'm trying to create a helper function for all my pre update hooks:
const preUpdateHelper = function(updateQuery, updateMethod) {
const update = updateQuery.getUpdate();
if (update && update.$set && update.$set.emailAddress) {
const emailAddress = update.$set.emailAddress;
updateMethod({}, {$set: {emailAddress: emailAddress.trim()}});
}
updateMethod({},{ $set: { updatedAt: new Date() } });
}
UserSchema.pre('findOneAndUpdate', function() {
const updateMethod = this.findOneAndUpdate;
var x = function() {
console.log('hi');
};
console.log(x)
x();
console.log(updateMethod);
updateMethod({},{$set: {updatedAt: new Date()}});
console.log('after update method ')
preUpdateHelper(this, this.findOneAndUpdate);
});
The logs print:
[Function: x]
hi
[Function]
So, after update method is not printed. I'm getting the error: Cannot set property 'op' of undefined.
I can't figure out why I can't call this method after assigning it to a variable. Any help would be greatly appreciated!
Most likely, this context.
const updateMethod = this.findOneAndUpdate;
//...
updateMethod(args);
isn't the same as
this.findOneAndUpdate(args);
Try binding the method:
const updateMethod = this.findOneAndUpdate.bind(this);
Here's the minimal example demonstrating how those things are different:
const foo = {
myName: "foo",
introduceYourself: function() {
console.log("Hello, my name is " + this.myName);
}
}
foo.introduceYourself(); // logs what everyone expects
const introduceYourself = foo.introduceYourself;
introduceYourself(); // logs undefined
const introduceFoo = introduceYourself.bind(foo);
introduceFoo(); // logs foo again
introduceYourself isn't exactly what in OOP is called a method. It's not really tied to foo. Only by invoking it as an object property: foo.introduceYourself (or equivalent: foo['introduceYourself']), you're passing it foo as its this context. Otherwise,introduceYourself` doesn't get it.
Other ways to pass this context to a function are bind (shown here), call and apply.
Further reading - Kyle Simpson's "You Don't Know JS: this & Object Prototypes".

Javascript object options: function or null

This question has probably been asked before, but I don't really know the proper keywords to find a solution on Google, so all my researches returned 0 :) Or better, all my researches ended up with "optional parameters" but for functions.
Let's assume I have this class:
var Class = function (params) {
// I get the data somehow
params.name(data);
}
The problem is that not every time I instantiate new Class() I need also to set name() like:
new Class({
name: function (data) {
// Do something with the data
}
... other params ...
})
But, with the above syntax, if I don't declare name parameter as a function, an error params.name is not a function is thrown:
// Error
new Class({
... other params ...
})
My question is: is there a way to set params.name() as an optional parameter (in the object) that can be a function or completely not exist (e.g. null)? Something like saying: "when you instantiate the class:
if you declare name: function (data) {} => then: params.name(data) is a function
if you skip name => it's anyway ok (no error is thrown)"
In ES6, You can use default function parameter option, like
var Class = function(params = {
name: (x) => x
}) {
// I get the data somehow
data='value';
params.name(data);
}
Class();
Class({name: x => console.log(x)});
Hope this will help!
EDIT
Yes #brigo, you are right, sorry I overlooked that point. We are actually looking for nested default rather. A possible solution could be to destructure the object inside the function like
var Class = function(params = {}) {
// I get the data somehow
data = 'value';
withDefaultParams = {
name: (x) => x,
...params
}
withDefaultParams.name(data);
}
Class();
Class({
name: x => console.log('non-default')
});
Class({
others: x => console.log('non-default')
});
yes, you could do something like
if (typeof param.name === 'function') {
param.name(data);
} else {
// dont do anything or do something different
}
If using jQuery is an option, you could use its extend method (API docs) like in the following example:
var Class = function(params) {
var _defaults = {
name: function(){},
some: 'other stuff'
};
params = jQuery.extend(_defaults,params);
// I get the data somehow
data='value';
params.name(data);
}
Class();
Class({foo:'bar'});
Class({name: console.log });

Create helper function to run a function in an isolated scope

This code works:
it.cb(h => {
console.log(h);
h.ctn();
});
it.cb(new Function(
'h', [
'console.log(h)',
'h.ctn()'
]
.join(';')
));
these two test cases are basically identical. But constructing a string with array like that is cumbersome, and you can't get static analysis. So what I was thinking of doing was something like this:
it.cb(isolated(h => {
console.log(h);
h.ctn();
}));
where isolated is a helper function that looks something like:
const isolated = function(fn){
const str = fn.toString();
const paramNames = getParamNames(str);
return new Function(...paramNames.concat(str));
};
the biggest problem is that Function.prototype.toString() gives you the whole function. Does anyone know of a good way to just get the function body from the string representation of the function?
Update: PRoberts was asking what the purpose of this is, the purpose is simply:
const foo = 3;
it.cb(isolated(h => {
console.log(foo); // this will throw "ReferenceError: foo is not defined"
h.ctn();
}));
I wrote a version of isolated() that handles any non-binded user-defined function expression and throws custom errors for scoped accesses:
function isolated (fn) {
return new Function(`
with (new Proxy({}, {
has () { return true; },
get (target, property) {
if (typeof property !== 'string') return target[property];
throw new ReferenceError(property + ' accessed from isolated scope');
},
set (target, property) {
throw new ReferenceError(property + ' accessed from isolated scope');
}
})) return ${Function.prototype.toString.call(fn)}
`).call(new Proxy(function () {}, new Proxy({}, {
get() { throw new ReferenceError('this accessed from isolated scope'); }
})));
}
// test functions
[
() => arguments, // fail
() => this, // pass, no way to intercept this
() => this.foo, // fail
() => this.foo = 'bar', // fail
() => this(), // fail
() => new this, // fail
h => h, // pass
h => i, // fail
(a, b) => b > a ? b : a, // pass
].forEach(fn => {
const isolate = isolated(fn);
console.log(isolate.toString());
try {
isolate();
console.log('passed');
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
})
This implementation is somewhat simpler, and therefore much less error-prone than attempting to parse the parameters and body of a user-defined function.
The with statement is a relatively simplistic means of catching any scoped references within the forcibly isolated function and throwing a ReferenceError. It does so by inserting a Proxy intermediate into the scope with a get trap that intercepts the scoped variable name that was accessed.
The Proxy that is passed as the context of the function was the only part that was a bit tricky to implement, and also incomplete. It was necessary because the Proxy provided as the scope to the with statement does not intercept accesses to the this keyword, so the context must also be wrapped explicitly in order to intercept and throw on any indirect usage of this inside an isolated arrow function.
I would simply use indexOf('{') and lastIndexOf('}').
const yourFunction = h => {
console.log(h);
h.ctn();
};
const fnText = yourFunction.toString();
const body = fnText.substring(fnText.indexOf('{') + 1, fnText.lastIndexOf('}'));
console.log(body);
Knowing that this will not cover arrow functions without a body:
const fn = k => k + 1
Alright this works, that wasn't too hard.
We just assume the first and last parens are the outline of function body.
const isolated = function(fn){
const str = fn.toString();
const first = str.indexOf('{') + 1;
const last = str.lastIndexOf('}');
const body = str.substr(first, last-first);
const paramNames = ['h'];
return new Function(...paramNames.concat(body));
};
above we assume the only argument is called "h", but you will need to find function arguments parser. I have used require('function-arguments') in the past.

Javascript function (type) to store & use data

I really never used a javascript function type or class before, I understand Java and Python, but not javascript. So, I build a class like this:
function FormStore (type) {
this.setup = () =>{
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
};
var FS = new FormStore();
FS.setup();
The store is filled by components on document.ready. There is a function that looks up if the aligned components (glyph, label, input) have some classes or values and for the specific component fills a dict: {label:false,glyph:false, input:false}. However, for some reason it doesn't matter. Even if I enter some values in to the store right away (in setup) or create them on the fly, in checkVal the store doesn't exist, it's undefined.
Please, anybody, what am I not understanding about javascript type and classes here? I am googling this a lot and trying to find good resources but, "javascipt variable class" (or type) just yields a lot of DOM manipulation.
edit
There is a context problem in checkVal, you are using a non-arrow (and not explicitly bound) callback function and trying to access this inside of it. Change that to an arrow function as well, and the parent context (this) will be preserved:
$.each( geoArr, (val) => {
id = geoArr[val];
console.log(this.store)
if (!(this.store[id])) {
return false;
}
});
And while you are at changing that section, it's not going to work. You will not get access to $.each's return value. You should rely on native array APIs for this task and use Array.every to determine if all geoArr items are in the store (assuming that's your goal):
// returns false if not all geoArr items are in the store
geoArr.every(id => this.store[id])
original
I don't see you calling checkVal() anywhere, but based on the error you are getting it is called prior to setup() (since setup initializes the store). You could solve that problem straight away by moving this.store = {} out of setup (right at the top), e.g.:
function FormStore(type) {
this.store = {};
...
Having said that, I would suggest either defining your methods on the prototype, or utilizing ES6 classes. Here is a simplified version of both:
ES5 class
function FormStore(type) {
// make sure user didn't forget new keyword
if (this === window) {
throw new Error('FormStore must be called with "new" keyword')
}
// initialize state, this is the constructor
this.type = type;
this.store = {};
// any other state the class manages
}
FormStore.prototype = {
setup: function() {
// do setup stuff
// "this" points to instance
console.log('setup', this.type)
},
checkVal: function() {
}
}
var formStore = new FormStore('foo')
console.log(formStore.store) // <-- not undefined
formStore.setup()
ES6 Class
class FormStore {
constructor(type) {
this.type = type;
this.store = {};
}
setup() {
console.log('setup', this.type)
}
checkVal() {
}
}
const formStore = new FormStore('bar')
console.log(formStore.store) // <-- not undefined
formStore.setup()
It has to do with scoping. Your $.each in checkVal has a normal function. Inside the function the scope if this is different. If you want to keep the original scope you could use a fat arrow function like you do when defining the methods.
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, val => {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
When you run your original code and place a breakpoint on the line with console.log you can see in the inspector that this is set to the Window object and no longer points to your FormStore.
function FormStore () {
this.setup = function(){
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= function(){
var geoArr = ['id_xx','myID'];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
};
var FS = new FormStore();
FS.setup();
Works absolutely fine, the code you provided had a missing bracket and you were using some broken es6 syntax

Categories

Resources