An infinite loop caused because of two getters? - javascript

The following code executes an infinite loop (prints getter1 once, and then getter2 until stopped):
var person1 = {
_age: 42,
get age() {
console.log("getter1");
return this._age;
}
};
Object.defineProperty(person1, "_age", {
get: function() {
console.log("getter2");
return this._age;
}
});
console.log(person1.age); // true
What causes it? (Note: I'm aware to the fact that I declared two different getters).

It's not an infinite loop, but infinite recursion. You're return this._age inside a getter for _age, but accessing this._age implicitly calls the getter again.
Note that once you've defined a getter called _age the old value of _age (42) is overwritten. There is no property with the value 42 anymore. If you want to create a getter that returns the value of a property it can't be the getter for that same property, so name your getter something other than _age:
var person1 = {
_age: 42,
get age() {
console.log("getter1");
return this._age;
}
};
Object.defineProperty(person1, "age2", {
get: function() {
console.log("getter2");
return this._age;
}
});
console.log(person1.age); // 42
console.log(person1.age2); // 42

Each time you return a value from a getter you fire the other getter

Your first getter is asking for this._get this triggers the second getter, which asks for this._get which triggers the second getter, which asks for this._get, which triggers... you get the point.

Related

Invoking a method from an Object key

I have this class
class Dark {
constructor(name) {
this.name = name;
}
destroy() {
console.log("method called")
console.log(this);
}
}
const DarkObject = new Dark('DarkObject');
const copyDarkObject = {destroy: DarkObject.destroy}
console.log(copyDarkObject.destroy())
//> method called
// > undefined
I stored the reference of the class method to the key of a other Object
const copyDarkObject = { destroy: DarkObject.destroy };
the created Object is not the owner of the method so the return is undefined thats clear to me but I can still invoke the method from the key with console.log(copyDarkObject.destroy())
how is that possible ?
the created Object is not the owner of the method so the return is undefined
No. The statement
console.log(copyDarkObject.destroy())
logs undefined because the destroy method doesn't return anything (since it doesn't use a return statement).
The statement
console.log(this)
in the destroy method will log your copyDarkObject, because destroy was called on copyDarkObject.
And that's why the log says:
method called
Object { destroy: destroy() } // the object destroy was invoked on
undefined // the return value of destoy
Class keyword in JavaScript is just syntactic sugar.
Under the hood it is still prototype based inheritance/system, which allows some extra flexibility.
What exactly happens in your code:
You see returned value undefined - this is correct, because you never return anything from destroy() and Js by default "returns" undefined from functions.
You create a new object giving it one property, which is a reference to darkObject.destroy - value of this pointer/reference in Js depends on how function was called. You can read up on MDN on how built-in function like bind() or apply or call work. In this case when you call destroy on copyOfDarkObject, this points to copyOfDarkObject
class Dark {
constructor(name) {
this.name = name;
}
destroy() {
console.log('I am calling destroy on', this);
return `I am ${this.name}`;
}
}
const darkObject = new Dark('DarkObject');
const copyOfDarkObject = {destroy: darkObject.destroy}
console.log('returned value', copyOfDarkObject.destroy());
console.log('returned value', darkObject.destroy());
console.log(copyOfDarkObject instanceof Dark);
console.log(darkObject instanceof Dark);
Some extra info:
While logging this you can see, that copyOfDarkObject has one property called destroy, but darkObject only one called name. It is so, because destroy method exists on darkObject prototype and you don't see it directly on the object.

javascript - object value only to be worked out when needed / called upon

for example, i have an object where the value of a key is yet to be known/computed but when i call that key for the first time it computes the value and replaces the calculation / function with the computed value. The point being that the value is only worked out when needed and then can be reused again without having to work it out again...
this is what i have so far...
function newobj() {
this.test=function(){
this.test=[1,2,3,4]//complex function in real world use
return this.test
}
}
a = new newobj()
a.test() //[1,2,3,4]
a.test() //not a function!
a.test //[1,2,3,4]
as you can see this requires parenthesis to call initially but not the second time, so this requires knowing whether it's already been called or not - not ideal
version 2
function newobj() {
this.test=function(){
var a=[1,2,3,4]
this.test=function(){return a}
return a
}
}
a = new newobj()
a.test() //[1,2,3,4]
a.test() //[1,2,3,4]
this just somehow doesn't seem the correct way to go about this.. but maybe it is?
apologies if this is a dumb question and thanks in advance for your help
You can use the ability of a getter to overwrite itself to create a "lazy" property whose value is only calculated the first time it's accessed:
There's a good example at MDN.
get test() {
delete this.test;
return this.test = someExpensiveFunctionCall();
}
Subsequent accesses to the property don't even use the getter - they retrieve the value direct from the object.
In your case since you're not using the usual getter syntax, you'd have to modify your function thus:
function newobj() {
Object.defineProperty(this, 'test', {
get: function() {
delete this.test;
return this.test = someExpensiveFunctionCall();
},
configurable: true
});
}
I think what you need is the Module Pattern. You could do
let Module = (function(){
let test = null;
return function(){
if(test == null){
console.log('initialized test');
test = [1, 2, 3, 4];
}
return test;
}
})();
let a = new Module();
console.log(a);
console.log(new Module());

JavaScript syntax clarifying

For the following syntax
a = {
get p() {
alert(1)
}
};
alert(a.p);
It prompts me 1, than undefined.
For
a = {
set p(x) {
alert(x)
}
};
alert(a.p);
It prompts me undefined.
I do not totally understand the behaviour,
what does
a = {
get p() {
alert(1)
}
}
and
a = {
set p(x) {
alert(x)
}
};
mean?
There are two types of object properties: data properties and accessor properties. Accessor properties are accessed by getters and setters.
Your object a is intended as object with accessor property which called p.
Normally, such objects are declared in the following way:
a = {
_p: 'my value', // intended as private
get p() {
return this._p;
},
set p(x) {
this._p = x;
}
};
console.log(a.p); // gives 'my value'
a.p = 'new value';
console.log(a.p); // gives 'new value'
Another way is to use Object.defineProperty() method which lets you to set all needed properties attributes. Like this:
var a = {p: 'my value'};
Object.defineProperty(a, 'p', {
get: function() { return this.p; },
set: function(newValue) { this.p = newValue; },
enumerable: true,
configurable: true
});
because p() method returns nothing hence it returns undefined
if you do
a={get p(){alert(1); return 2;}};
alert(a.p);
it will alert 1 and then 2 since this p() method returned 2
{get p(){alert(1)}}
this is an object that has a getter p
when you use a.p it will use that getter to retrieve some value
so when you do alert(a.p); it first call the getter, so alert 1, then alert the returned value undefined.
[Edit] - You changed your original question so this answer doesn't cover everything.
p is a getter function - It is called whenever you access the p property. In your getter you have alert(1).
The getter function doesn't return anything. Thus, p is undefined. So the alert(a.p) alerts undefined.
Thus, your program does:
Get value of a.p: alert(a.p)
Calls p getter function. Which has alert(1)
p getter function returns nothing
Thus alert(a.p) alerts undefined

How to use getters and setters in Javascript

Can somebody please explain to me why this simple piece of code is not working?
var user = {
get name() {
return this.name;
},
set name(value) {
this.name = value;
}
};
user.name = 'David';
When I put this in the Firebug console in Firefox 21.0 it gives me this error:
InternalError: too much recursion
this.name = value;
Why? What is the proper way to define getters and setters in Javascript?
When you attempt to set name, the function will set this.name = value.
But the function is now attempting to set name. Therefore it will call the function again and set this.name to value.
But the function is now attempting to set name. Therefore it will call the function again and set this.name to value.
But the function is now attempting to set name. Therefore it will call the function again and set this.name to value.
....... SOME TIME LATER .......
But the function is now attempting to set name. Therefore it will call the function again and set this.name to value.
But the browser has determined that the call stack is too deep, the function has called itself too many times, and therefore to prevent a complete crash it causes the function to fail with the error you see.
Try using a different property name, such as this._name, to store and retrieve the value.
Your setter is calling itself.
Here's a solution :
var user = {
get name() {
return this._name;
},
set name(value) {
this._name = value;
}
};
user.name = 'David';
Side note : Be careful that the get and set operators aren't supported in IE8.
Try,
var user = {
get name() {
return this._name;
},
set name(value) {
this._name = value;
}
};
user.name = 'David';
Note the use of _name instead of name. Setting the value of name in the setter of name is a recursive call, hence the exception.

Object.defineProperty get/set closure

Ok, I try to create new object this way:
var src = {a:'a', b:'b', c:'c'};
var out = {};
for(var prop in src){
Object.defineProperty(out, prop,{
get: function(){
return src[prop];
},
set: function(val){
src[prop]=val;
}
})
}
And get a bad result:
out = {a:'c', b:'c', c:'c'}
I know other ways to create this object, so as:
for (var prop in src) {
(function(prop) {
Object.defineProperty(out, prop, {
get: function() {
return src[prop];
},
set: function(val) {
src[prop] = val;
}
})
})(prop)
}
or:
Object.keys(src).map(function(prop){
Object.defineProperty(out, prop,{
get: function(){
return src[prop];
},
set: function(val){
src[prop]=val;
}
})
})
But I can't understand why, in the first method, a string parameter "prop" will be sent to the function 'defineProperty' by link. Help me to understand this please.
Sorry for bad english.
When you create a function inside a loop you create a closure around the variables used in that loop. In this case there is a closure around prop. Each function (the getters) has a reference to prop so when they are called later on (when the getter is used) they use the value in prop which happens to be the last value that was assigned in the loop.
In other words, since the getter is called later, the value in prop is whatever value it was last set to. defineProperty, on the other hand, gets the correct value since there is no closure. It is called with the value at the time of the call rather than after the loop is complete.

Categories

Resources