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;
}
Related
I was learning JavaScript oops with some sample example when I came across to access superclass methods in subclass which is possible with super keyword
but when I try to access or return a variable of super class it returns undefined or the sub class variable I tried in various way to get variable
I have also gone this Stack Overflow post.
class dad {
constructor(name) {
this.name = name;
}
printname() {
console.log(this.name);
var a = 2;
return a;
}
sendVal() {
console.log(this.name);
var a = 2;
return this.name;
}
}
class son extends dad {
constructor(name) {
super(name);
}
printname() {
console.log(super.printname());
}
printvariable() {
console.log(super.name);
}
getvalue() {
console.log(super.sendVal())
}
}
var o1 = new dad('jackson');
var o2 = new son('jack')
o1.printname()
o2.printname()
o2.printvariable()
o2.getvalue()
When you use super.fieldName to access a field of a parent class, you are actually querying the field name on the prototype of the parent class.
So you might believe calling super(name); from the Son constructor sets the name in the prototype of the parent class, but it is not so, it actually sets the name property inherited by the Son class which you can access by using this.name.
So I modified your example code and shown how to actually get a value by calling super.fieldName. In the example I added a property age in the prototype of the Dad class and set its value to 50, now in the Son class printvariable() will correctly call the super.age by referring the prototype of the Dad class.
You can actually see it in action if you use babel to transpile it to ES2015, after all classes in JavaScript are actually syntactic sugar.
class Dad {
constructor(name) {
this.name = name;
Dad.prototype.age = 50; //adding property to prototype
}
printname() {
console.log(this.name);
var a = 2;
return a;
}
sendVal() {
console.log(this.name);
var a = 2;
return this.name;
}
}
class Son extends Dad {
constructor(name) {
super(name);
}
printname() {
console.log(super.printname());
}
printvariable() {
console.log(`super.name will be undefined, as not present in prototype of the Dad class: ${super.name}`);
console.log(`super.age will have a value of 50, present in the prototype of the Dad class: ${super.age}`);
console.log(`this.name will be jack, as it is set from the constructor of the Son class: ${this.name}`);
}
getvalue() {
console.log(super.sendVal());
}
}
var o1 = new Dad('jackson');
var o2 = new Son('jack')
o1.printname();
o2.printname();
o2.printvariable();
o2.getvalue();
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
}
}
I am trying to access the name defined in the constructor via a method, but it is returning undefined. Here is the simple code:
class Person {
constructor(){
let name = 'Tom';
}
logName(){
console.log(this.name);
}
}
let x = new Person();
x.logName();
You need to define the name as a property of the object. In your case this.name
class Person {
constructor(){
this.name = 'Tom';
}
logName(){
console.log(this.name);
}
}
let x = new Person();
x.logName();
In your code, you've defined the variable name inside the constructor. It remains in there but doesn't escape.
Could you explain why I get
Uncaught RangeError: Maximum call stack size exceeded
in this example. What's the sequence of actions?
"use strict";
let myClass = class myClass{
constructor(name){
this.name = name;
}
get name() { return this.name; }
set name(name){ this.name = name; }
}
let myObj = new myClass("John");
You're calling the setter from the setter, infinitely looping.
set name(name) {
this.name = name; // <-- ⛔ `this.name` calls the `set`ter again
}
You should use a differently named backing variable of some sort. Unfortunately the "TC39 Private Fields" proposal for JS is not finalized so they will be public, and a naming convention is needed for now.
Here's a modern example:
class Person {
_name = ""; // 'private' by convention, see also: https://github.com/tc39/proposal-class-fields#private-fields
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
constructor(name) {
this.name = name;
}
}
Or following the Question's structure:
"use strict";
let myClass = class myClass {
constructor(name) {
this.name = name;
}
get name() {
return this._name;
}
set name(name) {
this._name = name;
}
}
let myObj = new myClass("John");
console.log(myObj);
To my surprise it's not trivial to have variables private to a class.
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...
}