Javascript getters and setters for object, not object properties - javascript

I know how to use JS getters and setters for object properties like so
var myObject = {
value : 0,
get property() {
return this.value;
},
set property(v) {
this.value = v;
}
}
so that calling myObject.property = 2 will set myObject.value, but what I'm wondering is if there is some way to call myObject = 2 and still set myObject.value rather than changing myObject from an object into a number.
It's probably not possible, but javascript is an incredibly flexible language and I thought I'd pose the question to the community before I discarded the idea.

It is possible indeed. Only for global variables though.
Object.defineProperties(this, {
myObject: {
get: function () {
return myObjectValue;
},
set: function (value) {
myObjectValue = value;
},
enumerable: true,
configurable: true
},
myObjectValue: {
value: 0,
enumerable: false,
configurable: true,
writable: true
}
});
myObject = 5;
console.log(myObject);
console.log(delete myObject);
Now, every time you assign a value to myObject, it shall actually run the set function and assign the value to the other property instead. Now, if you wanted to minimize pollution, you could create an IIFE and use variables inside that instead to hold the values, per se.
http://jsbin.com/zopefuvi/1/edit
And here is the version with the IIFE.
http://jsbin.com/puzopawa/1/edit

Just as a side note. I didn't know until reading this question that you could define getters/setters without Object.defineProperty. I was wondering at what the difference was between this 'shorthand' method, and defining getters/setters via Object.defineProperty.
I found, using the myObject example in the question:
var myObject = {
value : 0,
get property() {
return this.value;
},
set property(v) {
this.value = v;
}
}
that Object.getOwnPropertyDescriptor(myObject, 'property')
returns
get: ƒ property()
set: ƒ property(v)
enumerable: true
configurable: true
So worth mentioning is that property in this case is enumerable, meaning it will show up in loops.

Related

Defining Javascript's object property by invoking function

Suppose I have a javascript Object and I want to define a property and property attributes for it. i.e I want to define scaleRatio through a function. How would I go about this ?
I know I can do canvasData.scaleRatio = function(){};
Do I then define Object.defineProperty{canvasData, 'scaleRatio', {enumerable: true, configurable: false}; ?
Is there a systematic way of defining properties and attributes directly on an object ? I know you can do set: function(){} inside defineProperty but I don't want to do that extra step canvasData.scaleRatio = 'asdf'; Why can't I just use an iffe inside object.defineProperty ?
Update:
My reasoning for this is that I want to call setupCanvas and initialize a CanvasObject and all it's properties. Some of it's properties will have to be computed by calling a function. I also want to be able to specify property attributes. How can I do this in a systematic way ?
function setupCanvas(chart){
canvasData = Object.defineProperties({}, {
scaleRatio: {
value: function(){
console.log('gets here');
var ratio = chart.data.datasets[0].data.reduce(function(prev,curr){
if (prev > curr){
return prev;
}
return curr;
});
return $(canvas).outerWidth() / (scaleRatio + 10);
},
enumerable: true,
configurable: false,
writable: true
}
});
console.log(canvasData.scaleRatio);,
}
Why can't I just use an iffe inside object.defineProperty ?
You can put an IIFE in place of the value. Here's an example of the proper syntax to do that:
var canvasData = Object.defineProperties({}, {
scaleRatio: {
value: (function(){
// put whatever logic you want here
return Math.random();
})(),
writable: true,
configurable: false,
enumerable: true
}
});
console.log(canvasData);
console.log(canvasData.scaleRatio);
The value function you show in your code example is not an IIFE - it's just a regular function so you're create a scaleRatio property that is a method, not a static property.

Object.defineProperty() default value with getter/setter

I am messing around with some "classical" inheritance and I'm running into an issue. I am using Object.defineProperty() to add properties to my LivingThing "class". I want to have a default value, along with a property getter/setter.
http://jsfiddle.net/fmpeyton/329ntgcL/
I am running into the following error:
Uncaught TypeError: Invalid property. A property cannot both have accessors and be writable or have a value, #<Object>
Why am I getting this error and what would be the best approach to have a default value and a getter/setter for a property, using Object.defineProperty()?
Use a function scoped variable to back the defined property and set that variable's initial value to the default:
function LivingThing(){
self = this;
var isAlive = true;
Object.defineProperty(self, 'isAlive', {
get: function(){
return isAlive;
},
set: function(newValue){
isAlive = newValue;
},
configurable: true
});
self.kill = function(){
self.isAlive = false;
};
}
http://jsfiddle.net/329ntgcL/5/
writable isn't necessary because you have a setter. That's what's causing your error. You can either have value/writable (data descriptor) OR get/set (accessor descriptor).
As a result, when you call var l = new LivingThing, l.isAlive == true and after you call l.kill(), l.isAlive == false
Your getter/setter pair presents a computed property, you can (and should) back that up with a real property if you need storage:
self._isAlive = true;
Object.defineProperty(self, 'isAlive', {
get: function(){
return this._isAlive;
},
set: function(newValue){
this._isAlive = newValue;
},
writable: true,
configurable: true
});
Presumably, put some logic there to justify a setter/getter vs a regular property. Setters are for wrapping property access.
Without a setter and getter, you can define a default value by just assigning it to the prototype:
LivingThing.prototype.isAlive = true
var o = new LivingThing()
console.log(o.isAlive) // true
Just to be clear, changing that property on an instance of LivingThing will create a property for that instance, not change the value in its __proto__.
o.isAlive = false
console.log(new LivingThing().isAlive) // still true

JS non-enumerable function

I'm trying to define a non-enumerable toJSON function on a prototype object without much luck. I'm hoping for something similar to ECMAScript 5 toJSON:
Object.defineProperty(obj, prop, { enumerable: false });
However this defines it as a property which cannot be accessed as a method.
[EDIT: Nick is wrong; it can be accessed as a method. His mistake was in code that is not shown in this question - see his comments on answers below, for details.]
I was hoping to be able to define the function in a non-enumerable fashion, as I was planning to define in the prototypes of all primitive types (String, Number, Boolean, Array, and Object), so that I can recursively apply the function through complex objects.
The end goal here is to be able JSONify a Backbone model/collection with nested collections recursively.
I guess in total I have two main questions:
Is it possible to define a non-enumerable function on a prototype? If so how?
Is there a better way to JSONify nested Backbone models?
I don't get it, why can't you access it as a method?
var foo = {};
Object.defineProperty(foo, 'bar', {
enumerable: false,
value: function () {console.log('foo.bar\'d!');}
});
foo.bar(); // foo.bar'd!
If you wanted it on the prototype, it's as easy as
Object.defineProperty(foo.prototype, /* etc */);
or even directly in Object.create
foo.prototype = Object.create(null, {
'bar': {value: function () {/* ... */}}
});
However, unless you're creating instances of foo, it won't show up if you try to foo.bar, and only be visible as foo.prototype.bar.
If foo has it's own prototype (e.g. foo = Object.create({})), you can get it with Object.getPrototypeOf, add the property to that and then foo.bar would work even if it is not an instance.
var proto = Object.getPrototypeOf(foo); // get prototype
Object.defineProperty(proto, /* etc */);
You can see visibility of enumerable vs non-enumerable properties here.
Paul S. is right about needing to set the property definition's value instead of a get, but I wanted to add that you don't need to pass enumerable: false, because false is the default for that option in Object.defineProperty() The answer can be simplified to:
var foo = {};
Object.defineProperty(foo, 'bar', {
value: function(){ console.log('calling bar!'); }
});
foo.bar();
Always you can avoid enumerable functions properties in object when you looping through it. And instead of define property in each object and set enumerable to false , you can create function which will call to any object with the property you want and put a condition to not take the property in the looping list. here is the example :
const obj = {
name: "myName",
title: "developer"
}
function prop() {
this.loop = function(i) {
for (i in this) {
if (typeof(this[i]) == "function") {
continue;
} else {
console.log(this[i]);
}
}
}
}
prop.call(obj);
obj.loop();
output >> myName, developer

JavaScript property & field reflection

I came across this little snippet of code for property reflection in JavaScript:
function GetProperties(obj) {
var result = [];
for (var prop in obj) {
if (typeof obj[prop] !== "function") {
result.push(prop);
}
}
return result;
}
I've tested it using the following "CustomObject":
var CustomObject = (function () {
function CustomObject() {
this.message = "Hello World";
this.id = 1234;
}
Object.defineProperty(CustomObject.prototype, "Foo", {
get: function () {
return "foo";
},
enumerable: true,
configurable: true
});
Object.defineProperty(CustomObject.prototype, "Bar", {
get: function () {
return "bar";
},
enumerable: true,
configurable: true
});
return CustomObject;
})();
Here is a little test using jQuery:
$(document).ready(function () {
console.log(GetProperties(new CustomObject()));
});
Here are the results:
["message", "id", "Foo", "Bar"]
I understand that the GetProperties function just returns an array of anything in the input object that is not a function, but I want to filter the results to get only the "real" properties, so my output should be:
["Foo", "Bar"]
Is this possible?
Also, can I do the opposite and just return the fields?
There are two things you could do (and possibly more, it depends on your exact situation):
Name "private" properties differently, e.g. with a trailing underscore and check whether the property name ends with an underscore when you are iterating over the properties (and exclude them).
If by "real properties" you mean the properties defined on the prototype and you want to ignore all properties defined on the object itself, you can use .hasOwnPrototype to check where it is defined. Alternatively, you could use Object.getPrototypeOf and iterate over the properties of the prototype only.
Bad code. I'm leaving (with a comment) it 'cause the subsequent discussion
might help somebody else.
If you always use defineProperty() to get non enumerable properties, then this would work:
function GetProperties(obj) {
var result = [];
for (var prop in obj) {
// propertyIsEnumerable() returns false just because the properties
// are inherited thru the prototype chain. It was just a coincidence
// that it got the desired result. Don't do this.
if (typeof obj[prop] !== "function" && !obj.propertyIsEnumerable(prop)) {
result.push(prop);
}
}
return result;
}
Otherwise, I'd be curious to know a general solution to the problem.
EDIT: I see that the code has enumerable: true and still my code does exactly what was asked. Double you tee ef?

Why is Object.create() so verbose?

Object.create is a great addition to JavaScript, because it adheres more to the prototypical nature of JS. However, I can't help but find the syntax of the 2nd parameter to the function to be too verbose, and a step back.
For example, if I want to create an object, and specify a new property in the derived object, I need to include that property value within a property object, regardless if I'm interested in the extra features or not.
So, something as simple as this:
o = Object.create({}, { p: 42 })
Now becomes:
o = Object.create({}, { p: { value: 42 } })
Obviously this is a simple example, but to me the verbosity is unnecessary, and should be optional.
Does anyone understand the decision to require a properties object? What is your opinion of the requirement of the new syntax?
Note: I understand there are easy solutions to overcome this requirement.
The syntax is done this way so that you can add parameters that control each property:
So, when you do this:
o = Object.create({}, { p: { value: 42 } })
you are saying that you want a property named p with a value of 42. The key here is that there are other parameters you can set for each property and if there wasn't this extra level of object hierarchy, you wouldn't have any way to pass those extra parameters.
So, for example, you could also do this:
o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
Here's you are not just specifying the value of 42, but also some options for that property. If there wasn't the extra level of object hierarchy here, then you wouldn't have a place to put those extra options.
Yes, it does seem inconvenient when you only want the simple case. But, you could easily write yourself a helper function that made the simpler syntax work:
function myCreate(proto, props, enumerable, writable, configurable) {
// last three arguments are optional - default them to true if not present
if (typeof enumerable === "undefined") {enumerable = true;}
if (typeof writable === "undefined") {writable = true;}
if (typeof configurable === "undefined") {configurable = true;}
var wrapper = {};
for (var i in props) {
wrapper[i] = {
value: props[i],
enumerable: enumerable,
configurable: configurable,
writable: writable
};
}
return(Object.create(proto, wrapper));
}
Demo here: http://jsfiddle.net/jfriend00/vVjRA/

Categories

Resources