JavaScript syntax clarifying - javascript

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

Related

An infinite loop caused because of two getters?

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.

How to add getter to an existing object

I can have a getter in a JavaScript object like this:
var member = {
firstName:"XYZ",
lastName:"zzz",
get fullName(){ return (this.firstName + ' ' + this.lastName); }
}
I can even add more properties on the fly, like this:
member.isGuest = true;
But, is there any way we can add getters to an existing object? Something like this:
member.isGuest = get isGuest(){ return this.firstName == 'Guest'; }
try defineProperty
Object.defineProperty(member, 'isGuest', {
get: function() { return this.firstName=='Guest' }
});
You can use the new Object.defineProperty this way:
Object.defineProperty(
member,
'prop',
{
get: function() {
return this.lastName.toUpperCase()
}
}
);
In the past you had to use __defineGetter__ but now it has been deprecated.
This is possible not only with the above solutions, but also using the ... operator.
// Initialize x
var x = {
x: 5
}
// Log x.x and x.y
console.log(x.x, x.y /* undefined */)
x = {
...x, // {...x} is the same as {x: x.x}
// The getter
get y() {
return this.x
}
}
// Log x.x and x.y
console.log(x.x, x.y /* Not undefined */)
// Set x.x to 1234
x.x = 1234
// Log x.x and x.y
console.log(x.x, x.y)
... is the spread operator, which "spreads" the contents of the object it is used on, hence the name. For example:
doSomething(...[1, 2, 3]) is the same as doSomething(1, 2, 3)
{ ...{x: 1, y: 2, z: 3} } is the same as { x: 1, y: 2, z: 3 }
Moreover, you can add a custom getter and preserve the old value if it is not supposed to be calculated from other values, like in your example. To do so, create additional context layer using an anonimous function like this:
(function() {
const oldValue = yourObject[targetProperty];
let currentValue = oldValue;
const getter = function() {
return currentValue; // or do something before returning, like applying parseInt or whatever
};
const setter = function(newValue) {
currentValue = newValue; // or add some customization as well
};
Object.defineProperty(yourObject, targetProperty, {
get: getter,
set: setter,
enumerable: true, // depending on your needs
configurable: true // depending on your needs
});
})();
For the good old purposes of completeness, Reflect.defineProperty is also an option :). Note that Object.defineProperty and Reflect.defineProperty do not behave the same.
MDN compares the two functions (as well as all similar Object and Reflect methods):
Method Name
Object
Reflect
defineProperty()
Object.defineProperty() returns the object that was passed to the function. Returns a TypeError if the property was not successfully defined on the object.
Reflect.defineProperty() returns true if the property was defined on the object and false if it was not.
I prefer Reflect because I think boolean checks make more sense, but in general remember to error handle if you think that there may be conflicts (these should be infrequent when messing with object properties, unless one of your libraries is making similar, possibly conflicting changes).
You can only use get and set in a Class, It's mean that getter and setter method. but you can add a function to class:
member.isGuest = function isGuest(){ return this.firstName=='Guest';}
member.isGuest()
the get means that property can be read!
the set means that property can be write!
You can look it in book 《JavaScript:The.Definitive.Guide》6edition the 6 chapter!

Javascript defineProperty

When using:
Object.defineProperty(obj,prop,desc){
get: function(){...
set: function(){...
}
Does the getter/setter apply to obj[prop] or does it act on obj no matter what property is specified?
I am asking because I'm trying to setup some data binding based on a nested object like:
obj[propA] = {propB:'seomthing',propC:'somethingElse'}
and when I do something like this:
var obj = {value:{propA:'testA',propB:'testB'}};
Object.defineProperty(obj.value,'propA',{
get: function(){return this.value;},
set: function(newValue){this.value=newValue;console.log('propA: ',newValue);}
});
console.log(obj.value.propA);
obj.value.propA = 'testA';
Object.defineProperty(obj.value,'propB',{
get: function(){return this.value;},
set: function(newValue){this.value=newValue;console.log('propB: ',newValue);}
});
console.log(obj.value.propB);
obj.value.propB = 'testB';
console.log('propA: ',obj.value.propA,' --propB: ',obj.value.propB);
the getter assigns the value to ALL the properties set by defineProperty within the object.
If this is the correct functionality, is there a way to have the getter/setter work only on the property defined such that in the fiddle above, propA would yield testA and propB would yield testB?
The getter and setter only apply to the named property, but this inside each one refers to the object whose property it is (you don’t have to have a backing variable for every property).
In your example, you’re always reading and modifying obj.value.value. You can create a different variable for each one by wrapping each in an IIFE, for example:
(function () {
var value;
Object.defineProperty(obj.value, 'propA', {
get: function () { return value; },
set: function (newValue) { value = newValue; },
});
})();
Updated fiddle

Inheritance and Object Literals

I have created the following test and I am unsure why it does not work: http://jsfiddle.net/SKphY/. I should be getting three alert dialogs: "hello" and "goodbye" and "goodbye". Instead I am only geting the first two.
var p = {
hello : function() {
alert('hello');
}
};
var obj1 = Object.create(p, {
goodbye : function() {
alert('goodbye');
}
});
var obj2 = $.extend(p, {
goodbye : function() {
alert('goodbye');
}
});
$(function() {
// The third line (below) gives the parser error:
// 'Uncaught TypeError: Property 'goodbye' of object #<Object>
// is not a function'
obj1.hello();
obj2.goodbye(); // This executes fine
obj1.goodbye(); // This gives the parser error
});
The point is I am learning how to work with object inheritance, in this case with object literals, and I am curious why it is working for me when I use jQuery.extend, but not with Object.create. From what I can tell, I seem to have followed the approach that is outlined at https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create. What am I doing wrong?
Thanks for your time,
ktm.
http://jsfiddle.net/SKphY/1/
As #headacheCoder points out, the second argument in Object.create is for properties object (this is also described in the MDN document you linked).
Check the link above for a workable solution:
var obj1 = Object.create(p, {
goodbye : {value : function() {
alert('goodbye');
}}
});
The second argument in Object.create is for a properties object, not for merging. Use var obj1 = Object.create(p); instead and it will work as expected.
If specified and not undefined, an object whose enumerable own properties (that is, those properties defined upon itself and not enumerable properties along its prototype chain) specify property descriptors to be added to the newly-created object, with the corresponding property names.
// Example where we create an object with a couple of sample properties.
// (Note that the second parameter maps keys to *property descriptors*.)
o = Object.create(Object.prototype, {
// foo is a regular "value property"
foo: { writable:true, configurable:true, value: "hello" },
// bar is a getter-and-setter (accessor) property
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) { console.log("Setting `o.bar` to", value) }
}})

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