I am trying to create an object. But I dont understand why my property getters setters cannot simply call this.bar. As such my foo object seems to end up with two properties.
Is this correct or am I:
using defineProperties wrong
missing the point
creating foo with a bar property
var foo = function ()
{
Object.defineProperties(this, {
bar : {
get : function () {return this.barVal},
set : function(value) { this.barVal = value},
enumerable: true,
configurable: true
}
})
};
var o = new foo();
o.bar = "Hello";
console.log(JSON.stringify(o));
//output {"bar":"Hello","barVal":"Hello"}
JSON.stringify activates the getter to resolve the property. Your setter sets a second property, so you end up seeing both. What you need is a way to store the "internal" value for foo.bar that isn't ON foo itself.
function Foo(){
var secret = {};
Object.defineProperties(this, {
bar: {
get: function( ){ return secret.bar },
set: function(v){ secret.bar = v },
enumerable: true,
configurable: true
}
});
}
var foo = new Foo;
foo.bar = 'Hello';
console.log(JSON.stringify(foo));
You are creating two properties, one named "bar" and the other named "barVal". "bar" is created by the defineProperties call and "barVal" is created by the this.barVal assignment in the set function. They both have a true value for their enumerable attribute (you explicitly set it for varVal, the assignment implicitly set it for barVal) so they are both listed by JSON.stringify.
If you intent is for barVal to be treated as a private value that does not show up in a JSON or for-in enumeration you can explicitly set its enumerable attribute to false:
var foo = function ()
{
Object.defineProperties(this, {
bar : {
get : function () {return this.barVal},
set : function(value) { this.barVal = value},
enumerable: true,
configurable: true
},
barVal : {
value: undefined,
enumerable: false, writable: true, configurable: true
}
})
};
Related
I'm trying to understand why using Object.defineProperty makes a property not visible when the object is logged in the console.
For instance:
let foo = {};
Object.defineProperty(foo, "a", {
get() { return "a"; }
});
Object.defineProperty(foo, "b", {
get() { return "b"; },
enumerable: true
});
console.log( foo ); // returns {}
I do understand that enumerable will affect the outcome of a for...in loop or the Object.keys() static method.
But I don't get why console.log(foo) returns {}, regardless of the use of enumerable, and even if I start by declaring foo as let foo = { a: 'bar' };
Thanks!
This is the source code autobind.js.
I am confused of the getter method.The getter method return a boundFn, but also define a boundFn on "this",why? For the efficiency?
get() {
// ...omit
const boundFn = bind(fn, this);
defineProperty(this, key, {
configurable: true,
writable: true,
// NOT enumerable when it's a bound method
enumerable: false,
value: boundFn
});
return boundFn;
},
I test like this.
function Foo(){}
Object.defineProperty(Foo.prototype, 'test', {
get() {
console.log('123');
Object.defineProperty(this, 'test', {
get(){
console.log('456');
return '456';
}
})
return '123'
}
});
var a = new Foo();
a.test; //FirstTime: 123,
a.test; //and then always: 456,
For the efficiency.It will not always lookup the prototype chain.I think so.
When declaring a class (using the ECMAScript 2015 class) syntax, where are the methods that are defined as part of the class stored? I was expecting to find them on the prototype-object, but for some reason I could not access them in the following sample:
class MyClass {
constructor() {
this.a = function() {return ("a");};
}
b() { return ("b"); }
}
MyClass.prototype.c = function() { return ("c"); }
const m = new MyClass();
// Sanity check: All three functions are callable and return correct results
console.log(m.a()); // "a"
console.log(m.b()); // "b"
console.log(m.c()); // "c"
// Expected: The only property of the object m itself is a
console.log(Object.keys(m)); // ["a"]
// Unexpected: Where is b?
console.log(Object.keys(Object.getPrototypeOf(m))); // ["c"]
console.log(Object.keys(MyClass.prototype)); // ["c"]
The b-function must be somewhere (it is clearly callable), but where? I tested this behavior node 9.5.0 and Firefox 58.0.1
Object.keys only shows enumerable properties. The properties defined on the prototype by class aren’t enumerable.
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(m)));
You can use getOwnPropertyNames() method to return all the properties:
class MyClass {
constructor() {
this.a = function() {
return ("a");
};
}
b() {
return ("b");
}
}
MyClass.prototype.c = function() {
return ("c");
}
const m = new MyClass();
// Sanity check: All three functions are callable and return correct results
console.log(m.a()); // "a"
console.log(m.b()); // "b"
console.log(m.c()); // "c"
// Expected: The only property of the object m itself is a
console.log(Object.keys(m)); // ["a"]
// Unexpected: Where is b?
console.log(Object.keys(Object.getPrototypeOf(m))); // ["c"]
console.log(Object.keys(MyClass.prototype)); // ["c"]
console.log(Object.getOwnPropertyNames(MyClass.prototype));
The Object.getOwnPropertyNames() method returns an array of all properties (including non-enumerable properties except for those which use Symbol) found directly upon a given object.
Class methods defined within the class syntax are non-enumerable, which is why b doesn't appear in the output of Object.keys:
> Object.getOwnPropertyDescriptor(MyClass.prototype, "b")
{value: ƒ, writable: true, enumerable: false, configurable: true}
> Object.getOwnPropertyDescriptor(MyClass.prototype, "c")
{value: ƒ, writable: true, enumerable: true, configurable: true}
My use case is the following: I want to create a factory which produces various kinds of data transfer objects (DTOs). They must be easily serializable and they must have a few additional methods.
My current implementation looks like this (simplified):
window.Dto = function(type, properties)
{
var
self = this,
values = {},
object = Object.create(self);
properties.forEach(function(prop){
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
this.getType = function()
{
return type;
};
this.doSomeMagic = function()
{
// ...
};
return object;
};
// creating a DTO of the Transport.Motorized.Car class
var carObject = new Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);
(Note: I do not want to create an explicit class for each of these objects, because there are hundets of them, and they are exported from the server side. Also, what you see as properties parameter above, is actually a map of meta data with validation constraints etc.)
I did a quick performance check with a loop where 50,000 of such objects were created. performance.now() tells me that it took a bit more than 1s – which looks ok, but not too impressive.
My question is mainly: Is it ok that the factory creates an instance from its own prototype (if I understand correctly what that code does) and returns it? What side effects can it have? Is there a better way?
As far as I understand factory functions, their whole point is not needing to create new instances of the function itself. Instead, it just returns a newly created object.
So instead of using instance properties (via this) of the newly created instance (via the new operator), I would just create an object (let's call it factoryProto) and assign all the "instance" methods to that object instead.
Then, you can use factoryProto as the [[Prototype]] for your new object:
window.Dto = function(type, properties) {
var factoryProto = {
getType: function() {
return type;
},
doSomeMagic: function() {
// ...
}
},
values = {},
object = Object.create(factoryProto);
properties.forEach(function(prop) {
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
return object;
};
// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);
If you want to fully profit from the prototype-chain, you could define the factoryProto outside of the factory function. To keep track of type, you could add it as a non-enumerable object property:
window.Dto = (function() {
var factoryProto = {
getType: function() {
return this.type;
},
doSomeMagic: function() {
// ...
}
};
return function(type, properties) {
var values = {},
object = Object.create(factoryProto);
properties.forEach(function(prop) {
Object.defineProperty(object, prop, {
get: function() { return values[prop]; },
set: function(value) { values[prop] = value; },
enumerable: true
});
});
Object.defineProperty(object, 'type', {
value: type,
enumerable: false
});
return object;
};
})();
// creating a DTO of the Transport.Motorized.Car class
var carObject = Dto("Transport.Motorized.Car", ["vendor", "model", "color", "vmax", "price"]);
How come constants cannot be set as properties of objects which are variables themselves?
const a = 'constant' // all is well
// set constant property of variable object
const window.b = 'constant' // throws Exception
// OR
var App = {}; // want to be able to extend
const App.goldenRatio= 1.6180339887 // throws Exception
And how come constants passed by reference suddenly become variable?
EDIT: I know App won't (or rather... SHOULDN'T) be mutable; this is just an observation...
(function() {
const App;
// bunch of code
window.com_namespace = App;
}());
window.com_namespace; // App
window.com_namespace = 'something else';
window.com_namespace; // 'something else'
How can a nicely organized, extensible, object-oriented, singly namespaced library containing constants be made with these limitations?
EDIT: I believe zi42, but I just have to ask why
You cannot do it with constants. The only possible way to do something that behaves like you want, but is not using constants, is to define a non-writable property:
var obj = {};
Object.defineProperty( obj, "MY_FAKE_CONSTANT", {
value: "MY_FAKE_CONSTANT_VALUE",
writable: false,
enumerable: true,
configurable: true
});
Regarding your question as to why a const passed to a function becomes variable, the answer is because it's passed by value and not by reference. The function is getting a new variable that has the same value as your constant.
edit: thanks to #pst for noting that objects literals in javascript are not actually "passed by reference", but using call-by-sharing:
Although this term has widespread usage in the Python community, identical semantics in other languages such as Java and Visual Basic are often described as call-by-value, where the value is implied to be a reference to the object.
const person = {
name: "Nicholas"
};
// works
person.name = "Greg";
console.log(person) //Greg
That's why use Object.defineProperty
There is a far simpler way to do this. I like this pattern. Simple Objects.
window.Thingy = (function() {
const staticthing = "immutable";
function Thingy() {
let privateStuff = "something";
function get() {
return privateStuff;
}
function set(_) {
privateStuff = _;
}
return Object.freeze({
get,
set,
staticthing
});
}
Thingy.staticthing = staticthing;
return Object.freeze(Thingy);
})();
let myThingy = new Thingy();
Thingy.staticthing = "fluid";
myThingy.staticthing = "fluid";
console.log(Thingy.staticthing); // "immutable"
console.log(myThingy.staticthing); // "immutable"
Object.freeze is doing the work here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
if you want you can leave the static property off the instance by leaving it off the object literal return on the constructor function.
const will only make it a read-only reference. As soon as you assign it, like here in a object literal it becomes a property of the constructed object.
var obj = {};
Object.defineProperty( obj, "MY_FAKE_CONSTANT", {
value: "MY_FAKE_CONSTANT_VALUE",
writable: false,
enumerable: true,
configurable: false // instead of true
});
We should set also configurable to be false so that it will prevent the property from being deleted from the obj
delete obj.MY_FAKE_CONSTANT;
With configurable to be true, after the line, we don't have the MY_FAKE_CONSTANT anymore.
Reference
You should not forgot that the const declaration "creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned"
The const keyword work in a similar way than 'let', so you can redeclare it in an other block
const MyConst = 5;
console.log('global MyConst =', MyConst); //global MyConst = 5
if(true){
const MyConst = 99
console.log('in if block, MyConst =', MyConst); //in if block, MyConst = 99
}
console.log('global MyConst still 5 ?', MyConst===5); //global MyConst still 5 ? true
Just like #ziad-saab mantioned if you want an object property than act like a constant, you have to define it as a non-writable property.
if your constant is an object and is property should not change, use Object.freeze() to make the object immutable.
(function(){
var App = { };
// create a "constant" object property for App
Object.defineProperty(App , "fixedStuff", {
value: Object.freeze({ prop:6 }),
writable: false,
enumerable: true,
configurable: true
});
Object.defineProperty(window, "com_namespace", {
value: App,
writable: false,
enumerable: true,
configurable: true
});
})()
com_namespace.newStuff = 'An extension';
com_namespace.fixedStuff.prop = 'new value'; // do nothing!
console.log(com_namespace.fixedStuff.prop); //6
I thinks that you should define a constant in property making configurable:false and writable:false in Object.defineProperty, if you leave configurable:true then you can still making changes to the property
for example: when you set configurable:true in Object.defineProperty
const obj = {name: 'some name'};
Object.defineProperty(obj, 'name', {
enumerable: true,
configurable: true,
writable: false
});
With this you are making obj.name 'constant' but after you could do
Object.defineProperty(obj, 'name', {
enumerable: true,
configurable: true,
writable: true // <-- you can set to true because configurable is true
});
But if you set configurable to false then you could never edit writable property.
Object.defineProperty(obj, 'name', {
enumerable: true,
configurable: false, // <-- with this you never could edit writable property
writable: false
});
And for all properties you could use Object.freeze
Object.freeze(obj);
Object.freeze will iterate over enumerable properties only