What's the difference between Object.getOwnPropertyNames and Object.keys in javascript? Also some examples would be appreciated.
There is a little difference. Object.getOwnPropertyNames(a) returns all own properties of the object a. Object.keys(a) returns all enumerable own properties. It means that if you define your object properties without making some of them enumerable: false these two methods will give you the same result.
It's easy to test:
var a = {};
Object.defineProperties(a, {
one: {enumerable: true, value: 1},
two: {enumerable: false, value: 2},
});
Object.keys(a); // ["one"]
Object.getOwnPropertyNames(a); // ["one", "two"]
If you define a property without providing property attributes descriptor (meaning you don't use Object.defineProperties), for example:
a.test = 21;
then such property becomes an enumerable automatically and both methods produce the same array.
Another difference is in case of array Object.getOwnPropertyNames method will return an extra property that is length.
var x = ["a", "b", "c", "d"];
Object.keys(x); //[ '0', '1', '2', '3' ]
Object.getOwnPropertyNames(x); //[ '0', '1', '2', '3', 'length' ]
Literal notation vs constructor when creating object. Here is something that got me.
const cat1 = {
eat() {},
sleep() {},
talk() {}
};
// here the methods will be part of the Cat Prototype
class Cat {
eat() {}
sleep() {}
talk() {}
}
const cat2 = new Cat()
Object.keys(cat1) // ["eat", "sleep", "talk"]
Object.keys(Object.getPrototypeOf(cat2)) // []
Object.getOwnPropertyNames(cat1) // ["eat", "sleep", "talk"]
Object.getOwnPropertyNames(Object.getPrototypeOf(cat2)) // ["eat", "sleep", "talk"]
cat1 // {eat: function, sleep: function, talk: function}
cat2 // Cat {}
// a partial of a function that is used to do some magic redeclaration of props
function foo(Obj) {
var propNames = Object.keys(Obj);
// I was missing this if
// if (propNames.length === 0) {
// propNames = Object.getOwnPropertyNames(Obj);
// }
for (var prop in propNames) {
var propName = propNames[prop];
APIObject[propName] = "reasign/redefine or sth";
}
}
So in my case the foo function didn't work if I gave it objects of the cat2 type.
There are other ways to create objects so there could be other kinks in there as well.
As was already explained, .keys doesn't return non-enumerable properties.
Regarding to examples, one of pitfall cases is an Error object: some of its properties are non-enumerable.
So while console.log(Object.keys(new Error('some msg'))) yields [],
console.log(Object.getOwnPropertyNames(new Error('some msg'))) yields ["stack", "message"]
console.log(Object.keys(new Error('some msg')));
console.log(Object.getOwnPropertyNames(new Error('some msg')));
Another difference is that (at least with nodejs) "getOwnPropertyNames" function does not guarantee keys order, that's why I usually use "keys" function :
Object.keys(o).forEach(function(k) {
if (!o.propertyIsEnumerable(k)) return;
// do something...
});
Related
I want to make my function addHat() reusable for other person objects as well, so I decided to use this instead of exact name of the person and pass my other objects as this by addHat.call() and addHat.apply() methods.
ie. I want to shallow copy the person object using spread (having new references for all of the properties.
However, it doesn't work using this on both sides of the assignment
let person1 = {
a: "a",
hats: [],
gloves: []
};
function addHat(newHat, ...params) {
this = {
...this,
hats: [...this.hats, newHat, ...params],
};
}
addHat.call(person1, "hatA");
console.log(person1.hats);
But perfectly works when using the name directly instead:
let person1 = {
a: "a",
hats: [],
gloves: []
};
function addHat(newHat, ...params) {
person1 = {
...person1,
hats: [...person1.hats, newHat, ...params],
};
}
addHat.apply("uselessThis", ["hatA", "hatB"]);
console.log(person1.hats);
Can you help me to make this function reusable with the current format? (using spread and shallow copying of the object properties)
every hint or help will be much appreciated.
I think that it's easier if you pass persona as an argument of the function.
Try to avoid the use of this in js because it can become tricky to debug
let person1 = {
a: "a",
hats: [],
gloves: []
};
function immutableAddHat(person, ...newHats) {
return {
...person,
hats: [...person.hats, ...newHats],
};
}
function addHat(...newHats) {
Object.entries(this).forEach(([k, v]) => {
this[k] = v
})
this.hats = [...this.hats, ...newHats]
}
const newPerson1 = immutableAddHat(person1, "hatA");
console.log(newPerson1.hats);
addHat.call(person1, "Hatb")
console.log(person1)
I added the method that uses this
I want an object to call a function defined in another object or JSON (as a string or reference), with a variable number of values also defined there.
Here's a pseudo-code example of what I'm trying to do:
// function name and variable-length values definition
const definition = {
functionName: "function1",
values: [ 1, "foo", 3.14 ]
}
// objectA calling the function with the values
// objectA.definedFunction(definedValues);
// objectA.[functionName]([val1], [val2], ..., [valN]);
objectA.function1(1, "foo", 3.14);
Note: objectA may/should? reference the definition in a member variable.
How can that be achieved?
And is it possible to expand that so that function1 is a member function of another object that is also defined in definition?
There are two mechanisms you need.
First the square bracket notation to access object properties:
objectA[definition.functionName]; // this will return the function you want
// This is the same as objectA.function1
Second you want to pass an array as parameter list. For that you can use Function.prototype.apply which expects the argument list in the form of an array:
objectA[definition.functionName].apply(objectA, definition.values);
// This is the same as objectA.function1(1, "foo", 3.14);
string to function: Function
const fx1 = Function(definition.functionString)
const fx2 = objectA[definition.functionName]
there has serval way to call fx, depend on how fx handle parameter
fx.apply(null, definition.values)
fx(...definition.values)
fx(definition.values)
If you have an object (i.e. dictionary) of functions and their arguments' values, you can get the function simply by accessing its matching value in the dictionary.
Then, in order to use its arguments, you can use the spread operator:
const dictionary = {
function1: { // For example
definition: (a, b, c) => `{ a: ${a}, b: ${b}, c: ${c} }`,
values: [1, "foo", 3.14],
},
};
function callFunctionByName(name) {
const currFunction = dictionary[name];
const { definition, values } = currFunction;
return definition(...values);
}
console.log(callFunctionByName('function1'));
let objA = {
function1: function(a, b, c) {
console.log(a)
console.log(b);
console.log(c);
}
}
const definition = {
functionName: "function1",
values: [ 1, "foo", 3.14 ]
}
objA[definition.functionName].apply(null, definition.values);
//Second approach if it function1 is not a key of objA
function function1(first, sec, third) {
console.log(first);
console.log(sec);
console.log(third);
}
const definition = {
functionName: "function1",
values: [ 1, "foo", 3.14 ]
}
eval(`${definition.functionName}(${definition.values[0]}, "${definition.values[1]}", ${definition.values[2]})`);
When reviewing the ES6 docs, I noted that it is recommended to use the spread syntax over the more verbose Object.assign() method. But, I am a bit confused as to how this is being accomplished.
Is object in this case being broken down to key: value pairs, after which the property on the right of the comma is either added or overwritten, and finally being reassembled?
Is object in this case being broken down to key: value pairs, after which the property on the right of the comma is either added or overwritten, and finally being reassembled?
The key-value pairs of the original object object are actually being used in combination (merging) with the new object which has an extra property var2( they are getting combined to newObject).
You can think of it as object is becoming a subset of newObject in the place where the spread syntax in being used, while properties with same key are being overridden.
Check the below example:
const object = { txt: 'Test' };
const newObject = {...object, var2: 'aa' };
// logs Object {txt: "Test", var2: "aa"}
console.log(newObject);
const object2 = { txt: 'Test' };
const newObject2 = {...object, txt: 'Test2', var2: 'aa' };
// Object {txt: "Test2", var2: "aa"}
console.log(newObject2);
// merging all objects to a new object
var object3 = {first: 'Hello', second: 'World'};
var newObject3 = {...object, anotherVar: 'stack', ...object2, ...object3};
// Object {txt: "Test", anotherVar: "stack", first: "Hello", second: "World"}
console.log(newObject3);
I know redux is strict with state management, but is adding a value from an array of objects considered a no no in redux? Example:
// Consider this array of objects on action
action.arr = [ { test: 'me', hail: 'hydra'}, { test: 'you', ring: 'of fire'} ]
// reducer.js
fn = (state = defaultState, action) => {
...
case action.LORD_COMMANDER:
return action.arr.map(v => {
v.john = 'snow'
return v
})
...
}
Is this completely safe on my reducer or should I use Object.assign()?
I think better use Object.assign. Let's consider two examples
const arr = [ { test: 'me', hail: 'hydra'}, { test: 'you', ring: 'of fire'} ];
const arr1 = arr.map(v => {
v.john = 'snow'
return v;
});
console.log(arr, arr1);
As you can see each Object in two arrays have property john because we change Objects which have same reference that is not safe for previous Array., in example below you can see that only in second Array Objects have property john it is because we make copy of Object
const arr = [ { test: 'me', hail: 'hydra'}, { test: 'you', ring: 'of fire'} ];
const arr1 = arr.map(v => {
return Object.assign({}, v, { john: 'show' })
});
console.log(arr, arr1);
in this case you are making your state to be the array and since the array in the action is always going to be different then yes you are mutating the state. as long as the objects are not references the state will be completely mutated a sample of a dirty state is.
var objA = {};
var objB = {};
action.array = [objA, objB];
in this case when you do the map your reference to the array would be new but the reference to the objects would be the same. here it would be advised to use Object.assign or (...) operator inside of your map function. or better yet i would recommend looking into immutablejs to handle immutable data
https://facebook.github.io/immutable-js/
I recently started using ES6's destructuring assignment syntax and started to get familiar with the concept. I was wondering if it's possible to extract a nested property using the same syntax.
For example, let's say I have the following code:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
I know I am able to access extract foo into a variable by doing:
// where foo = { bar: "Nick Cage" }
let { foo } = cagingIt;
However, is it possible to extract a deeply nested property, like bar. Perhaps something like this:
// where bar = "Nick Cage"
let { foo[bar] } = cagingIt;
I've tried finding documentation on the matter but to no avail. Any help would be greatly appreciated. Thank you!
There is a way to handle nested objects and arrays using this syntax. Given the problem described above, a solution would be the following:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
let { foo: {bar: name} } = cagingIt;
console.log(name); // "Nick Cage"
In this example, foo is referring to the property name "foo". Following the colon, we then use bar which refers to the property "bar". Finally, name acts as the variable storing the value.
As for array destructuring, you would handle it like so:
let cagingIt = {
foo: {
bar: 'Nick Cage',
counts: [1, 2, 3]
}
};
let { foo: {counts: [ ct1, ct2, ct3 ]} } = cagingIt;
console.log(ct2); // prints 2
It follows the same concept as the object, just you are able to use array destructuring and store those values as well.
You can destructure a property "as" something else:
const { foo: myFoo } = { foo: 'bar' } // myFoo == 'bar'
Here foo was destructured as myFoo. You can also destructure an object "as" a destructured object
const { foo: { bar } } = { foo: { bar: 'baz' } } // bar == 'baz'
Only one variable was defined in each situation, myFoo and bar, and you can see how they are in similar locations as well, except bar has the { } around it.
You can do this for as many layers of nesting as you like, but if you aren't careful going too many level deep you'll get the old "Cannot read properties of undefined(reading 'foo')".
// Here's an example that doesn't work:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: { // baz is undefined in foo, so by trying to destructure it we're trying to access a property of 'undefined'
qux
}
}
} = foo;
// throws Uncaught TypeError: Cannot read properties of undefined (reading 'baz')
// because baz is 'undefined'
// Won't run due to error above
console.log(qux);
In this case it should be obvious that we shouldn't be trying to destructure it because we can see the definition of foo on the previous line doesn't define the property baz. If the object is coming from an API, though, you aren't always guaranteed that every nested property of your expected result will be non-null or not undefined and you can't know beforehand.
You can set a default value for a destructured object by adding = {}:
const { foo: myFoo = 'bar' } = { baz: 'qux' }; // myFoo == 'bar'
const { bar: { baz } = {} } = { qux: 'quuz' } // baz == undefined
// baz is destructured from the object that was set as the default for foo, which is undefined
// this would throw without the default object, as were trying to destructure from 'undefined'
You can do this for deeply nested destructurings:
// Here's an example that works:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: {
qux // you can default the final item to anything you like as well including null or undefined, but it will be undefined in this case
} = {} // if 'baz' undefined, return {}
} = {} // if 'bar' undefined, return {}
} = foo;
console.log(qux); // logs 'undefined'
If any property is null or undefined along the way, it will cause a cascade of returning empty objects, whose properties to be destructured at the next level will just be undefined. This gets out of hand really quickly though with deeper objects, which can be many lines of code with this formatting. Here's another option that does the same exact thing.
const foo = { bar: { notBaz: 1 } };
const {qux} = foo?.bar?.baz ?? {}; // optional chaining and nullish coalescing
If at any point along the way foo, bar, or baz is null or undefined or null, it will return an empty object that you can destructure( the empty object after ??.
It doesn't make much sense to use destructuring on { qux } if you only need to extract one property, though, because this also requires us to add the nullish coalesced value ?? {}. Below is probably better.
const foo = { bar: { notBaz: 1 } };
const { qux, quux, quuz } = foo?.bar?.baz ?? {}; // this method is better for multiple properties
const quxFromBaz = foo?.bar?.baz?.qux; // this method is better for single properties
For me personally, I think it looks a little messy to include all the optional chaining question marks, but it's better than the alternative with nested destructuring and default values at every level.
And it works with arrays
const foo = {
bar: [
{ a: 1 },
{ b: 2 }
]
}
const c = foo?.bar?.[2]?.c // c == undefined
// bar[2] will be undefined, so trying to access property 'c' would normally throw an error
If you have lodash installed, you can use one of the following:
_.get
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
or if you need multiple keys.
_.at
var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// => [3, 4]
You can also safely pair _.at() up with with Array destructuring. Handy for json responses.
[title, artist, release, artwork] = _.at(object, [
'items[0].recording.title',
'items[0].recording.artists[0].name',
'items[0].recording.releases[0].title',
'items[0].recording.releases[0].artwork[0].url'
]);
Three Levels Deep
In case this helps anyone, here's a bit of code that shows how to destructure three levels deep. In this case, I'm using the find() method on an array.
const id = someId
array.find(({ data: { document: { docId }, }, }) => docId == id)
Above, the array data is structured like this (each obj in the array is the same shape):
[{
isSuccess: true,
isLoading: false,
data: {
foo: bar,
...,
document: {
docId: '123',
...
},
}}]