Defining Javascript's object property by invoking function - javascript

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.

Related

adding a prototype property by Object.defineProperty override the constructor property

function Test(){
this.time='pat';
}
Object.defineProperty(Test.prototype, 'time', {
configurable: true,
get: function () { return 'test'; }
});
var a=new Test();
console.log(a.time); //test
why a.time return value is 'test',I'm so confused with it.
Since time is not a simple property, an assignment to it uses the property definition on the prototype. Since the property on the prototype is defined with a getter but not setter, it cannot be assigned to. If you were in strict mode (generally a good idea), you'd get an error about it.
The constructor can create an "own" time property on the instance, just not with simple assignment; it has to use defineProperty to do it:
function Test(){
Object.defineProperty(this, "time", {
writable: true,
enumerable: true,
configurable: true,
value: 'pat'
});
}
Object.defineProperty(Test.prototype, 'time', {
configurable: true,
get: function () { return 'test'; }
});
var a=new Test();
console.log(a.hasOwnProperty("time")); // true
console.log(a.time); //pat
a.time = "updated";
console.log(a.time); //updated
Because that's what you defined your get method to return. Whatever value you have the function in get return is the value it resolves to.
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.defineProperty(Test.prototype, 'time', {
configurable: true,
get: function () { return 'A new value!'; }
});
var a = new Test();
console.log(a.time); //A new value!
If you're wondering why the console.log outputs the new value as opposed to the one originally defined, well, that's because you set Object.defineProperty to reconfigure Test.prototype.time to be a new value which made new instance of it also a new value.
You could test this for yourself:
function Test(){
this.time='pat';
}
// This instance will have the old values
var b = new Test();
// Change value returned by Test.time by new Test instances
Object.defineProperty(Test.prototype, 'time', {
configurable: true,
get: function () { return 'A new value!'; }
});
var a = new Test();
console.log(a.time); // A new value!
consoel.log(b.time); // pat
When you instantiated b you gave you made an instance with the original definitions. However, when you instantialized a you made an instance with the new definitions of time which is why it's different.

JavaScript Extend Class with hidden functions

I'm having this issue today:
I'd like to extend a JavaScript Object class with some useful functions, so I wrote something like this:
Object.prototype.fn = function() {
// Do some cool stuff...
};
Then I use another function to do a loop like this:
for( var i in obj ) {
this[i] = obj[i]; // 'this' --> is another Object
console.log( i, obj[i] );
}
So I moved stuff to another object from 'obj'. However, as you can see in logs, 'fn' function is now either in my extended object and in it's __proto__. All I want is to avoid this without using this in my 'for' loop:
if( typeof obj[i] !== 'function' ) // Then do it
So I was wondering how native functions (like toString() for example) are implemented to stay invisible outside the prototype. Any suggestions?
You can use a method called defineProperty to set your property and have it not be enumerable (or as you said, stay invisible):
var obj = {};
Object.defineProperty(obj, 'test', {
enumerable: false,
configurable: false,
writable: true,
value: 'hello'
});
your property obj.test exists (value is hello) but now wont be enumerable, so doing something like:
for(var i in obj) console.log(i);
// OR
Object.keys(obj);
will not print 'test', BUT you can still access it as a property (obj.test)

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

Javascript getters and setters for object, not object properties

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.

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