Javascript: Confuse about object property access - javascript

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

Related

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

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!

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).

Can you use shorthand property assignment using 'this'?

I can do this:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
let boo = 'boo'
return {
boo
}
}
}
console.log(new Temp().getObj())
//prints { boo: 'boo' }
So how can I do this:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
return {
this.foo
}
}
}
console.log(new Temp().getObj())
Is there special syntax or is it not supported?
I guess what you actually look for is:
return { ...this };
or if you want to omit some properties:
const { bar, ...take } = this;
return take;
No, this is not yet supported. You will have to go by {foo: this.foo}.
However, there is a stage 1 proposal for shorthand property definition improvements that would allow you to write {this.foo} as an object literal.
If 'foo' is a public property you can do something like this:
class Foo {
constructor() {
this.foo = 'foo';
}
}
const { foo } = (new Foo());
console.log(foo); // 'foo'
https://repl.it/repls/FastForcefulMice
If you want to use destructuring, you'll need to extract the property to a const, and then use shorthand property names:
class Temp {
constructor() {
this.foo = 'foo'
this.bar = 'bar'
}
getObj() {
const { foo } = this;
return {
foo
}
}
}
console.log(new Temp().getObj())

Destructure a function parameter subproperty

I've got a Function that I want to be able to call in 2 ways - and it should behave the same.
Is there any ES6 syntax that will allow me to call the function doBar below using both ways with the same result?
Consider a function like this:
const doBar = ({ foo = 'bar' }) => {
console.log(foo) // should log 'baz'
}
I'm using a framework that binds events like so:
<x-component on-some-event="doBar"></x-component>
which will essentially cause an invocation like so:
// where e = { detail: { foo: 'baz' } }
doBar(e)
.. but I'd like to be able to both call my Function explicitly as well, albeit with a proper call signature like so:
doBar({ foo: 'baz' })
You can use a default parameter. If foo is not found, it will use the value of detail.foo.
const doBar = ({ detail = {}, foo = detail.foo }) => {
console.log(foo) // should log 'baz'
}
doBar({ foo: 'baz' })
doBar({
detail: {
foo: 'baz'
}
});
You can't do this properly in the parameter declaration. Just put your destructuring assignment in the function body:
function doBar(e) {
const { foo = "bar", qux } = e.detail || e;
consoel.log(foo, qux);
}
I'd like to be able to both call my function explicitly as well
I would recommend to not do that. A function should have one signature only and not be overloaded to different types of arguments. Just be explicit about when to use what:
function doBar({ foo = "bar", qux }) {
console.log(foo);
}
function withDetail(fn) {
return e => fn(e.detail)
}
<x-component on-some-event="withDetail(doBar)"></x-component>
doBar({ foo: 'baz' })
No. Not unless you consider this to be an adequate alternative:
const thing = {
detail: {
foo: 'baz'
}
};
doBar(thing.detail);

Node.js adding multiple properties to an object

I'm wondering if there's a better way of doing this:
module.exports.foo = "bar";
module.exports.bar = "baz";
module.exports.foobar = "foobaz";
Now, I've heard about 'with' (and its dangers) but it doesn't work here because it can't add new properties to an object, it can only change existing properties. In fact if you try to add new properties you end up clobbering the enclosing scope which isn't very nice.
Something like this would be nice:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
But that converts both sides to strings before concatenating them which is not what I want.
Does such syntactic sugar exist in Node.JS-flavoured JavaScript? Maybe a function on 'module' to add lots of properties at once?
You could write a simple function to loop over them and add them to module.exports or you can try node.extend which tries to mimic jquery's $.extend()
Use Object.assign()
Object.assign(module.exports, {
foo: "bar",
bar: "baz",
foobar: "foobaz"
});
Consider using _.extend
So in your example:
module.exports += {
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
Would be:
var _ = require('underscore');
_.extend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
Or, if you do not want any dependencies, using raw javascript:
function myExtend(dest, obj) {
for (key in obj) {
var val = obj[key];
dest[key] = val;
}
}
myExtend(module.exports,
{
foo: "bar",
bar: "baz",
foobar: "foobaz"
}
);
If you are using the latter method, make sure to check for edge cases, which I haven't here!

Categories

Resources