Object.defineProperty for IE - javascript

var o = {}; // Creates a new object
Example of an object property added with defineProperty with a data property descriptor
Object.defineProperty(o, "a", {value : 37,
writable : true,
enumerable : true,
configurable : true});
How to implement for IE7-8?(with attributes writable,enumerable,configurable)

if (!Object.defineProperty) {
Object.defineProperty = function (obj, prop, descriptor) {
if (arguments.length < 3) { // all arguments required
throw new TypeError("Arguments not optional");
}
prop += ""; // convert prop to string
...

Related

Javascript: how to assign setter to object via `=` sign?

Is there way to assign setter/getter to existing object via = sign?
window.somesetter = set(v) {};
window.somesetter = function set(v) {};
window.somesetter = set settername(v) {};
No, there isn't.
If you want to assign a setter or getter to an existing object, use defineProperty.
Object.defineProperty(window, 'setter', {
set: function(value) { window.test = value }
});
window.setter = "Hello";
console.log(window.test);
I'd say no. But the answer is "kinda yes" if you use a proxy instead of the object itself.
But this is black magic and by all means is NOT recommended for production :)
const winProxy = new Proxy(window, {
set(target, name, value) {
if (typeof value === 'function' && {set: 1, get: 1}[value.name]) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, name) || {
enumerable: false,
configurable: true,
}
descriptor[value.name] = value
Reflect.defineProperty(target, name, descriptor)
} else {
Reflect.set(target, name, value)
}
}
})
winProxy.someProp = function set(v) {
console.log('setter', v);
}
winProxy.someProp = function get() {
console.log('getter')
}
window.someProp = 1;
window.someProp
Try this form:
window.somesetter = function (v) {};

How do i iterate over an object which used getter in javascript

Here's the object defination:
var Vars = new function(){
var that = this;
this.assign = function(name) {
var realValue = undefined;
Object.defineProperty(that, name, {
configurable: true,
get: function() {
//console.log("Get"); do something...
return realValue;
},
set: function(v) {
//console.log("Set"); do something...
realValue = v;
}
});
}
this.destroy = function(name) {
return delete that[name];
}
};
But i found i cannot iterate over this object by the way i want.
>> Vars.assign("key")
<- undefined
>> Vars.key = 1
<- 1
>> Vars.key
<- 1
>> for(var i in Vars){console.log(i);}
assign
destroy
<- undefined
How could i reach "key" when i iterate over the object?
You have to state explicitly at the property descriptor that your property is enumerable. The default value is false. This is the reason why you don't get it when you use the for..in. According to MDN
The for...in statement iterates over the enumerable properties of an
object, in arbitrary order. For each distinct property, statements can
be executed.
Regarding the enumerable property, as it is stated here:
enumerable
true if and only if this property shows up during
enumeration of the properties on the corresponding object. Defaults to
false.
var Vars = new function(){
var that = this;
this.assign = function(name) {
var realValue = undefined;
Object.defineProperty(that, name, {
configurable: true,
// This is the missing line
enumerable: true,
get: function() {
//console.log("Get"); do something...
return realValue;
},
set: function(v) {
//console.log("Set"); do something...
realValue = v;
}
});
}
this.destroy = function(name) {
return delete that.Local[name];
}
};
Vars.assign("key");
for(var i in Vars){console.log(i);}

Not understand the result hasOwnProperty

Why true? How method foo written in the object?
Object.prototype.foo = function(obj) {
for(var i in obj) this[i] = obj[i];
};
var obj = {a: 1};
var testObj = {};
testObj.foo(obj)
alert( testObj.hasOwnProperty("foo") );
A for ... in loop iterates through all of the visible properties of the target objects, including those on its prototype chain.
If you only want to transfer the "own" properties of the source object, you can add a test:
Object.prototype.foo = function(obj) {
for(var i in obj)
if (obj.hasOwnProperty(i))
this[i] = obj[i];
};
When you use testObj.foo(obj), the foo method is called with the this value set to testObj.
Therefore, this code...
for(var i in obj) this[i] = obj[i];
...adds the enumerable (own or inherited) properties of obj as own properties of this (testObj in this case).
And foo is an enumerable (inherited) property of obj, so it is added to testObj.
Object.prototype.foo = function(obj) {
for(var i in obj) this[i] = obj[i];
};
var obj = {a: 1};
var testObj = {};
alert( testObj.hasOwnProperty("foo") ); // false
testObj.foo(obj)
alert( testObj.hasOwnProperty("foo") ); // true
As you can see the foo method is attached to the object after foo is called.
Why? Because for..in will iterate through all keys which are enumerable, regardless if they live on the object or on one of its prototypes.
Object.prototype.foo = function(obj) {
for ( var i in obj ) {
console.log(i, '=', obj.hasOwnProperty(i)); // foo=false
}
};
The 'foo' method is enumerable, you can check using
Object.prototype.propertyIsEnumerable("foo") // true
How do you make a property non-enumerable?
Object.defineProperty(Object.prototype, 'foo', {
'configurable': true, // can be removed using 'delete' operator
'enumerable': false, // will not show up in a for..in iteration
'writable': true, // can be overridden
'value': function () {
for ( var key in this ) {
console.log(key, '=', this.hasOwnProperty(key));
}
}
});
Just in case you were wondering:
Object.prototype.propertyIsEnumerable("hasOwnProperty") // false
Object.prototype.propertyIsEnumerable("propertyIsEnumerable") // false

Do JavaScript property descriptors support custom attributes?

I would like to define a JavaScript property using a property descriptor that has custom attributes, in other words, attributes other than the standard value, writable, etc...
In the example below I have defined a property with a property descriptor that has the custom attribute customAttr. The call to Object.defineProperty works fine but later when I try to loop over the attributes of the property descriptor, my custom attribute is not listed.
Is what I am trying to do possible?
const o = {}
Object.defineProperty(o, 'newDataProperty', {
value: 101,
writable: true,
enumerable: true,
configurable: true,
customAttr: 1,
})
const desc = Object.getOwnPropertyDescriptor(o, 'newDataProperty')
// List the descriptor attributes.
for (const prop in desc) {
console.log(`${prop}: ${desc[prop]}`)
}
// PROBLEM: `customAttr` is not listed
No, it's not possible. This is what Object.defineProperty does:
...
3. Let desc be the result of calling ToPropertyDescriptor with Attributes as the argument.
4. Call the [[DefineOwnProperty]] internal method of O with arguments name, desc, and true.
5. Return O.
And in short, ToPropertyDescriptor simply ignores anything that's not "enumerable", "writable", "configurable", "value", "get" or "set":
...
Let desc be the result of creating a new Property Descriptor that initially has no fields.
If the result of calling the [[HasProperty]] internal method of Obj with argument "enumerable" is true, then
...
(repeat step 3 for other valid descriptor properties)
10. Return desc.
Resurrecting an old post here, but I found the idea interesting. You can extract the fact that functions are objects in javascript, and use the get function as the attribute holder :
function setPropertyAttribute(obj, propertyName, attributeName, attributeValue) {
var descriptor = getCustomPropertyDescriptor(obj, propertyName);
descriptor.get.$custom[attributeName] = attributeValue;
}
function getPropertyAttributes(obj, propertyName) {
var descriptor = getCustomPropertyDescriptor(obj, propertyName);
return descriptor.get.$custom;
}
function getPropertyAttribute(obj, propertyName, attributeName) {
return getPropertyAttributes(obj, propertyName)[attributeName];
}
function getCustomPropertyDescriptor(obj, prop) {
var actualDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
if (actualDescriptor && actualDescriptor.get && actualDescriptor.get.$custom) {
return actualDescriptor;
}
var value = obj[prop];
var descriptor = {
get: function() {
return value;
},
set: function(newValue) {
value = newValue;
}
}
descriptor.get.$custom = {};
Object.defineProperty(obj, prop, descriptor);
return Object.getOwnPropertyDescriptor(obj, prop);
}
Then :
var obj = {
text: 'value',
number: 256
}
setPropertyAttribute(obj, 'text', 'myAttribute', 'myAttributeValue');
var attrValue = getPropertyAttribute(obj, 'text', 'myAttribute'); //'myAttributeValue'
fiddle here.

Defining read-only properties in JavaScript

Given an object obj, I would like to define a read-only property 'prop' and set its value to val. Is this the proper way to do that?
Object.defineProperty( obj, 'prop', {
get: function () {
return val;
}
});
The result should be (for val = 'test'):
obj.prop; // 'test'
obj.prop = 'changed';
obj.prop; // still 'test' since it's read-only
This method works btw: http://jsfiddle.net/GHMjN/
I'm just unsure if this is the easiest / smoothest / most proper way to do it...
You could instead use the writable property of the property descriptor, which prevents the need for a get accessor:
var obj = {};
Object.defineProperty(obj, "prop", {
value: "test",
writable: false
});
As mentioned in the comments, the writable option defaults to false so you can omit it in this case:
Object.defineProperty(obj, "prop", {
value: "test"
});
This is ECMAScript 5 so won't work in older browsers.
In new browsers or node.js it is possible to use Proxy to create read-only object.
var obj = {
prop: 'test'
}
obj = new Proxy(obj ,{
setProperty: function(target, key, value){
if(target.hasOwnProperty(key))
return target[key];
return target[key] = value;
},
get: function(target, key){
return target[key];
},
set: function(target, key, value){
return this.setProperty(target, key, value);
},
defineProperty: function (target, key, desc) {
return this.setProperty(target, key, desc.value);
},
deleteProperty: function(target, key) {
return false;
}
});
You can still assign new properties to that object, and they would be read-only as well.
Example
obj.prop
// > 'test'
obj.prop = 'changed';
obj.prop
// > 'test'
// New value
obj.myValue = 'foo';
obj.myValue = 'bar';
obj.myValue
// > 'foo'
In my case I needed an object where we can set its properties only once.
So I made it throw an error when somebody tries to change already set value.
class SetOnlyOnce {
#innerObj = {}; // private field, not accessible from outside
getCurrentPropertyName(){
const stack = new Error().stack; // probably not really performant method
const name = stack.match(/\[as (\w+)\]/)[1];
return name;
}
getValue(){
const key = this.getCurrentPropertyName();
if(this.#innerObj[key] === undefined){
throw new Error('No global param value set for property: ' + key);
}
return this.#innerObj[key];
}
setValue(value){
const key = this.getCurrentPropertyName();
if(this.#innerObj[key] !== undefined){
throw new Error('Changing global parameters is prohibited, as it easily leads to errors: ' + key)
}
this.#innerObj[key] = value;
}
}
class GlobalParams extends SetOnlyOnce {
get couchbaseBucket() { return this.getValue()}
set couchbaseBucket(value){ this.setValue(value)}
get elasticIndex() { return this.getValue()}
set elasticIndex(value){ this.setValue(value)}
}
const _globalParams = new GlobalParams();
_globalParams.couchbaseBucket = 'some-bucket';
_globalParams.elasticIndex = 'some-index';
console.log(_globalParams.couchbaseBucket)
console.log(_globalParams.elasticIndex)
_globalParams.elasticIndex = 'another-index'; // ERROR is thrown here
console.log(_globalParams.elasticIndex)
Because of the old browsers (backwards compatibility) I had to come up with accessor functions for properties. I made it part of bob.js:
var obj = { };
//declare read-only property.
bob.prop.namedProp(obj, 'name', 'Bob', true);
//declare read-write property.
bob.prop.namedProp(obj, 'age', 1);
//get values of properties.
console.log(bob.string.formatString('{0} is {1} years old.', obj.get_name(), obj.get_age()));
//set value of read-write property.
obj.set_age(2);
console.log(bob.string.formatString('Now {0} is {1} years old.', obj.get_name(), obj.get_age()));
//cannot set read-only property of obj. Next line would throw an error.
// obj.set_name('Rob');
//Output:
//========
// Bob is 1 years old.
// Now Bob is 2 years old.
I hope it helps.
I tried and it Works ...
element.readOnly = "readOnly" (then .readonly-> true)
element.readOnly = "" (then .readonly-> false)

Categories