I want to create a setter in JS. But there is something wrong with my code, and this is my code:
class test {
constructor(str) {
this.name = str;
}
set name(str) {
this.sayHi();
}
sayHi() {
let temp = this.name;
console.log(`My name is ${temp}`)
}
}
let a = new test('bill') //My name is undefined
a.sayHi() //My name is undefined
why does it console undefined in this example?how to make it work?
Your setter needs to store the value somewhere; you'll also need a getter to get the value from that place.
Here's a simple example storing the value in another property:
class Test {
constructor(str) {
this._name = str; // ***
// (You might use `this.name = str` here, setters are sometimes
// considered an exception to the "don't call methods in the
// constructor" rule)
}
set name(str) {
this._name = str; // ***
}
get name() { // ***
return this._name; // ***
} // ***
sayHi() {
let temp = this.name;
console.log(`My name is ${temp}`)
}
}
let a = new Test('bill') //My name is undefined
a.sayHi() //My name is undefined
Of course, if you're going to do that, it doesn't make a lot of sense to have a setter, but that's getting a bit afield of the question...
Note: I changed the name of your class to Test (instead of test). The overwhelming convention in JavaScript is that class names (really constructor function names) are initially-capitalized.
Try this instead:
class test {
constructor(str) {
this.name = str;
}
set name(str) {
this._name = str
}
sayHi() {
let temp = this.name;
console.log(`My name is ${temp}`)
}
get name() {
return this._name
}
}
Related
If we declare a class like this:
class client{
constructor(name){
this.name = name
}
get name()
{
return this.name ;
}
set name(val)
{
this.name = val ;
}
}
Instantiate
it will give an error ( too much recursion) and that normal because the setter will call itself.
class client{
constructor(name){
this.name = name
}
get name()
{
return this.name ;
}
set name(val)
{
this.name = val ;
}
}
let cl = new client();
But when i declare 'name' as class field i don't get that error, I don't understand why.
class client{
name = null ;
constructor(name){
this.name = name
}
get name()
{
return this.name ;
}
set name(val)
{
this.name = val ;
}
}
let p = new client();
console.log(p) ;
TL;DR You can't have both a data property and an accessor property with the same name on the same object, and if you have a data property on an object, any property with the same name on its prototype won't be used (unless you do so explicitly). The name class field definition creates an own data property on the object being constructed.
In the first case (as I think you know), this.name inside the name setter calls the name setter again, recursing until you run out of stack. (And the same for the getter, if you used it.)
So what's different when you have that name field definition there?
The way class fields are defined, that name definition creates an own property on the instance being created, as though you had this code at the beginning of the constructor (just after a super call if this were a subclass):
constructor(name) {
// Definition of `name` field:
Object.defineProperty(this, "name", {
value: undefined,
writable: true,
configurable: true,
enumerable: true,
});
// Remainder of constructor code:
this.name = name;
}
Since it's an own property, this.name resolves to that data property, not to the accessor property on the prototype that the get name and set name accessor definitions create. So those accessors on p's prototype are never used.
You can see that if you look at the definition of the property in your second example:
class client {
name = null;
constructor(name){
this.name = name;
}
get name()
{
return this.name ;
}
set name(val)
{
this.name = val ;
}
}
let p = new client();
console.log(p) ;
console.log(Object.getOwnPropertyDescriptor(p, "name"));
If you want to have the accessor property, you could use a private field, which is quite new but supported in modern environments:
class Client {
#name = null;
constructor(name) {
this.#name = name;
}
get name() {
return this.#name;
}
set name(val) {
this.#name = val;
}
}
let p = new Client("Joe");
console.log(p.name);
console.log(Object.getOwnPropertyDescriptor(p, "name")); // undefined
console.log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(p), "name")); // accessor
.as-console-wrapper {
max-height: 100% !important;
}
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;
}
}
I've tried trouble shooting this with essentially the exact same issue on codecademy to no avail. I'm trying to get my javascript setter to check whether the argument I am passing in is a number, and if the conditions are satisfied set it to the class property, else return an error string.
I can't understand why this isn't working and I've spent several long hours on it already, any help would be greatly appreciated!
Code:
class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
get name() {
return this._name;
}
get age() {
return this._age;
}
set age(num) {
if (num.isNaN()) {
console.log('error!')
} else {
this._age = num
}
}
}
let human = new Person('Armand', 'string');
console.log(human);
If you want to fire the setter function inside the constructor, you should not use the backing field property (this._age) inside the constructor. Remove the underscore. (Source)
Furthermore, string does not have the isNan() method rather, it takes the variable as an argument like the code below. (Source)
If you want a begginer's guide to Javascript ES6 Class Syntax, you should check this out. Thanks :)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
get name() {
return this._name;
}
get age() {
return this._age;
}
set age(numeric) {
if (isNaN(numeric)) {
console.log('error!')
} else {
this._age = numeric
}
}
set name(newName) {
this._name = newName;
}
}
let human = new Person('Armand', 'string');
console.log(human);
I have a namespace MyNameSpace which has a public variable Name and public methods SayName and ChangeName
var MyNameSpace = MyNameSpace|| function () {
var _name = "John";
function _SayMyName() {
return _name;
}
function _changeMyName(newName) {
_name = newName;
}
return {
SayName: _SayMyName,
ChangeName : _changeMyName,
Name:_name
}
}();
alert(MyNameSpace.SayName()) // John
MyNameSpace.ChangeName("Paul")
alert(MyNameSpace.SayName()) // Paul
alert(MyNameSpace.Name) // John
jsfiddle
MyNameSpace.Name will always return John even if _Name has been changed. Why?
MyNameSpace.Name doesn't hold a reference to _Name?
What to do so MyNameSpace.Name returns the same value as _Name?
You are creating a Object which Name property holds a copy of _name. The value of _nameis copied by value, not by reference, that's why it does not change.
You could write this, to mimic your desired behaviour:
return {
get Name () { return _name; },
//if you like to have a setter, than:
set Name (n) { _name = n; }
//the other properties go here...
}
I'd like to create a class in JS that uses native getters and setters. I know I can create getters/setters for objects, like so:
var obj = {
get value(){
return this._value;
},
set value(val){
this._value = val;
}
}
I also know that I can use this.__defineGetter__ inside a class/function, but MDN says that using __defineGetter__() etc is discauraged.
Is there any better way to add getters and setters to js class than:
function class(){
};
class.prototype = {
get value(){
//....
}
?
2019: Hooray for ES6!
class Person {
get name() {
return this._name + '!!!'
}
set name(newValue) {
this._name = newValue
}
constructor(name) {
this._name = name
}
}
const me = new Person('Zach')
console.log(me.name) // Zach!!!
me.name = 'Jacob'
console.log(me.name) // Jacob!!!
// Of course, _name is not actually private.
console.log(me._name) // Jacob
The cleanest way to define properties on a class is via Object.defineProperties. This allows you to define all of your properties in a single, easily readable block. Here's an example:
var MyClass = function() {
this._a = undefined;
this._b = undefined;
};
Object.defineProperties(MyClass.prototype, {
//Create a read-only property
a : {
get : function() {
return this._a;
}
},
//Create a simple read-write property
b : {
get : function() {
return this._b;
},
set : function(value) {
this._b = value;
}
}
});
There are a plethora of other options when defining properties, so be sure to check out the link I posted for more information. It's also important to keep in mind that even the most basic getter/setter property is only as fast as a method call in current browsers, so they can become a bottleneck in performance-intensive situation.
How about this implementation:
function makeObject(obj, name) {
// The property
var value;
// The setter
obj["get" + name] = function() {return value;};
// The getter
obj["set" + name] = function(v) {
value = v;
};
}
To experiment:
var obj = {};
makeObject(obj, "Name");
obj.setName("Lolo");
print(obj.getName());
Of course, you can test name for validity before storing it in value. The test can be supplied as an additional argument to the makeObject function.