Using classes in for loop in node.js - javascript

To be honest, I don't know what to ask for my case so I'll just add some details. I come from java so I am familiar with classes.
Let's take for instance a class and for making it easy, drop the private fields to public and no getter and setter
class User{
public String name;
public String email;
}
This is useful in certain cases, for instance have an array of users and then I can use something like:
for(User user: usersList){
user.name='some new value'
//since user is a class, code assist, suggests the name and also if I have user.namee it throws an error
}
Now moving into javascript, I can obtain an array and do something like
for (let user of usersList){
user.name='some new value'
//however if I type user.nammee no error is thrown and code assist does not know what to recomment
}
I think that now you may get the idea. I want it to make it easier to properly obtain the object fields with code assist and also avoid typing errors because of property name badly typed. Javascript classes from what I see have only methods... so what can I apply in this case?

check out Typescript
and this "fiddle" on the TypeScript Playground
class User {
constructor(public name: string, public email: string = "") { }
/*
//shorthand for
public name: string;
public email: string;
constructor(name:string, email:string="") {
this.name = name;
this.email = email;
}
*/
}
var usersList = [
new User("Jack"),
new User("Jill"),
new User("Joe")
];
for (let user of usersList) {
user.email = user.name + "#mail.com";
}
and since userList is recognized as type User[], let user is also typed as let user:User

JavaScript allows to reference properties which an object does not have: you can add them ad-hoc, and if you read non-existing properties, you just get undefined. This is how JavaScript works.
If you want to get an error when you try to assign to a non-existing property, then consider using the Object.seal method, which you could apply in the constructor for your class:
"use strict";
class User{
constructor (name, email) {
this.name = name;
this.email = email;
// Seal will disallow new properties
Object.seal(this);
}
}
const user = new User();
user.name = "Jeff";
console.log(user); // Shows property with "Jeff"
user.nammmeeee = "Jeff"; // Produces error
Make sure to use strict mode ("use strict") as otherwise these errors are suppressed.

You can do this in javascript. Just that, your userslist will be array in which you will have your users. Loop will be for loop from 0 to length-1. This class will have some syntax like this.
var userclass = {
Name:'xyz',
Addr:'address',
.....other props
}

Related

How to access a private field in a class

class Movie {
constructor(movieName, category ) {
this._movieName = movieName;
this._category = category;
}
showMovieName() {
return `${movieName}`;
}
}
const movie1 = new Movie("Avengers", "superheroes");
console.log(movie1.showMovieName());
I have a Movie class and two fields with underscores. I need to create a method that returns the title of the movie. How can i do this? Now in the console the error movieName is not defined
None of your fields are private. Just prefix movieName with underscore.
showMovieName() {
return `${this._movieName}`;
}
In your code you need to change return `${movieName}`; to return `${this._movieName}`;
More info
Your example uses the public field declaration syntax of classes to declare two public instance properties named _movieName and _category.
Using underscores as property name prefixes is an old-fashioned (although still common) way to indicate that a property is intended to be private (although this approach offers no enforcement whatsoever).
JavaScript now has private class fields. These can be declared using the # prefix ("hash name syntax") and cannot be accessed in any way by anything outside of the class body itself.
Private fields are added before the constructor runs and are not inherited.
class Movie {
#movieName;
constructor(movieName) {
this.#movieName = movieName;
}
showMovieName() {
return `${this.#movieName}`;
}
}
const movie1 = new Movie('Avengers');
console.log(movie1.showMovieName());
There are no real private properties in JS at the moment, however, you are trying to access a property so must use this., please see below
class Movie {
constructor(movieName, category ) {
this._movieName = movieName;
this._category = category;
}
showMovieName() {
return `${this._movieName}`;
}
}
const movie1 = new Movie("Avengers", "superheroes");
console.log(movie1.showMovieName());

Better Method than using setter in typescript : private variables are declared but value not read issue

I have a scenario like this:
class Employee {
private fullName: string;
private firstName: string;
private lastName: string;
private age: number;
private isValid: boolean;
constructor() {
this.fullName = null;
this.firstName = null;
this.lastName = null;
this.age = null;
this.isValid = false;
}
setfullName(fullName: string) {
this.fullName = fullName;
}
setfirstName(firstName: string) {
this.firstName = firstName;
}
setlastName(lastName: string) {
this.lastName = lastName;
}
setAge(age: number) {
this.age = age;
}
setValid(isValid: boolean) {
this.isValid = isValid;
}
}
// test.ts file
let employee = new Employee();
// set in different positions in test.ts file based on getting the input paramters
employee.setfullName("james cooper");
employee.setfirstName("mary");
employee.setlastName("fransis"); // getting lastName from another api call
employee.setAge(50); // getting 50 from another api call
employee.setValid(true);
Here i am getting a warning in vscode like "private variables are declared but its value is not read".
Inorder to prevent this warning, i have to use getter method, but here purpose is to save the object properties and not reading. So getter method seems to be useless.
Since i am new to typescript, without setting these variables to public or disabling in tslint configuration, can anybody suggest a better approach for the same?
The purpose is to set the employee info, for that I created Employee model.
Any help would be really appreciated.
Thanks in advance
Since you're not doing anything with the data on this side other than assigning to properties of it, it sounds like you should be creating a plain object instead. Since in your original code, all the methods which set properties are public, and don't do anything else, they don't accomplish anything useful. If an external source can call a setter method, it may as well just assign a property directly. The class adds unnecessary and confusing overhead, and part of that is why Typescript is complaining. Instead, do something like:
type Employee = Partial<{
fullName: string;
firstName: string;
lastName: string;
age: number;
isValid: boolean;
}>;
const employee: Employee = {};
employee.age = 15;
employee.isValid = false;
// send employee to front-end
IMO, a class is generally useful only when you need to have data associated with an instance and methods which retrieve and use the data in some way.

Define custom generic setter for JavaScript Class

I wonder if it's possible to define a custom setter() function for a javascript class.
I know this is possible thanks to a Proxy:
class Person {
name;
surname;
constructor({name, surname}) {
this.name = name;
this.surname = surname;
}
}
const customSetter = {
set: function(obj: any, prop: any, value: any) {
obj[prop] = value;
// Performing custom operations.
return true;
}
}
const ProxiedClass = <T, V>(obj: T, classGenerator: { new(obj: T): V}) : any => new Proxy(new classGenerator(obj), customSetter);
var foo = ProxiedClass({name: 'pippo', surname: 'pluto'}, Person);
console.log(foo); // → Object { name: "pippo", surname: "pluto" }
Though, there are few things that I don't like about this solution:
Since I would have lots of classes, I would like to have a generic Proxy like the one I implemented, but then the most important keyword, the one that define which type we will have, it's just an argument (Person is passed as argument).
Assume I even create a single Proxy for each class (like ProxiedPerson).. Another thing I don't like is that I cannot call it with new: if another developers read the line, he doesn't read right away the fact that I'm creating a new instance because we are missing the new keyword.
Once the object is shown in console, we also lose the name of the class: you see that it's shown Object instead of Person.
I know these are not big issues, but i'm trying to write "user-friendly" code, and to me this solution it seems not user-friendly at all.
I also know it's possible to define a setter and getter function for each property, but I'd like to avoid that.
EDIT: After #Bergi comments, I tried to do something like he suggests.
Some more info on the big picture I'm trying to achieve: I want to implement some sort of validation, like we were in C#: I have classes with attributes, and I managed to retrieve the attributes/functions when a class property change.
Though, I need to run the functions in the setter() method, that's why I need to intercept the setter().
So, I thought of recreating the ValidatingObject class: if a class extends ValidatingObject, then the setter() properties method are intercept are handled.
I managed to do something:
class ValidatingObject {
public hasValidationError : boolean;
public validationErorrs : any;
constructor() {
this.hasValidationError = false;
this.validationErorrs = {};
let properties = Object.getOwnPropertyNames(this);
properties.forEach(p => {
Object.defineProperty(this, "_" + p, {value: this[p], writable: true});
Object.defineProperty(this, p,
{
get: function() { return this["_" + p]},
set: (v: any) => { this["_" + p] = v; console.log("Perfom custom OP"); }
}
);
});
}
}
class Person extends ValidatingObject {
public name;
public surname;
constructor({name, surname}) {
super();
this.name = name;
this.surname = surname;
}
}
var foo = new Person({name: 'pippo', surname: 'pluto'});
foo.hasValidationError = true; // → 'Perform custom OP' shown in console.
foo.name = "paperino"; // → Nothing shown in console.
But now, I've another problem. In the constructor of ValidatingObject, when I run Object.getOwnPropertyNames(this), I got only the ValidatingObject properties, and not the ones in Person, thus I haven't intercepted the setter() method I wanted the most.
So, another question now could be: is there any way I can get Person properties from ValidatingObject?

Why do developers use "_" while using get and set in JavaScript? [duplicate]

This question already has answers here:
JavaScript classes with getter and setter cause RangeError: Maximum call stack size exceeded
(5 answers)
Closed 3 years ago.
I know that using an underscore is just a convention to define private variables in JavaScript. But I came across a use case [while using a class] where use of _ seems mandatory to make the code work! My question is how _ is used under the hood by get and set.
The below code throws an error:
RangeError: Maximum call stack size exceeded
class User {
constructor(name) {
this.name = name;
}
get name() {
return this.name;
}
set name(val) {
this.name = val;
}
}
let user = new User("Jhon");
console.log(user.name);
Now, if I use _ the code works!
class User {
constructor(name) {
this.name = name;
}
get name() {
return this._name; // Added "_" here
}
set name(val) {
this._name = val; // Added "_" here
}
}
let user = new User("Jhon");
console.log(user.name);
Your first snippet uses the same name for the getter/setter as the property you try to assign to. So, in the constructor, when you do
this.name = name;
you are invoking the name setter, which does:
this.name = val;
again invoking the name setter, which recursively calls itself until the stack overflows.
Using a different variable name for the actual property the data is stored in (compared to the getters/setters) allows for the code to work as intended. It doesn't have to be prefixed with an underscore - pretty much anything other than the same name used by the getters/setters will work.
class User {
constructor(name) {
this.name = name;
}
get name() {
return this.actualProperty;
}
set name(val) {
this.actualProperty = val;
}
}
let user = new User("Jhon");
console.log(user.name);
The _ before a property name is generally meant to indicate that the property is meant to be private, and that only the class itself should access it, but it's no guarantee - users of the class are still free to reference user._name if they wish. If you want actual private data for each instance, you should define the class in a closure with a WeakMap that holds the private data:
const User = (() => {
const data = new WeakMap();
return class User {
constructor(name) {
this.name = name;
}
get name() {
return data.get(this);
}
set name(val) {
data.set(this, val);
}
}
})();
let user = new User("Jhon");
console.log(user.name);
Just look at this piece of code logically:
get name() {
return this.name
}
You read object.name. To return a value, the get name() getter reads this.name, which, in turn, resolves to get name(). And now, welcome to the infinite loop.
Hence, you need a separate variable name (to store the actual content of name) than the getter's name. That would be a private variable, and it has become a convention to prepend an underscore in these cases.
The _ affix is commonly used for private properties.
You use private properties in addition to getters and/or setters when you want to be able to control how and when you can update a property, or add side effects to those actions.
You should also have a private property declaration in your class
class User {
private _name; // <-- here
constructor(name) {
this.name = name;
}
get name() {
return this._name;
}
set name(val) {
this._name = val;
}
}

Protecting object properties against overwriting from console

I'm trying to protect a property of an object from being overwritten by the console. e.g. I have a person object, with a list of allergies as a property. The list of allergies should be able to be modified, however it should always be a list and an error should be thrown if a user tries writing 'person.allergies = "nonsense" '
I've tried looking into Object.freeze() and Object.seal() but cannot get these to work for this purpose, as I don't believe there is a way to unfreeze an object.
class Person {
constructor(name){
this.name = name
this.allergies = []
}
addAllergy(allergy){
this.allergies.push(allergy)
return allergy
}
}
ben = new Person('Ben') // Creating a new Object
ben.addAllergy('Dairy') // Giving the object a new allergy
ben.allergies // Should output ['Dairy']
ben.allergies = 'Soy' // Should make no changes to ben object.
You could make allergies a non writable property with Object.defineProperty:
class Person {
constructor(name){
this.name = name
Object.defineProperty(this, 'allergies', {
value: [],
writable: false
});
}
addAllergy(allergy){
this.allergies.push(allergy)
return allergy
}
}
ben = new Person('Ben') // Creating a new Object
ben.addAllergy('Dairy') // Giving the object a new allergy
console.log(ben.allergies) // Should output ['Dairy']
ben.allergies = 'Soy' // Should make no changes to ben object.
ben.addAllergy('Peanutes')
console.log(ben.allergies) // still array
writable defaults to false so you don't need to explicitly set it, but I think it makes the intention clearer. configurable also defaults to false which means you can't redefine the property with another call to Object.defineProperty().
Use private properties:
class Person {
#allergies;
constructor(name){
this.name = name
this.#allergies = []
}
addAllergy(allergy){
this.#allergies.push(allergy)
return allergy
}
get allergies() {
// you might want to deep clone it here
// to fully protect from changes the array behind
return this.#allergies;
}
set allergies(value) {
throw new Error('haha, I got you!');
}
}
Private fields are being implemented in the ECMA standard. You can
start using them today with babel 7 and stage 3 preset
Source

Categories

Resources