Why is that Object.freeze(obj) is made to fail silently when obj is mutated?
Wouldn't it be more logical to throw than error when an immutable object is mutated? Then, it would be to the user's discretion if he wants to wrap a try catch around his Object.freeze(obj). Similar to how we need to wrap JSON.parse() with try catch to avoid errors with empty strings or malformed JSON's.
I wish if the community can make a comment.
Edit after few answers and comments:
It is understood that Object.frozen() throws error in strict mode. But why is it necessary for the method to fail silently in non-strict mode?
Object.freeze() does throw, in strict mode.
In strict mode, the following program will throw any time a property is added, mutated or deleted on the frozen object.
Assume we have
'use strict'
const obj = Object.freeze({ test: true })
Error when adding a property:
obj.other = true
// TypeError: Can't add property another, object is not extensible
Mutating an existing property throws:
obj.test = false
// TypeError: Cannot assign to read only property 'test' of object '#<Object>'
Deleting a property throws:
delete obj.test
TypeError: Cannot delete property 'test' of #<Object>
If would like to understand strict mode in more detail (highly recommended) have a look at the official MDN docs for the differences.
The opinions on this topic vary - one would expect mutations of the object to throw because "you are doing something you should not be doing" while others might say that if you Object.freeze() something, you just want to prevent any modifications to it. And that you successfully accomplish. If it caused applications to crash on mutations, programmers would have no choice but to either check all objects they did not create for their frozenness or they would have to wrap all such operations in try/catch block. This is simply too much to ask.
Note about try/catch
You can avoid wrapping property manipulations in try/catch blocks simply by checking the object's temperature 😎:
if (Object.isFrozen(obj) {
// Can't touch this!
}
I think this is why modifying a frozen object fails silently,
If error is thrown, execution of code will stop. Try and catch
statements are available to handle the error. Is it okay to use try
and catch statements for every line in your code to avoid the error?
I think no
1. Unnecessary use of try and catch blocks in code
Let's say an object is frozen at some point in the flow of control. There maybe many lines in code which try to add a new property or modify the value of an existing property in the frozen object. In this scenario, if error is thrown, then you need to add try and catch blocks in all those lines of code.
2. Any number of objects can be freezed
Let's say more than one object is freezed. This will increase the need for more try and catch statements. It may lead to enclosing each line of your code with a try and catch block
Related
im trying to learn gjs and having a few questions which i think are basic about ParamFlags.
I'm creating a subclass and trying to use GObject.ParamFlags.CONSTRUCT_ONLY | GObject.ParamFlags.READABLE for one of the parameters. The use case being what it sounds like, being able to set it only during construction and later being able to read it. I'm declaring it like:
magic_word: GObject.ParamSpec.string(
"magic-word",
"Magic word",
"My magic word",
GObject.ParamFlags.CONSTRUCT_ONLY | GObject.ParamFlags.READABLE,
"magicword default value"
),
But this throws the following error during module loading.
GLib-GObject-CRITICAL **: 14:18:53.750: validate_pspec_to_install: assertion 'pspec->flags & G_PARAM_WRITABLE' failed
It still allows the program to continue execution though.
It seems the only way to get this error to stop is to also give it the WRITABLE or READWRITE flag and perhaps implement the desired behavior through getter/setter. But then the purpose of CONSTRUCT_ONLY seems lost. What am I missing?
G_PARAM_CONSTRUCT_ONLY means a property can only be set during construction, implying that the property must be writable. In other words, CONSTRUCT_ONLY is simply a constraint on WRITABLE.
The merge request adding JS-level support for G_PARAM_CONSTRUCT_ONLY might help clarify how this works in GJS: https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/377. Keep in mind this was only merged in November 2020.
Until today, I had not known the with operator existed. I stumbled upon it while debugging an issue being thrown from a plugin (Backbone.Epoxy).
The operator creates block level scope for each property on the passed object.
var testObj = { "cat":true };
with (testObj) {
console.log(cat ? "Cat!": "dog"); // Cat!
}
Simple enough? Initially I thought this could potentially be really cool. Until I realized why my code was throwing an error. Here is an example derived from my code.
var testObj = { "css":true, "background-color":"blue" };
with (testObj) {
console.log(css ? background-color : ""); // throws
}
The actual code is a bit more dynamic, but this is essentially what occurs behind the scenes in the plugin. Since dashes are not allowed within variable names but are allowed in property names, which cause the error to be thrown.
So, to the questions:
Is there a way to sanitize the block scope local variable in order to avoid the issues with the dash while keeping it in my property name?
Has anyone else worked around this issue with epoxy?
You would have to make an exception and write:
testObj["background-color"]
As you may suspect, you cannot write just background-color, for the same reason you cannot write testObj.background-color. You should also ask whether using with, which is fairly non-standard, is worth the character-count savings. Usually the answer is "no".
I've always wondered if there was a way of improving the error message one gets when attempting to use a function that doesn't exist (or that isn't a function). For example:
document.iDontExist()
TypeError: undefined is not a function
I'd like to be able to log something like:
document.iDontExist is not a function
Just to save a few seconds. I realise I have line numbers etc, I'm just wondering whether this is possible.
Is there some property of the TypeError I could use to look up the name that was attempted?
No - not generally.
JavaScript doesn't "send messages" to invoke functions and any expression construct can have the () operator applied .. none of the browser engines try to "read into" the expression to report a more useful message.
A debugger (with break-on-exception) can be useful, but otherwise no - the exceptions will remain general without manual intervention.
Unfortunately not, but if this is an option for you, it could work...
Instead of calling document.iDontExist(), try:
(document.iDontExist || logError("iDontExist undefined")) && document.iDontExist();
This abuses logical operators to only call the function if it exists, and log an error (assuming logError exists!) otherwise.
I experiences this problem very often and finally thought that it might be worth a question here.
Running the following code in IE, results in the output frameElement, which means, that the property frameElement was found but if you try to access it via window["frameElement"] it throws an error.
for (var i in window){
try {
var c = window[i]
} catch(e) {
console.log(i);
}
}
FIDDLE
I originally realized this problem when i tried to simply access every property of window, but always ended up with an error.
Does anyone know the reason for this? How can it be, that there is a property but that it is not accessible?
This can happen on any browser. Properties on any object, including Window, can be getters methods that can have side-effects including throwing exceptions.
Object.preventExtensions and Object.seal prevent unknown properties from being added to an object, but those attributions silently fail instead of throwing an error. Is there a way to force them to be errors?
var myObj = Object.seal({});
try{
myObj.someProp = 17;
console.log("I don't want to reach this message");
}catch(err){
console.log("I want an error to occur instead.")
console.log("Or at least get a warning somewhere.");
}
I tested this in Chrome 19 and Firefox 9. I wouldn't mind browser-specific solutions either, since I would only need this during development.
If strict mode is an option, Object.seal seems to do the trick (at least in Firefox):
Attempting to delete or add properties to a sealed object, or to convert a data property to accessor or vice versa, will fail, either silently or by throwing a TypeError (most commonly, although not exclusively, when in strict mode code).
Works pretty good: http://jsfiddle.net/yVWr6/
(btw: works the same for preventExtensions())
According to the MDN documentation for Object.seal() (and Object.freeze()) if you enable strict mode then a TypeError will be thrown when attempting to modify a sealed object:
function go() {
'use strict';
var x = {};
Object.seal(x);
x.foo = 123;
}
go(); // => TypeError: Can't add property foo, object is not extensible