Redefine Object.defineProperty in Javascript - javascript

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/

Related

Determine if a function is accessed or invoked in the proxy handler

I have a proxy handler like this:
let handler = {
get: function(target, name) {
let member = target[name];
if (typeof member === 'function') {
return function() {
//
}
}
return member;
}
}
Whenever a method is called on Proxy object:
var obj = {
foo: function() {
//
}
}
var p = new Proxy(obj, handler);
p.foo();
...It invokes the function that was returned from the handler's getter. But the problem is, when the method is accessed rather than invoked like this:
p.foo;
The entire definition of the function is returned.
Is there any way by which I could check to see if the method is being accessed (p.foo) or invoked (p.foo())? I am trying to achieve something like:
if (typeof member === 'function' && member.isBeingCalled()) {
return function() {
//
}
}
Also, in case of p.foo, I would like to return the definition of member instead of the function that handler's getter returns.
Yes, this is possible but in order to make it work you need to consider what happens when call a method
o.foo()
as there is more going on than you might think.
The first operation is a property lookup that retrieves the value of the property having the key "foo" from the object o. Now, the value retrieved is itself an object, specifically a function object.
The second operation is the invocation of that function as a method by way of
function.prototype.apply. What this does is invoke the function with its this value bound to o.
Therefore, in order to achieve the desired behavior, one must proxy the value of the property o.foo rather than o itself. your proxy will use the apply trap to intercept and modify the invocation of the function what is the value of that property. This is described well by MDN.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply
Essentially you would write something like
const foo = o.foo;
const proxiedFoo = new Proxy(foo, {
apply(target, thisArg, argumentsList) {
if (thisArg) {
// this function is being invoked as a method
}
}
});
o.foo = proxiedFoo;

JavaScript Object.defineProperty doesn't work as expect

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.

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

Object doesn't support property or method '__defineGetter__'

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
}
}

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