Why does Object.defineProperty make a property invisible when logged? - javascript

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!

Related

Problem with default parameter and object

I've got a problem with default parameters in Javascript.
I have a function like this:
function search(filterOptions = {
foo: 'bar',
foo2: 'bar2'
}) {
...
}
When I call search() without arguments, filterOptions is set to {foo: 'bar', foo2: 'bar'},
but when I call search({ foo: 'something' }), foo2 is undefined.
I cannot separate filterOptions into several arguments because options are independent.
How can i make foo2 take its default value anyway (and cleanly)?
(I'm on nodejs)
Thank you!
You could define the defaults within the function and use the spread syntax to combine the two objects, which will override the defaults where applicable.
function search(filterOptions) {
const defaults = { foo: 'foo', foo2: 'bar' };
filterOptions = {...defaults,...filterOptions};
console.log(filterOptions);
}
search({foo: 'something'});
You can provide default values in the parameter list:
function search({ foo = "bar", foo2 = "bar2"} = {}) {
console.log("foo is " + foo + ", foo2 is " + foo2);
}
console.log(search());
console.log(search({ foo: "broccoli" }));
console.log(search({ foo: "my foo", foo2: "my foo2" }));
The = {} at the end is to handle the case when the function is called with no parameters.
You can define the default variable with an if statement:
function search(arr) {
if(arr.bar === undefined) {
arr.bar = "bar1";
}
//do whatever
}
The use of === is to make sure that the if does not run if bar is set to "undefined" (a string).

Instance methods missing from prototype object

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}

Javascript: Confuse about object property access

Here is a object I build:
var Obj = {
foo: {
foo: function () {
return this.bar;
},
bar: "bar"
},
bar: "BAR"
}
console.log(Obj.foo.foo());
The Obj.foo.foo method can only access the Obj.foo.bar property's value, or the property of its owner Obj.foo. Can it access the Obj.bar's value? How?
Scope scope and scope
Javascript is a fun language. And one of the realy cool things is the binding of this.
this is defined on function call. So depending on how you call a method. this can change. You can even change it on the fly. Given the following objects:
var Obj = {
foo: {
myFunc: function () {
return this.bar;
},
bar: "bar"
},
bar: "BAR"
}
var Obj2 = {
foo: {
bar: "foobar"
},
bar: "FOOBAR"
}
we can have some fun:
Obj.foo.myFunc(); // "bar"
but we could use call the method binding a different scope
Obj.foo.myFunc.call(Obj); // "BAR"
Here Obj is bound to this.
We can even bind completly different objects:
Obj.foo.myFunc.call(Obj2); // "FOOBAR"
Obj.foo.myFunc.call(Obj2.foo); // "foobar"
or call it ouside of it's scope:
var bar = "something completly different";
var func = Obj.foo.myFunc;
func(); // "something completly different"
so much fun :)
You could introduce a function scope holding the needed reference:
var Obj = (function() {
var that = this;
return {
foo: {
foo: function () {
return that.bar;
},
bar: "bar"
},
bar: "BAR"
})();
I think this should work..
No, there is no way to access "parent" reference - there is no such concept in Javascript. The only way to access Obj.bar from Obj.foo is by direct reference.
var Obj = {
foo: {
foo: function () {
return Obj.bar;
},
bar: "bar"
},
bar: "BAR"
}

Using Object.defineProperties gives two properties

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

How to create Javascript constants as properties of objects using const keyword?

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

Categories

Resources