I mistakenly used Object.defineProperty by pass a function as its descriptor param, just like blow code:
let fakeDesc = () => {}
let obj = {
method1: function() {
console.log('this is method1');
}
};
Object.defineProperty(obj, 'method1', fakeDesc);
obj.method1();
the code evaluated result is that method1 is not overrided.
according to the MDN doc(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)
If a descriptor has neither of value, writable, get and set keys, it is treated as a data descriptor.
so I think that the fakeDesc should be treated like a data descriptor, and the default value property should be undefined, so the above code would result in obj.method1 equal undefined.
How about this situation? and is there some document can explain this issue?
The passed descriptor (the third argument) is supposed to be a plain object with properties like value and writable. If you pass a function instead of an object, it gets ignored.
A data descriptor is a property that has a value, which may or may not be writable.
If you pass a function, you're not passing a data descriptor.
If you pass an object with a value property of your fakeDesc, it'll work as expected:
let fakeDesc = () => {}
let obj = {
method1: function() {
console.log('this is method1');
}
};
Object.defineProperty(obj, 'method1', { value: fakeDesc });
obj.method1();
When you pass an object without a value property, the underlying value won't change, although the descriptor on that property may change. For example, the following changes enumerable to false on method1, despite not changing the underlying value (of the this is method1 function):
let obj = {
method1: function() {
console.log('this is method1');
}
};
console.log(Object.getOwnPropertyDescriptor(obj, 'method1'));
Object.defineProperty(obj, 'method1', { enumerable: false });
console.log(Object.getOwnPropertyDescriptor(obj, 'method1'));
I suppose you could technically pass a function and have it change the descriptor, but you would have to set a property directly on the function, which is really weird:
const fakeFn = () => {};
fakeFn.enumerable = false;
let obj = {
method1: function() {
console.log('this is method1');
}
};
console.log(Object.getOwnPropertyDescriptor(obj, 'method1'));
Object.defineProperty(obj, 'method1', fakeFn);
console.log(Object.getOwnPropertyDescriptor(obj, 'method1'));
obj.method1();
When MDN says that the default values for the different keys (like configurable and enumerable) are false, and the default value for value is false, it's referring to the process when you create a property that doesn't exist on the object already. The process is defined in DefineOwnProperty.
Related
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
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
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
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.
I would like to learn if it is possible to redefine the "definePropery" function of the Object(.prototype) in a subclass(.prototype) so that the setter not only sets the value of the property but also eg executes an additional method.
I have tried something like that:
Myclass.prototype.defineProperty = function (obj, prop, meth) {
Object.defineProperty.call(this, obj, prop, {
get: function () {
return obj[prop]
},
set: function () {
obj[prop] = n;
alert("dev")
}
})
}
But id does not work
You appear to be confused about how Object.defineProperty works. You only ever call defineProperty on Object, and you pass in the object you want to define a property of. So you don't need this magic, because defineProperty does not exist on instances of your class at all.
var obj = {a:123};
Object.defineProperty(obj, 'foo');
// works fine!
obj.defineProperty('foo');
// TypeError: Object #<Object> has no method 'defineProperty'
But that means you can add that in, no problem:
Myclass.prototype.defineProperty = function (obj, prop, meth) {
Object.defineProperty(this, prop, {
get: function () {
return obj[prop]
},
set: function (n) {
obj[prop] = n;
alert("dev")
}
})
}
Note this line:
Object.defineProperty(this, prop, {
We pass in the object we want the property on (this), and the name of the property (prop). Then the setter/getter object. No need to override anything at all. You are simple providing a method that allow an object to declare it's own properties.
See working example here: http://jsfiddle.net/TeZ82/