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);}
Related
Is it possible to create an object that can be used like an array and an object?
The closest I have gotten so far is below which just allows you to assign a value using either an array key or object property:
var ALO = {
db: {}
};
Object.defineProperty(ALO, 'length', {
get: function() {
var count = 0;
for (var prop in ALO.db) {
if (ALO.db.hasOwnProperty(prop)) count++;
}
return count;
},
enumerable: false
});
function add(baseObj, obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (!isNaN(prop)) {
//create a default string begining with i
baseObj.db["i" + prop] = obj[prop];
} else {
baseObj.db[prop] = obj[prop];
}
Object.defineProperty(baseObj, prop, {
get: function() {
return baseObj.db[prop];
},
set: function(newValue) {
baseObj.db[prop] = newValue;
},
enumerable: false,
configurable: true
});
Object.defineProperty(baseObj, baseObj.length - 1, {
get: function() {
return baseObj.db[prop];
},
set: function(newValue) {
baseObj.db[prop] = newValue;
},
enumerable: false,
configurable: true
});
}
}
}
add(ALO, {
"myprop": "myvalue"
});
document.write(ALO["myprop"] + "<br>");
ALO[0] = "mynewvalue";
document.write(ALO["myprop"] + "<br>");
ALO["myprop"] = "mynewervalue";
document.write(ALO["myprop"]);
I would like to be able to automatically create what is needed when assigning a non existing item e.g.
ALO[1] = "create and assign this string";
or
ALO["somenonexistingprop"] = "another string";
I believe you will find that normal Arrays in Javascript inherit from Object, which means you can abuse them like normal objects if you want.
let MyArray = [1, 2, 3];
console.log('let MyArray = [1, 2, 3];');
console.log('Typeof MyArray = ', typeof(MyArray));
console.log('Wait.....');
console.log('So can I just assign stuff to an array like an object?');
MyArray['key'] = 'value';
console.log(`MyArray.key = 'value';`);
console.log('console.log(MyArray.key) =>', JSON.stringify(MyArray.key));
console.log('Keys:', JSON.stringify(Object.keys(MyArray)));
console.log('Array Length:', MyArray.length);
console.log('TADA, the standard browser Array does what you want');
There are probably reasons not to do this other than it being very confusing, but I believe it would fulfill your requirements, and requires no code!
EDIT: After reading some of the comments below the fold, it seems the OP for some reason wants to access properties added in Object mode as array members. If you need that, you can just use Object.keys(MyArray) instead of MyArray as demonstrated in the last lines of the console output above.
I have all non - enumerable properties in object, I want to clone that object.
My problem non-enumerable properties are not getting cloned.
Take below example
Object.defineProperty(this, 'prop', {
get: function () {
return prop;
},
set: function (value) {
prop= value;
}
});
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
For example I have above two properties in my object doing clone using following methods, my properties are not getting cloned I believe it is because they are non - enumerable.
var newObject = $.extend({},oldObject);
var newObject= Object.assign({},oldobject);
How do I copy non-enumerable properties in javascript.
If one or more properties aren't enumerable, how do you want to auto-magically enumerate them?
Since you know their names, you should do something like this:
var sourceObj = this;
var targetObj = {};
["prop", "otherProperty"].forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Or you can build the whole property name array whenever you define a non-enumerable property:
var propertyNames = [];
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop"); // <---
Object.defineProperty(this, 'newprop2', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop2"); // <---
propertyNames.forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Alternate approach: Object.getOwnPropertyNames
The Object.getOwnPropertyNames() method returns an array of all
properties (enumerable or not) found directly upon a given object.
Maybe this is the best approach. Object.getOwnPropertyNames gets the name of own object's properties either if they're enumerable or not. That is, you can avoid building the whole propertyNames array, and this approach should be fine with you because you said that all properties aren't enumerable:
var sourceObj = this;
var targetObj = {};
Object.getOwnPropertyNames(sourceObj).forEach(function(property) {
targetObj[property] = sourceObj[property];
});
You can use Object.defineProperties and Object.getOwnPropertyDescriptors() function
The Object.defineProperties() method defines new or modifies existing properties directly on an object, returning the object.
The Object.getOwnPropertyDescriptors() method returns all own property descriptors of a given object.
So, you can add the properties of the original object into an empty object, as follow
var source_obj = this
var cloned_obj = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source_obj));
I have a similar question in How to implement Ruby's extend module in JavaScript.
And because I don't wanna use iterate attach each method to new object, so the final best way I can do is like that, welcome give me any idea and suggestion, thanks.
class Test {
constructor(name) {
this.name = name;
}
test1(){
return this.name + "test1"
}
}
const Other = {
test2: function(){
return this.name + "test2"
},
test3: function(){
return this.name + "test3"
},
test4: function(){
return this.name + "test4"
}
}
var person = new Test("HKE")
Object.assign(person,Other) //this like object.extend(Module) in ruby
console.log(person.test1()) // HKEtest1
console.log(person.test2()) // HKEtest2
console.log(person.test3()) // HKEtest3
console.log(person.test4()) // HKEtest4
var house = new Test("Tsao")
console.log(house.test1())
console.log(house.test2()) // TypeError: house.test2 is not a function
console.log(house.test3()) // TypeError: house.test3 is not a function
console.log(house.test4()) // TypeError: house.test4 is not a function
In the code below I have 2 properties:
sharedProperty : This has a primitive type as value and is being set to just configurable.
sharedMethodAsProperty: This has a function as a value and is also being set to configurable.
Now, near the end of snippet I can override sharedProperty just fine(with it being readonly and configurable), however for sharedMethodAsProperty I have to set it to writable else I get complaints that a readonly property can't be overridden. Ideas?
(function () {
'use strict';
var Person = function () {
Object.defineProperties(Person.prototype, {
"sharedProperty" : {
value : 10,
configurable: true
},
"sharedPropertyThroughAccessor" : {
get : function() {
return "shared property";
},
configurable: true
},
"sharedMethodAsProperty" : {
value: function() {
return "shared method as property";
},
configurable: true,
// if we omit this true here, we can't override it below.
//writable: true
}
});
};
Object.prototype.sharedMethod = function() {
return "shared method";
};
var person1 = new Person("John", "Doe");
var man = Object.create(person1);
var sharedProperty = Object.getOwnPropertyDescriptor(Person.prototype, "sharedProperty").value;
Object.defineProperty(man, "sharedProperty", {
value : 11 + sharedProperty,
configurable: true
});
var sharedPropertyThroughAccessor = Object.getOwnPropertyDescriptor(Person.prototype, "sharedPropertyThroughAccessor");
// bind with man, else you'd get person1's properties
var sharedFn = sharedPropertyThroughAccessor.get.bind(man);
Object.defineProperty(man, "sharedPropertyThroughAccessor", {
get : function() {
return sharedFn() + " overridden";
}
});
var sharedMethodFn = person1.sharedMethod.bind(man);
// can't do: man.prototype. That property only exists on functions.
man.sharedMethod = function() {
return sharedMethodFn() + " overridden";
};
var sharedMethodAsProperty = Object.getOwnPropertyDescriptor(Person.prototype, "sharedMethodAsProperty");
var sharedMethodAsPropertyFn = sharedMethodAsProperty.value.bind(man);
man.sharedMethodAsProperty = function() {
return sharedMethodAsPropertyFn() + " overridden";
};
}());
With fuyushimoya help I realized how dumb I was and why was it not working.
sharedProperty is redefined for man object, it is never assigned a new value so that is why override works even without sharedProperty being writable.
sharedMethodAsProperty is being assigned a new value for man object. A new function is being created and assigned to it. Assignment would require it to be writable. It'd makes sense to rather redefine it using Object.defineProperty(), just like how sharedProperty was overridden for man object.
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)
I'm trying to extend Object functionality this way:
Object.prototype.get_type = function() {
if(this.constructor) {
var r = /\W*function\s+([\w\$]+)\(/;
var match = r.exec(this.constructor.toString());
return match ? match[1].toLowerCase() : undefined;
}
else {
return typeof this;
}
}
It's great, but there is a problem:
var foo = { 'bar' : 'eggs' };
for(var key in foo) {
alert(key);
}
There'll be 3 passages of cycle.
Is there any way to avoid this?
I, for one, am not completely against extending native types and ECMA-262 5th ed. solves the problems mentioned in other answers and linked articles for us in a nice manner. See these slides for a good overview.
You can extend any object and define property descriptors that control the behavior of those properties. The property can be made non enumerable meaning when you access the objects properties in a for..in loop, that property will not be included.
Here's how you can define a getType method on Object.prototype itself, and make it non enumerable:
Object.defineProperty(Object.prototype, "getType", {
enumerable: false,
writable: false,
configurable: false,
value: function() {
return typeof this;
}
});
// only logs "foo"
for(var name in { "foo": "bar" }) {
console.log(name);
}
The getType function above is mostly useless as it simply returns the typeof object which in most cases will simply be object, but it's only there for demonstration.
[].getType();
{}.getType();
(6).getType();
true.getType();
You shouldn't extend the object prototype, for that exact reason:
http://erik.eae.net/archives/2005/06/06/22.13.54/
Use a static method instead.
If you have no choice, you can use the "hasOwnProperty" method:
Object.prototype.foo = function(){ alert('x'); }
var x = { y: 'bar' };
for(var prop in x){
if(x.hasOwnProperty(prop)){
console.log(prop);
}
}
You can use the hasOwnProperty() method to check if the property belongs to the foo object:
var foo = { 'bar' : 'eggs' };
for (var key in foo) {
if (foo.hasOwnProperty(key)) {
alert(key);
}
}
Is there any way to avoid this?
Yes, don't extend native types.
Use a wrapper instead:
var wrapper = (function(){
var wrapper = function(obj) {
return new Wrapper(obj);
};
function Wrapper(o) {
this.obj = obj;
}
Wrapper.prototype = wrapper.prototype;
return wrapper;
}());
// Define your get_type method:
wrapper.prototype.get_type = function(){
if(this.obj.constructor) {
var r = /\W*function\s+([\w\$]+)\(/;
var match = r.exec(this.obj.constructor.toString());
return match ? match[1].toLowerCase() : undefined;
}
else {
return typeof this.obj;
}
};
Usage:
var obj = { 'bar' : 'eggs' };
alert(wrapper(obj).get_type());
for(var i in obj) { ... works properly }
When you loop over enumerable properties of an object, you can can determin if the current property was "inherited" or not with Object.hasOwnProperty()
for ( var key in foo )
{
if ( foo.hasOwnProperty( key ) )
{
alert(key);
}
}
But let the dangers of monkey patching be known to ye, especially on Object, as others have posted about
Create your own object instead of extending the default Object.
Also see:
http://erik.eae.net/archives/2005/06/06/22.13.54/
http://dean.edwards.name/weblog/2006/07/erlaubt/