This question is somewhat for understanding the prototype get-set.
I've a scenario where I need to map the SpeechSynthesis supported voice to Google Translation API supported language codes.
For example,
Now, I can do the same by either fetching the voices runtime or by storing the voice and hard-coding the mapping in some method in javascript.
If I go for runtime approach, I need to call getVoice() in speechSynthesis.onvoiceschanged = () => {} and then map the numbers, which gets called in every voice change event. So, I want to go for hard-coding one.
Now, when I store the mapping array in a variable and call it by index, I get the SpeechSynthesisVoice Object, just like what we do in getVoices()[index].
Further, If I set this object value to speechSynthesis.voice, I get an error:
Uncaught TypeError: Failed to set the 'voice' property on 'SpeechSynthesisUtterance': The provided value is not of type 'SpeechSynthesisVoice'.
This is due to mis-match of prototype of the manually stored object value.
For example,
1. SpeechSynthesisVoice object:
2. Manually stored value of SpeechSynthesisVoice object:
To resolve, this I've fetched the proto of the SpeechSynthesisVoice object using getVoice(), and then set it to a variable and further, set this variable to my manual mapped object.
Like,
Get:
voicePrototype = getVoices()[9].__proto__;
Set:
voices[index].SpeechSynthesisVoice.__proto__ = voicePrototype;
And, it gets set, as in below screenshot:
I've also tried through Object.setPrototypeOf() and got the same result.
Now, again when I want to set this object to speechSynthesis.voice, I still get the same error, although my prototype matches.
Can anyone, please advise, if its possible to set the object proto likewise and use it? Thanks in advance.
Related
Is there any place where it is specified exactly how the properties of localStorage work? The only thing I found in the standards is:
value = storage.getItem (key)
value = storage[key]
Returns the current value associated with the given key, or null if the given key does not exist.
storage.setItem (key, value)
storage[key] = value
Sets the value of the pair identified by key to value, creating a new key/value pair if none > existed for key previously.
But it doesn’t say anything about what happens if key is a special property name like length or setItem (or __proto__ or constructor).
In practice this seems to work as one would expect: “special” properties work as usual, and only “unused” property names can be accessed with the bracket syntax.
But stuff gets a bit weird if you look at the details. At least on Chrome, if you add a value using as key a “special” property name, like localStorage.setItem('length', 'test'), then, somehow, Object.keys(localStorage) does include 'length' in the result. Also, if you try Object.getOwnPropertyNames(localStorage), 'length' is included. But if you try Object.getOwnPropertyDescriptors(localStorage), no property named 'length' is returned.
I can’t find any specification, or even description, of how these work. The ECMA standard mentions that “exotic” objects can override “abstract operations” with special behavior. But I couldn’t find any specs for how that is supposed to work, exactly, with localStorage. All that I could see are cursory mentions about behavior in specific browsers, and those are usually old and I presume obsolete.
So, in short: Is the detailed behavior of the localStorage specified anywhere?
I have an odd situation in my Javascript code which I cannot explain. So far it has been observed only on Safari/Mac.
I have a line like this:
dict[id].sliceHovered = true;
And sometimes it throws an error:
Attempted to assign to readonly property.
Also:
dict is a bare blank object which I create myself with dict={}.
id is supplied by outside data, so it can be anything (I don't yet know which particular value causes this).
sliceHovered is obviously not a name of something that Javascript has built
in.
The objects in the dict are of my own type. They have a sliceHovered member, but it's not a Javascript defined property (as in Object.defineProperty()), just a regular property (the constructor executes this.sliceHovered=false).
"use strict" is on.
Object.freeze(), Object.seal(), Object.preventExtensions() and const are not used anywhere in the entire codebase.
Thus it's extremely puzzling as to how such an error could be thrown here. If I had an indexing error and dict[id] would be undefined or null, the error would be different. My only idea is that since the dict is created as dict={} then it inherits from Object and maybe id maps to some inherited property. But that means that the object returned from dict[id] would have to be read-only itself, because sliceHovered is definitely not a name of an existing Javascript property.
However I cannot think of any Javascript objects that would be intrinsically read-only like that.
Any ideas what could be wrong here?
You can check this situation
My only idea is that since the dict is created as dict={} then it inherits from Object
with: var dict = Object.create(null);
Also try to use Object.getOwnPropertyDescriptor(dict, id) to make sure descriptors have right values.
Last time I checked, the following two lines returned true:
null == localStorage["foo"];
null == localStorage.getItem("foo");
Same applies when replacing null with undefined.
So the first question is, why are there two ways to address the localStorage? And why does
localStorage["foo"]
return undefined while
localStorage.getItem("foo")
returns null?
Do I need to take care of that when developing JS?
The Web Storage Specification requires that .getItem() returns null for an unknown key.
Note however that .getItem() and .setItem() are specifically defined in the IDL as being the designated getter and setter for the Storage interface, and therefore they're also fully supported ways of accessing the contents of the storage.
However the [] syntax is more akin to a normal object and/or array property getter, and like those returns undefined for an unknown property name.
The reason not to use [] syntax is that it will operate on object properties first and will quite happily allow you to overwrite real properties and methods of the localStorage object, c.f:
> localStorage['getItem'] = function() { return 0 }
> localStorage.getItem('getItem')
0
localStorage["..."] is invalid usage of localstorage. You are trying to access methods of the localstorage object, rather than accessing actual database.
You have to use
localStorage.getItem("...")
and
localStorage.setItem("...")
methods to access storage database.
In javascript you always get an undefined value for keys that does not exist inside an object.
a = {}; //new object
alert(a["test"]); // you get 'undefined' because "test" keys is not found
In localStorage .getItem is a method who does check keys inside the localStorage object and returns null if not found.
Don't blame javascript, it's just the localStorage object behaviour
I've been working angular for probably six or so months and so have been working a lot more with JSON and objects in javascript. This situation I've been running into has been frustrating and I don't understand the reasoning. Maybe it's my inexperience with JS and so I don't know the reasoning or maybe I'm just doing it wrong but I'd appreciate any insight into this.
Example an object:
var data =
{
"object":
{
"Key":"Value",
"stuff":"here"
},
"array":
[
{
"Name":"Pizza Face",
"id":7
},
{
"Name":"Maple bar",
"id":1
}
]
}
So, let's say I want to add an object to the array. Easy, right?
var newObject = {"Name": "Master Sword", "id":2}
data.array.push(newObject);
But, let's say I want to add the object to the array before the array exists. The above method errors out saying Cannot read property 'push' of undefined. Instead, I have to wrap a condition around it.
var newObject = {"Name": "Master Sword", "id":2}
if(data.array){
data.array.push(newObject);
}
else{
data.array = [];
data.array.push(newObject);
}
I am allowed to add new objects to data whenever I want. However, woe unto me should I try to add an object within an object.
Example:
data.newObject = {"Name": "Master Sword", "id":2}
newObject will be created in this case and everyone is happy.
data.newObject.Name = "Master Sword"
FAIL! Cannot set property 'Name' of undefined
So...why doesn't JS just create newObject with the laid out key value pair? Or when I try to push to an array that doesn't exist why not just create the array rather than error out?
I want to be clear, although this is frustrating to me my purpose of this question isn't to complain. It's to ask if I'm doing this wrong. Do I really need a condition to check if the object/array exists before trying to add to it? Is there a better way to do this?
You are trying to create objects via chaining. The problem with that is except the last part in the chain, all the previous ones must be already defined Javascript Objects.
a.b.c.d = new Object(); // a,b,c must be already defined, cannot have cascading here
So, when you say data.newObject.Name = "Master Sword";, the newObject should have been defined earlier (you already had a data object I assume). This is how it has to work (probably in every language that follows at least some semantics). To get around such a problem, you can always create a new generic object say:
var myComplexObject = function() { //add all reusable object constructors here };
Populate your complex object any way you like, e.g. creating a new empty object in this constructor and add Prototypes to this constructor to expand the functionality. Write once and iterate forever over that. You could build a library suited to your requirements. That is how it should be done.
P.S.: Probably, for the checking part you could encapsulate in a try{}catch(e){} block.
The method push is located on a javascript Array object, unsurprisingly that method will not work on either null or undefined. This is the same for pretty much every programming language.
When calling data.newObject = {"Name": "Master Sword", "id":2} you are quite literally setting the property to a new object, this is a very different scenario to the above.
That´s because when you try to reference a key of an object, if that key doesn´t exist then that key will be kind of created and the undefined value will be assigned to it. undefined is just a JS Value type. If you want to check that "undefined" is not an error, just note that "undefined" is written with gray color on the console. (Errors have the red color).
If you assign an object to a key of other object, and you want to add properties to it, that won´t represent a problem at all.
But if you try to add a property to a key that can be initialized with undefined value, then it's reasonable that you will get an error message. Remember, JS allows you to store any value type you want on objects.
I suggest you to read a little bit more about objects, and the undefined value in JavaScript.
Hope that it helps.
This code sums up my question:
localStorage.setItem('getItem', 4)
undefined
localStorage.getItem
function getItem() { [native code] }
localStorage['getItem'] = 'user'
"user"
localStorage.getItem
"user"
localStorage.getItem('getItem')
TypeError: Property 'getItem' of object #<Storage> is not a function
Using the setItem method or using the property accessor changes the way localStorage reacts.
Is this a bug? A specification inconsistency? Something else?
I feel like I should report this somewhere, but don't really know where.
Using the setItem method or using the property accessor changes the way localStorage reacts.
Is this a bug?
It doesn't seem like it. From the spec:
When the setItem(), removeItem(), and clear() methods are called on a
Storage object x that is associated with a local storage area, if
the methods did something, then in every Document object whose
Window object's localStorage attribute's Storage object is associated
with the same storage area, other than x, a storage event must be
fired, as described below.
The localStorage object is an IDL attribute with a reference to the Storage interface. The Storage interface extends Object.prototype, which brings in extra properties, and also defines its own set of properties.
The setItem method stores a property in the storage list. The object also fires an event when there are changes on the object properties, and updates the storage area in those cases.
So the expected behavior for setItem is to store the key value pair in the storage list. The expected behavior for getItem is to pull the value associated with a key from the storage list.
So what is the expected behavior when we try to access a property directly?. This line says that the key-value list acts as "supported properties":
The supported property names on a Storage object are the keys of each
key/value pair currently present in the list associated with the
object.
What does this mean? It means that those properties are mapped to the getters and setters defined by the interface spec. For Storage the interface specification looks like this:
interface Storage {
readonly attribute unsigned long length;
DOMString? key(unsigned long index);
getter DOMString getItem(DOMString key);
setter creator void setItem(DOMString key, DOMString value);
deleter void removeItem(DOMString key);
void clear();
};
So localStorage["x"] is mapped to the value of getItem("x"), which returns the value of "x" in the storage list, and localStorage["x"] = y will be mapped to setItem("x",y).
However, these will only be run if there is not a native property by that name on the object unless the overridebuiltins attribute is set on the interface (which it is not for Storage).
If the [OverrideBuiltins] extended attribute appears on an interface,
it indicates that for a platform object implementing the interface,
properties corresponding to all of the object’s supported property
names will appear to be on the object, regardless of what other
properties exist on the object or its prototype chain. This means that
named properties will always shadow any properties that would
otherwise appear on the object. This is in contrast to the usual
behavior, which is for named properties to be exposed only if there is
no property with the same name on the object itself or somewhere on
its prototype chain.
So for properties we should expect that if a function is defined on an object it will always access/set that on a request. Otherwise it will check the storage list and see if it has a value and change or return it.
Summary (TL;DR)
The expected behavior from the spec, and what we find when you test is that setItem and getItem will store values of pre-existing properties/functions as key/value pairs, and access them, without overwriting the existing values. When we attempt to access or modify those values directly through localStorage["getItem"], we'll instead hit the local properties, because the "supported property" spec calls for not overriding built in properties by default.