I have an object that wraps some data:
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
this.method = function() {
return _foo
}
}
var obj1 = new Obj1()
Now when I call console.log(obj1); I want it to show me object obj2 content. The trick is that I need to still be able to call obj1.method and get value of _foo. How do I do that if it's even possible?
My thought was that sth like getter will be suitable, but can't figure out where and how to assign one.
As far as I understood you're trying to hide method property. To achieve this, use Object.defineProperty. Function will not be logged because enumerable property is false by default which prevents property from showing in console.log for example.
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
Object.defineProperty(this.obj2, 'method', {
value: function() {
return _foo;
}
});
return this.obj2;
}
var obj1 = new Obj1()
console.log(obj1);
console.log(obj1.method());
if i understand correctly, you can use prototype
Example
function Obj1() {
this.obj2 = {
'b': 'c'
}
}
Obj1.prototype.method = function() {
return 'bar';
}
var obj1 = new Obj1();
//prints only properties
console.log(obj1);
//prints method result
console.log(obj1.method())
Since you calling new Obj1(). The result variable var obj1 is a class object and not a function, for you to get the value of obj2 you will have to call obj1.obj2 in your console log. If you want obj1 to hold the value of obj2. Then use the following code
function Obj1() {
var obj2 = {
'b': 'c'
}
return this.obj2;
}
var obj1 = Obj1();
console.log(obj1);
This will give you the required result in the console log, but the object will no longer be a class object and will have only the value of obj2.
Sticking to your original snippet a factory looks like a good option:
function factory() {
var foo = 'bar';
var props = { b: 'c'};
var proto = {
method: function() { return foo; }
};
var obj = Object.create(proto);
Object.assign(obj, props);
return obj;
}
var obj = factory();
console.log(obj); // {b: 'c'}
console.log(obj.method()) // 'foo'
You could even pass props as an argument to get a more flexible way of spawning objects with an "unenumerable" method accessing private members.
Related
I ran into some code that looks like the following:
const {
foo = []
} = this.options
Assuming in this case that this.options is an JavaScript Object, how does this work? Does all of this.options get assigned to foo and if this.options is undefined, does foo just get initialized to an empty array? I found this code confusing because this.options is not an Array but is instead an Object of key/val pairs.
Sometimes it helps to just try things out. What you'd observe is that a default value is assigned to foo in case it is missing within the to be assigned object
function one() {
const options = {};
const {
foo = []
} = options;
console.log(foo);
}
function two() {
const options = {foo: 'bar'};
const {
foo = []
} = options;
console.log(foo);
}
function three() {
const options = {};
const {
foo = 'foo',
bar = 'bar',
baz = 'baz'
} = options;
console.log(foo, bar, baz);
}
one();
two();
three();
From MDN (emphesis mine ) :
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Not all of this.options get assigned to foo, it's unpacking foo from the Object :
const options = {
foo: ['foo', 'bar'],
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
And foo = [] is there to be a fallback to have an empty array if this.options does not have a property foo :
const options = {
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
If this.options is ` undefined, you'll get errors,
options is not defined
const {
foo = []
} = options;
console.log(foo);
Or:
Cannot destructure property foo of 'undefined' or 'null'.
const options = undefined;
const {
foo = []
} = options;
console.log(foo);
If you run it through babel you'll see that if this.options.foo exists then it will be bound to the name 'foo' in that scope and if it doesn't then foo is set to an empty array.
Here is an in-depths article for you to better understand ES6 Destructuring https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/
Destructuring assignment allows you to assign the properties of an
array or object to variables using syntax that looks similar to array
or object literals. This syntax can be extremely terse, while still
exhibiting more clarity than the traditional property access.
For your sample script the assignment are looking for an object with property "foo" from right side. If it can not find it, it will assign foo with default value as empty array.
If the left side is null or undefined, the operator will throw an error "Uncaught TypeError: Cannot destructure propertyfooof 'undefined' or 'null'."
// A regular JS object.
this.options = {
'a': 'a',
'foo': 'bar',
'z': 'z'
}
console.log('this.options.foo =', this.options.foo);
// this.options.foo === 'bar'
// Get 'foo' out of this.options. Becomes undefined if 'foo' doesn't exist in this.options.
const { foo } = this.options;
console.log('foo =', foo);
// foo === this.options.foo === 'bar';
const { nope } = this.options;
console.log('nope =', nope);
// typeof nope === 'undefined'
// Get 'zzz' if it exists, otherwise fall back to 1234.
const { zzz = 1234 } = this.options;
console.log('zzz =', zzz);
// zzz === 1234;
// zzz was set by its default fallback value.
As the subject, is it OK to use ... in that way to copy properties in an object to another?
Chrome always throws syntax error when I was trying to use it in an object like:
var a = function(){
return {c: 2};
}
var b = {...a()};
In es2018 the spread syntax if applied to an object behaves like object.assign.
var obj1 = { a: 1 }
var obj2 = function() { return {b:2} };
var obj3 = { ...obj1, ..obj2 }
The result would be a new object with combine properties of obj1 and obj2. So obj3 would now be { a:1, b2 }.
I know that an object literal can have its keys listed by using Object.keys() method. But what about an object that was created through a constructor function?
function Foo () {}
Foo.prototype.bar = 'bar';
Foo.prototype.baz = 'baz';
var foo = new Foo();
console.log(Object.keys(foo).join(', ')); // returns ''
console.log(Object.getOwnPropertyNames(foo).join(', ')); // returns ''
Object.keys will only get own enumerable properties, and getOwnPropertyNames will only get own properties (even if not enumerable). Neither of them will give you the names of properties inherited from the prototype (or its prototype, or its, ...).
If you only care about enumerable properties, see trincot's answer.
If you want all of them,¹ even if they're not enumerable, you have to loop through the prototype chain:
function getAllPropertyNames(obj) {
var result = [];
while (obj && obj !== Object.prototype) {
result.push.apply(result, Object.getOwnPropertyNames(obj));
obj = Object.getPrototypeOf(obj);
}
return result;
}
function Foo () {}
Foo.prototype.bar = 'bar';
Foo.prototype.baz = 'baz';
var foo = new Foo();
console.log(getAllPropertyNames(foo));
In that example, I stopped when we reached Object.prototype, but of course you could keep going until you hit null instead:
function getAllPropertyNames(obj) {
var result = [];
while (obj) {
result.push.apply(result, Object.getOwnPropertyNames(obj));
obj = Object.getPrototypeOf(obj);
}
return result;
}
function Foo () {}
Foo.prototype.bar = 'bar';
Foo.prototype.baz = 'baz';
var foo = new Foo();
console.log(getAllPropertyNames(foo));
¹ "If you want all of them..." Note that in the above, we haven't tried to get properties that are named by Symbols instead of strings. If we did, we'd use getOwnPropertySymbols as well as getOwnPropertyNames.
You can use a for loop:
function Foo () {}
Foo.prototype.bar = 'bar';
Foo.prototype.baz = 'baz';
var foo = new Foo();
for (var key in foo)
console.log(key, foo[key]);
Note that this has limitations. Some properties can be made non-enumerable, and will then not be included. This is for instance the case for the length property of arrays:
var a = [1, 2];
for (var key in a)
console.log(key, a[key]);
I have the following code snippet
function foo(a) {
this.a = a;
}
var obj = {a: 77};
var bar = function() {
return foo.apply( obj, arguments );
};
var o = new bar( 2 );
console.log( o.a ); // I think it's 2(why is it undefined?)
console.log(obj.a); // obj.a changed to 2, got it.
Why is o.a undefined? also if I delete the return keyword in bar, it will still be the same.
function foo(a) {
this.a = a;
}
var obj = {a: 77};
var bar = function() {
foo.apply( obj, arguments );
};
var o = new bar( 2 );
console.log( o.a ); // I think it's 2(why is it undefined?)
console.log(obj.a); // obj.a changed to 2, got it.
The question is actually what happens when hard binding and new binding happens the same time
Using new will create a new object and assign that object as this to the function you call it on. In your case, you're not using the new object and instead are calling a function setting obj as this.
As to why adding and removing return doesn't change anything is because in both cases, you're returning undefined. When a function is called with new and returns undefined (a.k.a the default return value) it returns the new object generated by new.
To quote MDN:
The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)
So you create a new object but manipulate an old object. Your code effectively works like this.
function foo(a) {
this.a = a;
}
var obj = {a: 77};
var bar = function() {
return foo.apply( obj, arguments );
};
var o = {}; // Originally you used `new` to create a new object
bar(2); // Since `bar` doesn't use `this`, it's the same as if it were called by itself
console.log(o.a); // Empty object
console.log(obj.a); // Modified by `bar`
You have to return this from foo(a), like so:
function foo(a) {
this.a = a;
// "this" NEEDS TO BE RETURNED, OTHERWISE (bar)'s
// VALUE WILL BE UNDEFINED, THUS o.a WILL BE UNDEFINED
return this;
}
var obj = {a: 77};
var bar = function() {
// MAKE SURE THIS IS RETURNED AS WELL
return foo.apply( obj, arguments );
};
var o = new bar( 2 );
console.log( o.a ); // I think it's 2(why is it undefined?)
console.log(obj.a); // obj.a changed to 2, got it.
The question is actually what happens when hard binding and new
binding happens the same time
In this case nothing because a new instance of new bar( 2 ) is useless when its return value is dependent on foo(a), and foo(a) is dependent on a singleton (obj).
I hope that helps!
When using object constructors, properties can be directly assigned to the value of previously defined properties:
var foo = new (function() {
this.bar = 5;
this.baz = this.bar;
})();
alert(foo.baz) // 5
I would like to refer to a previously defined property within an OBJECT LITERAL:
var foo = {
bar : 5,
baz : bar
}
alert (foo.baz) // I want 5, but evaluates to undefined
I know that I could do this:
var foo = {
bar : 5,
baz : function() {
alert(this.bar); // 5
}
But I want to assign baz directly to a value rather than a function. Any ideas?
No, you won't be able to use any properties of the object literal before it has been created. Your closest option is probably to use a temporary variable like so:
var tmp = 5,
foo = {
bar : tmp,
baz : tmp
}
If you are free to use ECMAScript 5 features, you could write a getter function for the baz property that instead returns the value of bar:
var yourObject = {
bar: 5
};
Object.defineProperty(yourObject, 'baz', {
get: function () { return yourObject.bar; }
});
You can also just build a literal by parts:
var foo = {bar:5};
foo.baz = foo.bar;
If you need to fit this inside an expression (instead of through multiple statements) you can try abusing the comma operator or you can make a helper function:
(Warning: untested code)
function make_fancy_object(base_object, copies_to_make){
var i, copy_from, copy_to_list;
for(copy_from in copies_to_make){
if(copies_to_make.hasOwnProperty(copy_from)){
copy_to_list = copies_to_make[copy_from];
for(var i=0; i<copy_to_list.length; i++){
base_object[copy_to_list[i]] = base_object[copy_from];
}
}
}
}
var foo = make_fancy_object(
{bar: 5},
{bar: ["baz", "biv"]}
);
//foo.baz and foo.biv should be 5 now as well.