How to access a private field in a class - javascript

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());

Related

Using underscores in classes in Javascript

I've read that the underscore in js is an acceptable naming character with no additional functionality, used to signify something is private. My question is how does this work then?
class User {
constructor(name) {
this.name = name
}
get name() {
return this._name
}
set name(value) {
if (value.length < 4) {
alert("Name is too short")
return
}
this._name = value
}
}
let user = new User("Kiki")
alert(user.name)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
The variable is not defined the first time with the underscore, how does JavaScript know what I'm referring to?
Also if I remove the underscores I get an error that
InternalError: too much recursion
I don't understand why? If the underscore has no functionality, shouldn't it work the same without the underscores?
Your working version has two properties:
_name, a data property you create in the constructor
name, an accessor property you create on the class prototype via get name() { /*...*/ } (and set name(value) { /*...*/ })
In this code:
let user = new User("Kiki")
alert(user.name)
...when you use user.name, you're using the accessor property's getter function (get name), which returns the value of this._name.
If you remove the _, you have only one property, which is the accessor property, and assigning to this.name in the constructor calls the accessor's setter function, which (in that version) would do this.name = ____, which would call the setter function, which would do this.name = ____, which would call the setter function...
If you're going to have that public name property that does validation on the value you assign to it, you need a place to keep the value the accessor accesses. That can be another public property with a different name (_name), which is fine though easily bypassed; or in modern code you might use a private instance field instead:
class User {
#name; // Private instance field declaration
constructor(name) {
this.name = name;
}
get name() {
return this.#name;
}
set name(value) {
if (value.length < 4) {
console.log("Name is too short");
return;
}
this.#name = value;
}
}
let user = new User("Kiki");
console.log(user.name);
Side note: I'd make the error condition throw an Error, not just output a message, not least so that new User("x") doesn't leave the name with the default value undefined:
set name(value) {
if (value.length < 4) {
throw new Error(`Name is too short, expected at least 4 chars, got ${value.length}`);
}
this.#name = value;
}
In a comment you've asked:
...if I put this._name=name in the constructor it works as well. Would that be better practice?
That would mean that you wouldn't check the length of the name passed to the constructor. But your instinct is good! In class-based OOP circles, best practice is usually not to call setters (or other public methods) from constructors, because the setter/method may be overridden in a derived class, and the derived class may expect that the setter is working with a fully-initialized instance. But when you all a setter from the constructor, the instance may not be fully initialized yet.
In your specific case it doesn't really matter, there's only one property (conceptually), but the usual way to avoid the issue with overridden setters/methods is to use a private function that does the work of the setter, and use that function both in the setter and the constructor:
class User {
#name;
constructor(name) {
this.#setName(name);
}
#setName(value) { // <== Private instance method
if (value.length < 4) {
throw new Error(`Name is too short, expected at least 4 chars, got ${value.length}`);
}
this.#name = value;
}
get name() {
return this.#name;
}
set name(value) {
this.#setName(value);
}
}
try {
let user = new User("Kiki");
console.log(user.name);
} catch (error) {
console.error("ERROR, the first call should work");
}
try {
let user = new User("X");
console.log(user.name);
console.error("ERROR, the second call should fail");
} catch (error) {
console.log(`Second call failed as expected: ${error.message}`);
}
try {
let user = new User("Kiki");
console.log(user.name);
user.name = "x";
console.error("ERROR, the third call should fail");
} catch (error) {
console.log(`Third call failed as expected: ${error.message}`);
}
Since a private method can't be overridden in a derived class, it's okay to call it from the constructor.
The underscore is just a commonly-used way of remembering that the property is private. You can use any variable name you want.
Just remember that there are two identifiers for the name, one is for internal use and one is for external use. They can't be exactly the same.
class User {
constructor(name) {
this.privateName = name
}
get name() {
return this.privateName
}
set name(value) {
if (value.length < 4) {
alert("Name is too short")
return
}
this.privateName = value
}
}
let user = new User("Kiki")
console.log(user.name)
That being said, in the latest javascript you can (should?) use the # to make private properties. This makes sure they are actually private (not reachable from outside the class)
class User {
#name
constructor(name) {
this.#name = name
}
get name() {
return this.#name
}
set name(value) {
if (value.length < 4) {
alert("Name is too short")
return
}
this.#name = value
}
}
let user = new User("Henk")
console.log(user.name)
And because we used # the private variable actually can't be reached:
console.log(user.#name) // ERROR

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;
}
}

Using classes in for loop in node.js

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
}

How to access a method from a class from another class?

I want to use Object Oriented Programming technique with JavaScript but I can't access a method from one class from another class. How can do like the following?
class one{
write(){
console.log("Yes! I did!");
}
}
class two{
var object=new one();
tryingMethod(){
object.write();
}
}
I get the following error:
Uncaught SyntaxError: Unexpected identifier -->> for var object=new one();
Your syntax is not legal. There should be an error in your console showing you which line of code is not correct.
If it's a static method (doesn't use any instance data), then declare it as a static method and you can directly call it.
If it's an instance method, then you would typically create an object of type one and then call the method on that object (usually in the constructor).
To make the method static (which appears to be fine in your specific case):
class One {
static write(){
console.log("Yes! I did!");
}
}
class Two {
tryingMethod(){
One.write();
}
}
For the non-static case, you don't have the proper syntax. It appears you want to create the instance of the One object in a constructor for Two like this:
class One {
write(){
console.log("Yes! I did!");
}
}
class Two {
constructor() {
this.one = new One();
}
tryingMethod(){
this.one.write();
}
}
var x = new Two();
x.tryingMethod();
Note: I'm also following a common Javascript convention of using an identifier that starts with a capital letter for the class/constructor name such as One instead of one.
What I'd recommend doing is not tying the classes together so tightly and doing something like this...
class One {
write() {
console.log("Yes! I did!");
}
}
class Two {
constructor(one = new One()) {
this.one = one;
}
tryingMethod() {
this.one.write();
}
}
Now what you can do is...
const two = new Two();
two.write();
This allows you to have a better separation of concerns and allows you to more easily unit test the Two class because you can pass in a mock implementation of the One class if you want.
describe("two class", () => {
it("should do something", () => {
const one = {
write: sinon.spy()
};
const two = new Two(one)
two.tryingMethod();
expect(one.write.calledOnce).to.be.ok;
});
});
You can pack dependencies in a container.
class Provider {
private _one?: One;
private _two?: Two;
one(): One {
return this._one || (this._one = new One(this));
}
two(): Two {
return this._two || (this._two = new Two(this));
}
}
class One {
constructor(private provider: Provider) { }
write() {
console.log("Yes! I did!");
}
}
class Two {
constructor(private provider: Provider) { }
tryingMethod() {
this.provider.one().write();
}
}

How to create private variables and methods inside a class ES6? [duplicate]

This question already has answers here:
Private properties in JavaScript ES6 classes
(41 answers)
Closed 6 years ago.
How to create private variables and methods using ES6 class which should be access by public methods of the same class.
class MyClass {
constructor() {
this.publicVar = "I am public";
//some private Variable e.g privteVar1 = "I am Private1"; privateVar2 = "I am Private2";
this.publicMethod = () => {
//it should have accesses to private variables and methods
console.log(privateVar1, privateVar2)
};
//similarly need to create some privateMethod
}
render() {
//it also should have access to the private variables
}
}
As noticed by #yibuyisheng you can use symbols to create unaccessible references to properties. Also you can create private method as well.
var privateMethod = Symbol('private');
class SomeClass {
constructor() {
}
[privateMethod]() {
// some private actions
}
render() {
// call private method
this[privateMethod]()
}
}
Symbol creates a unique reference and it can be used as property name. And this property or method could be called only by using it. It is possible because ES6 is introducing computed properties syntax and you can use variables for dynamic properties definition.
If you are not exposing this symbol for others, only you can call it.
Just make it a local variable, with var/let/const, and it will be available through closure from all the privileged methods you define in your constructor. This has not changed from ES5, btw.
class MyClass {
constructor() {
this.publicVar = "I am public";
const privateVar1 = "I am Private1",
const privateVar2 = "I am Private2";
this.publicMethod = () => {
// it has access to private variables
console.log(privateVar1, privateVar2)
};
}
}
Notice that those variables are local to the constructor, so you cannot access them from your prototype methods, for those you would need public properties or complicated workarounds.
You can use Symbol to implement it like this:
var privateSymbol = Symbol('private');
class SomeClass {
constructor() {
this.setPrivateValue('some init value');
}
setPrivateValue(value) {
this[privateSymbol] = value;
}
getPrivateValue() {
return this[privateValue];
}
}
Note that put the previous codes in some module, and do not expose the privateSymbol.
The only way to get true privacy in JS is through scoping, so there is no way to have a property that is a member of this that will be accessible only inside the component. The best way to store truly private data in ES6 is with a WeakMap.
const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();
class MyClass {
constructor() {
privateProp1.set(this, "I am Private1");
privateProp2.set(this, "I am Private2");
this.publicVar = "I am public";
this.publicMethod = () => {
console.log(privateProp1.get(this), privateProp2.get(this))
};
}
render() {
//it also should have access to the private variables
console.log(privateProp1.get(this));
}
}
Try this:
console.log(this[privateVar1], this[privateVar2])
Call your properties like this[myPrivate]. This is the way to make your properties a some kind of private. But this syntax does not provide true privacy.

Categories

Resources