Field, getter and setter with the same name - javascript

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.

Related

Too much recursion error when a classfield is not declared

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

How to set function to class in JS?

I have a function
function log() {
console.log('something');
}
that I want this object to have:
class Car {
constructor(name) {
this.name = name;
}
log() {
// something
}
}
How can I attach it without repeating the code inside the function?
You can attach the function as a method on the Car prototype, so now it will be available as a method on all instances.
function log() {
console.log('something');
}
class Car {
constructor(name) {
this.name = name;
}
}
Car.prototype.log = log;
const a = new Car("Audi");
const b = new Car("BMW");
a.log();
b.log();
Basically you can do it in three ways;
first way is by using class constructor and passing function in constructor call
function log() {
console.log('something');
}
class Car {
constructor(name,log) {
this.name = name;
this.log = log
}
}
const audi = new Car("name",log);
audi.log();
second way is by simply using equal operator
function log() {
console.log('something');
}
class Car {
constructor(name,log) {
this.name = name;
}
log = log
}
const audi = new Car("some");
audi.log();
Third way is by using prototype
function log() {
console.log('something');
}
class Car {
constructor(name,log) {
this.name = name;
}
}
Car.prototype.log = log;
const audi = new Car("some");
audi.log();
Like this
Either as a member function OR on the prototype
class Car {
constructor(name) {
this.name = name;
}
log = str => console.log(str); // or log = log if log is defined elsewhere
}
const carA = new Car();
carA.log("vroom");
// or
const logger = str => console.log(str);
Car.prototype.logger = logger;
const carB = new Car();
carA.logger("vroom"); // also available to the previous and other Car objects
carB.logger("vroom");

How to invoke the method myFunction to return (name) i've tried many ways but it doesn't work

Below is what am talking about
class User {
constructor(name) {
this.name = name;
}
myFunction() {
return function() {
return this.name;
}
}
}
let kk = new User("kk");
let speak = kk.myFunction();
speak();
I'm getting this error:
Uncaught TypeError: Cannot read property 'name' of undefined
You should use this.name instead, and the function should not return a function
class User {
constructor(name) {
this.name = name;
}
myFunction() {
alert(this.name);
return this.name;
}
}
let user = new User("jj").myFunction();
console.log(user)
EDIT:
The question you edited now is different from the original question you posted. This one needs a currying solution:
class User {
constructor(name) {
this.name = name;
}
myFunction() {
let name = this.name;
return function() {
return name;
}
}
}
let kk = new User("kk");
let speak = kk.myFunction();
console.log(speak());
You can use an arrow function (() => {}) which won't have it's own binding of this, instead the this value of the enclosing lexical scope is used (your class in this case).
class User {
constructor(name) {
this.name = name;
}
myFunction() {
return () => this.name;
}
}
const speak = new User("kk").myFunction();
console.log(speak());

TypeError: ... is not a function

My JavaScript code in NodeJS results in:
TypeError: ninja.changeName is not a function
Following is my code:
function Ninja(name){
this.name = name;
var changeName = function(name2) {
this.name = name2;
}
}
var ninja = new Ninja("John");
ninja.changeName("Bob");
console.log(ninja.name);
What's wrong with the code?
var changeName will just create a reference to a function which will be lost once the function is done executing.
You must assign the function as a property of the Ninja function instead:
function Ninja(name) {
this.name = name;
this.changeName = function(name2) {
this.name = name2;
}
}
var ninja = new Ninja("John");
ninja.changeName("Bob");
console.log(ninja.name);
var changeName = function(name2) {
this.name = name2;
}
You are declaring a function but not attaching that to the object.
It should be
this.changeName = function(name2) {
this.name = name2;
}
So that the property changeName as a function attached to the object.
You are assigning a function to a variable. This isn't the same as setting a function in the objects prototypal inheritance structure and the variable changeName is only in scope within the context of Ninja.
You can assign the function to this.changeName (important that you're binding to this) like the following:
function Ninja(name){
this.name = name;
this.changeName = function(name2) {
this.name = name2;
}
}
Or you could use prototypes:
function Ninja(name){
this.name = name;
}
Ninja.prototype.changeName = function(name2) {
this.name = name2;
}
Although these approaches look fairly similar, the difference is very important. The first approach creates a new function for every Ninja created. The second approach will use the same function for each object. To look into the reason to use prototypal inheritance, there are various blog posts scattered around the internet.
You are declaring the changeName as a variable but not binding it with the 'Ninja', So I believe instead of using var, it should this. So the code becomes like this.
function Ninja(name){
this.name = name;
this.changeName = function(name2) {
this.name = name2;
}
}
Hope it helps.
function Ninja(name){
this.name = name;
return {
changeName : function(name2) {
this.name = name2;
}
}
}
In your code changeName is not exposed if you want to access the private data you can try with the above snippet
Another approach to make your function public available would be to first declare it private (Some prefer to use an underscore, like usual in .Net):
function Ninja(name) {
this._name = name;
var _changeName = function(name) {
this._name = name;
}
}
And then export it using return. Some might know this from the classical Java boilerplate Getters and Setters, where fields are declared private by default and Methods make them available:
function Ninja(name) {
this._name = name;
var _changeName = function (name) {
this._name = name;
}
return {
changeName: _changeName,
getName: function () {return _name;}
}
}
...And now, use the object:
var ninja = new Ninja("John");
ninja.changeName("Bob");
console.log(ninja.getName());

Reflect.construct() Why it is necessary override object prototype?

class Name{
constructor(name)
{
this.name = name
}
}
function greet()
{
return "Hello World"
}
let Max = Reflect.construct(Name,["Maxi"],greet);
console.log(Max.__proto__ == greet.prototype); //true
Why it is necessary override object prototype
I believe this is one way for you to achieve what you would like to do with the classes of ES6;
class Name{
constructor(name)
{
this.name = name;
}
greet(n){return "Hello " + n;}
}
var Max = new Name("Maxi");
console.log(Max.greet("John"));

Categories

Resources