weird Object Destructuring in Javascript { obj : { } } - javascript

I was looking at someone's code of a chrome extension where there is an API chrome.storage.local.get which basically takes two parameters: key and callback respectively.
suppose we have a object (named Highlights) stored in local storage. if I wanna access that object then I have to pass 'highlights' as first parameter (i.e. key) to the chrome.storage.local.get function.
code should look like :
chrome.storage.local.get('highlights', callback);
it works fine but the developer of the extension used a different approach, his code look like :
chrome.storage.local.get({highlights: {} }, callback);
Notice the key parameter { highlights : {} } surprisingly it gives the same results.
I just wanna know that what does { highlights: {} } means and why does it works.
some extra information :
highlights is an object of arrays.
snapshot of highlights object in console.log :

That's not destructuring. That's an object literal containing a property, highlights, set to an empty object.
According to the documentation (which was harder to find than I would have expected):
keys
string | string[] | object optional
A single key to get, list of keys to get, or a dictionary specifying default values (see description of the object). An empty list or object will return an empty result object. Pass in null to get the entire contents of storage.
(my emphasis)
So passing in that object is saying: "Give me the highlights value from storage, using {} as the default if there isn't any."

Related

Destructuring assignment with rename and typing information

How do I destructure a variable into a new name while keeping typing information?
renderItem({item:'apple'})
// jsx:
function renderItem({item: region}) {
// region == 'apple'
return <div>{region}</div>;
}
The above will destructure an object with item and assign it to region.
How do I express typing information for this function signature?
Type the incoming item like so:
function renderItem({item: region}:{item:string}){}
Information about the typing of the feature is available in the TypeScript 2.1 documentation:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
Object rests are the dual of object spreads, in that they can extract any extra properties that don’t get picked up when destructuring an element:
When the Rest portion of the feature is used, it enhances object destructuring by enabling us to collect the rest of the properties into a new object comprised of them.
We can write the type annotation as we would for any other value. This is prefered as it can stop your function signatures getting verbose For example
interface IRenderItem {
item: String
}
function renderItem({ item: region }: IRenderItem): void {
console.log(item);
}

What is falsy here ? Why does it dedupe? and why does console.log() contain the answer when infront of the foreach?

Q1-Why does this message print with the object even though it is before the forEach?
Q2-Why does this dedupe the new object
Q3-What triggers the falsy here that allow the {} to be added?
var test = [{id:1,name:'s1'},{id:2,name:'s2'},{id:2,name:'s2'},{id:1,name:'s1'},{id:4,name:'s4'}];
var test1 = {};
//Q1
console.log(test1);
test.forEach(function(item){
//Q2 and Q3
var o = test1[item.name] = test1[item.name] || {};
o.id = item.id;
});
<!--Output
{}
​
s1: Object { id: 1 }
​
s2: Object { id: 2 }
​
s4: Object { id: 4 }
​
__proto__: Object { … }
--!>
The console is tricky. It does NOT lie, it just stores a reference to that object. It's not "taking a picture" of the object as it is when you log it, it's actually reflecting the object itself. If you were to update it on click, it would update that reference in the console, too. If you alert it, you would get [object Object] instead of the text. You can use console.log(JSON.stringify(test1)) to print the contents as they were at the moment the object was read in the code.
As for deduping, it's pretty straight-forward, but a bit of a logic puzzle. During one iteration, for example, it will see test1['s1'] and assign it to 's1'. The next time it runs across another s1, it's actually referencing the same property as before, test1['s1'] and reassigning it to 's1' again.
I'll come back to take a closer look at question 3, but it's a good idea to ask one clear question at a time here on StackOverflow. ;)
--
Edit: Well, I'm not exactly sure about how you're getting those values in your log to be honest, because I'm not getting the same results even though I'm running the same script. Check my code on codepen to see my script working as expected!
Q1. As discussed in comments, major browsers now log an inspectable tree of an object, not the conversion of the object to a string using its toString method. The tree is based on a reference to the object and may show property values current when expanding the tree rather than when the object was logged.
Q2. The forEach loop sets properties of test1 using item.name values for items in the test array. If name values are repeated in test entries, only the one property name is updated in test1. Hence the de-duplication - an object can't store separate properties of the same name.
Q3. Initialisation of a test1 property to an empty object only occurs the first time a property is created in test1 for a property name held in item.name. Subsequently, if the same value of item.name is encountered again, it retrieves the pre-existing property from test1 because
test1[item.name] || {};
now evaluates to the existing test1 object property. (If the left-hand operand of an || operator is non falsey, it returns the left-hand operator value as the result of the operation.)
That perhaps leaves the o variable - it is a copy of the reference to an object stored in test1. Updating its id property updates the same object as held in test1. If test had multiple entries of the same name but different id values, the test1 property for the name would hold the id of the last duplicate entry in test.

Difference between toJSON() and JSON.Stringify()

if you need to read or clone all of a model’s data attributes, use its
toJSON() method. This method returns a copy of the attributes as an
object (not a JSON string despite its name). (When JSON.stringify() is
passed an object with a toJSON() method, it stringifies the return
value of toJSON() instead of the original object. The examples in the
previous section took advantage of this feature when they called
JSON.stringify() to log model instances.)
http://addyosmani.github.io/backbone-fundamentals/#backbone-basics
Can anyone tell me the difference between both these ways of representing an object in JSON notation. I am just confused whether these to achieve the same or there is a difference.
From the fine manual:
toJSON behavior
If an object being stringified has a property named toJSON whose value is a function, then the toJSON method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON method when called will be serialized.
This is why Backbone uses the toJSON method for serialization and given a model instance called m, you can say things like:
var string = JSON.stringify(m);
and get just the attributes out of m rather than a bunch of noise that your server won't care about.
That said, the main difference is that toJSON produces a value (a number, boolean, object, ...) that gets converted to a JSON string whereas JSON.stringify always produces a string.
The default Backbone toJSON is simply this (for models):
return _.clone(this.attributes);
so m.toJSON() gives you a shallow copy of the model's attributes. If there are arrays or objects as attribute values then you will end unexpected reference sharing. Note that Backbone.Model#clone also suffers from this problem.
If you want to safely clone a model's data then you could send it through JSON.stringify and then JSON.parse to get a deep copy:
var data = JSON.parse(JSON.stringify(model_instance));
var cloned_model = new M(data);
where model_instance is your instance of the Backbone model M.
JSON.stringify() - Any valid JSON representation value can be stringified.
The JSON.stringify(..) utility will automatically omit undefined, function, and symbol values when it comes across them. If such a value is found in an array, that value is replaced by null (so that the array position information isn't altered). If found as a property of an object, that property will simply be excluded.
JSON stringification has the special behavior that if an object value has a toJSON() method defined, this method will be called first to get a value to use for serialization.
toJSON() - to a valid JSON value suitable for stringification.
One example, JSON.stringify() an object with circular reference in it, an error will be thrown. toJSON() can fix it as following.
var o = { };
var a = {
b: 32,
c: o
};
// circular reference
o.d = a;
// JSON.stringify( a ); // an error caused by circular reference
// define toJSON method
a.toJSON = function() {
return { b: this.b };
};
JSON.stringify( a ); // "{"b":32}"
I'm also reading Addy Osmani's Developing backbone.js application, and I have the same question. I figured out by trying his example (the todo list) in the console.
var Todo = Backbone.Model.extend({
defaults:{
title:"",
completed:false
}
});
var todo1 = new Todo();
console.log(todo1.toJSON())
//The console shows
//Object {title: "finish your assignment", completed: false}
console.log(JSON.stringify(todo1))
//The console shows
//{"title":"finish your assignment","completed":false}

Elements in Javascript object coming out undefined

To construct an object like this:
params (Object) (defaults to: {}) —
Delete — required — (map)
Objects — required — (Array<map>)
Key — required — (String) Key name of the object to delete.
I'm doing this:
var params = {
Bucket : data.bucket,
Delete: [{
Objects:[{ Key:""}] }]
};
for(i=0;i<data.keys.length;i++){
params.Delete[0].Objects[i].Key = data.keys[i];
}
It breaks on the Key with
params.Delete[0].Objects[i].Key = data.keys[i];
^
TypeError: Cannot set property 'Key' of undefined
What am I doing wrong?
It turns out there is only one Key inside of Objects. I'd like to put as many keys as I want inside of Objects so as to properly construct the object. How can I do this?
How many object literals are in the Objects array? From your initialization it only looks like 1. But you are using a for loop, implying you expect more than one.
That error means Objects[i] is undefined for whatever value of i you reach when it occurs.
It seems like you need to do something like
for(i=0;i<data.keys.length;i++){
params.Delete[0].Objects.push({Key: data.keys[i]})
}
to get new object literals into your Objects array as you process your data (change your definition of params to just have an empty array for Objects initially).
Your params.Delete[0].Objects here has only one object in it and it happens when the i is 1. you are counting a for loop based on the other loop with 2 different length.
data.keys
is different from:
params.Delete[0].Objects
this one has only one object, whereas the first one has more than one object.

Simple JavaScript object comparison

I am trying to pass a simple string to an event listener in order to identify an appropriate array object to modify. I understand by looking at the log (shown in comments) that what I am passing is an object and it can't be compared directly to another object's property.
Should I pass an object that has the string as a property (like the array object does), or is there a more appropriate way to reference the object as a string or call its name somehow (like the log does in the first comment)?
// I just want to pass a string as an argument. Here is a static example.
var timestampID = '1307740835264';
Ti.App.fireEvent('confirmation',timestampID);
Notice how the first log output interprets the argument as a string, but the if comparison recognizes it as an object.
Ti.App.addEventListener('confirmation',function(timestampID) { // produces "[DEBUG] fire app event: confirmation with 1307740835264"
Ti.API.info(timestampID); // produces "[INFO] { type = confirmation; }"
for (i=0;i<myArray.length;i++) {
Ti.API.info(myArray[i].timestampID + ' | ' + timestampID); // produces "[INFO] 1307740835264 | [object Object]"
if (myArray[i].timestampID == timestampID) { // will produce false
// will never happen
}
}
});
Thanks!
So to me it looks like the timestampID being passed in to the handler is an object, however from the second statement (and accompanying [INFO] comment), I have absolutely no idea what properties it has. Let's assume that it has a property called timestamp. Then your if statement should be
if (myArray[i].timestampID === timestampID.timestamp)
But that's about all I can glean from this code snippet I'm afraid.
UPDATE: OK, I see how you're triggering the event. The Titanium API is bafflingly obtuse on this point (it looks like the help for fireEvent is wrong: two parameters called "name"?). There are no examples that I can see. However it does say that what you pass in as the data is (must be?) serialized as JSON.
Now that I know that, looking at the second statement's [INFO] line it makes more sense. It's a string that has the JSON data. Your data is missing since it was a string value. Try this to fire:
Ti.App.fireEvent('confirmation', { timestampID: '1307740835264' } );
and this in the event handler:
Ti.App.addEventListener('confirmation', function (data) {
for (i=0;i<myArray.length;i++) {
if (myArray[i].timestampID === data.timestampID) {
...code...
}
}
});
Either write a comparison function or find the scalar property (Number or String) that you want to compare.

Categories

Resources