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
Related
So I am reading up on JS with Programming JS applications and JS the goodparts. Reading through objects and functions.. which are apparently the same. According to Crockford himself (and practically everybody). But then I ask: if they are the same then why if I have an object:
var myObject = {
value: 0,
increment: function (inc) {
this.value += inc;
}
};
I can add a function like so:
myObject.double = function () {
this.value += this.value;
}
But when I have a constructor (a function..) :
var myConstructor = function() {
this.value = 0;
this.increment = function(inc){
this.value += inc;
};
};
I cannot add a function like this:
myConstructor.double = function(){
this.value += this.value;
};
But only over the prototype like this:
myConstructor.prototype.double = function(){
this.value += this.value;
};
Now how can anybody say that object and function are the same??
Interestingly, the assignment of the "double" method to the constructor does not give an error, only when you create an object afterwards with new and call the double method on this object does it give: "TypeError can not find".
Also, console.log on the constructor gives:
{ [Function] double: [Function] }
Notice the missing comma between "[Function]" and "double" by the way.. But this probably shows that really functions are actually objects because we just added the double property (a function) to this object.
But then the question becomes... why can I call new on myConstructor and not on myObject ?? How does JS distinguish between them? If they are both objects which can consists of properties, values and functions..?
EDIT: ok, so I get they are not the same, sorry for my imprecise wording. But one question remains: how does JS know the difference between an object with a function property and a constructor to which properties have been added?
var object = {
property : "value",
method: function(){
}
};
console.log(object) outputs to:
{ property: 'asdf', method: [Function] }
and
var myConstructor = function() {
this.property = "value";
this.method = function(inc){
};
};
myConstructor.property = "value";
console.log(myConstructor) outputs to:
{ [Function] property: 'value' }
So if an object has an anonymous function JS knows its a constructor? Or does it know that over the prototype maybe?
How does JS know the difference between an object with a function
property and a constructor to which properties have been added?
There are different terminologies:
Functions are objects which are Function instances.
Callable objects are objects with an internal [[Call]] method.
Constructors are objects with an internal [[Construct]] method.
Usually they coincide, so it's usual to call them all functions.
Then it's easy:
If you attempt to call an object and it has a [[Call]] method, that method will be called. Otherwise it's not callable, so error.
If you attempt to instantiate an object and it has a [[Construct]] method, that method will be called. Otherwise it's not a constructor, so error.
Moreover, there is the [[Class]] internal property, which for functions is usually "Function".
I've noticed that when using bind on an object, you lose the ability to access the prototype.
function Foo(obj) {
this.fields = obj;
}
function Make(obj) {
return Foo.bind(Foo, obj);
}
var Test = Make({
name: 'Jeff'
});
console.log(Test.prototype);
Here's the same example not using bind:
function Foo(obj) {
this.fields = obj;
}
function Make(obj) {
return Foo;
}
var Test = Make({
name: 'Jeff'
});
console.log(Test.prototype);
Are there any ways around this?
So judging by the docs. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Bind ties a function to a specific object so that variables inside that object are always used. so when you're calling console.log in your example you're logging the prototype of the function Test. Not the object Test. So you get undefined.
In order to get the object you want try this:
console.log(new Test())
I am using arbor js . But when i run ie8,9 and 10 browser it show on error Object doesn't support property or method __defineGetter__ . how can i fix this problem.
__defineGetter__ is an old, proprietary way of defining property "getter" functions. If the library you're using relies on it, you can't use it on JavaScript engines that don't support it.
The modern, standard way of defining getter functions for properties doesn't use that syntax. Perhaps there's a more up-to-date version of the library you could use.
There are two/three current standard ways, and another coming:
Using get and set functions on an object initializer:
var obj = {
get foo() {
return /*...foo value...*/;
},
set foo(v) {
// Set foo value
}
};
...or using Object.defineProperty:
var obj = {};
Object.defineProperty(obj, "foo", {
get: function() {
return /*...foo value...*/;
},
set: function(v) {
// Set foo value
}
};
...or using Object.defineProperties (or the second argument to Object.create, which accepts the same thing):
var obj = {};
Object.defineProperties(obj, {
foo: {
get: function() {
return /*...foo value...*/;
},
set: function(v) {
// Set foo value
}
}
});
For objects created via constructors, ES6 will also add class:
class ThingWithFoo {
get foo() {
return /*...foo value...*/;
}
set foo(v) {
// Set foo value
}
}
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/
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.