Are any aspects of object destructuring assignments by reference? - javascript

I have a program that is incrementing requests on a session cookie and printing them out to the console. Initially, I was trying to figure out how I could save this data. After logging in a couple places, I realized that the data was being saved/changed despite me having a seperate variable to hold what I thought was a temporary version of the req member object.
This is the code that made me realize that the actual object was being changes when I incremented the variable I assigned it to:
recordRequest(req) {
const { ip } = req.info;
const { requestsPerSecond } = req.session;
if (req.originalUrl.split('/').filter(Boolean)[0] == 'www.example.com') {
requestsPerSecond[ip] = requestsPerSecond[ip] + 1 || 1;
}
console.log(req.session.requestsPerSecond);
}
I can't seem to find in the docs here or on Mozilla whether or not this is intended behavior, whether or not this is a result of my use of const (where you can mutate member variables), or there is some kind of weird bug going on. I also had trouble reproducing this example on a smaller scale, but I verified that nothing going in or going out of the function is affecting this chunk of code.
It's not breaking my code or anything (it's actually making my life easier) but I want to understand why this is happening!

I would default to object destructuring working essentially the same as normal assignments. Consider:
const req = {session: {requestsPerSecond: {"0.0.0.0": "foo"}}};
const requestsPerSecond = req.session.requestsPerSecond;
// updates to `requestsPerSecond` will also update `req`.
I'm not sure you can use destructuring to break the assignment, so you will have to use normal tactics:
const requestsPerSecond = Object.assign({}, req.session.requestsPerSecond);

From MDN:
The destructuring assignment syntax is a JavaScript expression that makes it possible to extract data from arrays or objects into distinct variables.
If this data happens to be an object reference, this object reference will be copied into the new variable, or in your case constant.
Minimal example:
const orig = {
foo: {
bar: 1
}
}
const { foo } = orig;
console.log(foo.bar); // 1
console.log(orig.foo.bar); // 1
foo.bar++;
console.log(foo.bar); // 2
console.log(orig.foo.bar); // 2

Related

Is there a way of listening/proxying a variable in vanilla Javascript?

I'm building a web framework, something like React; one of the things which I would like to improve on React is state.
My idea is something like Svelte, to use state you just create a normal variable (in my case it would be okay to use a function when creating te state, but not when updating it), but how Svelte does this Magic is by compiling, and I would like it to work in vanilla Javascript.
From my understanding this is not exactly possible, but I've still been trying to hack something somehow.
So the part of this state system that is not possible is knowing when a primitive is set and got (setters & getters), I want it to work with scoped variables; so I can't use the Object.defineProperty on the window or globalThis. I've been hacking around for quite some time and here are the only solutions I thought have could worked:
Proxing a new String(string), has given weird error of this beeing of the wrong type, unknows values, and stuff.
Proxing the Funtion.arguments object, but this didn't work.
Using Symbol.toPrimitive, but I couldn't find a way of using it without a + or ${}.
But as you can see they all have problems, I'm stuck and can't find anything, is there any (even if hacky, though without legacy or deprecated code) way to do this? Thank you!
You can't do what you've described in JavaScript. You can't proxy a primitive, and you can't run code some other way (getter, setter) when a variable is read or set, only when a property of an object is read or set.
There is an awful thing you can do in loose mode that's disallowed (for good reasons) in strict mode where you have an object with getters and setters that you then put into the environment used for resolving freestanding identifiers using the with statement, but again, it's disallowed for good reasons in strict mode (which is the default for modules and other mechanisms that create new contexts, like the body of a class).
I hesitate to give an example of it, but for completeness:
// This only works in loose mode, not strict mode
let a = 0;
const obj = {
get a() {
console.log(`Getter called, returning a = ${a}`);
return a;
},
set a(value) {
console.log(`Setter called, setting a = ${value}`);
a = value;
}
};
with (obj) {
console.log(a);
a = 42;
console.log(a);
}
Re your updated question:
My idea is something like Svelte, to use state you just create a normal variable...but how Svelte does this this Magic is by compiling, and I would like it to work in vanilla Javascript.
I wouldn't try to do it with freestanding variables, have the user provide a state object and convert its data properties to getter/setter combinations (or replace it with a new version with getter/setter combinations, etc.):
// Userland code provides a state object
const state = {
a: 0,
b: "hi",
};
// Your framework code converts it to using getters/setters
function enhance(obj) {
const descrs = Object.getOwnPropertyDescriptors(obj);
for (const key of Object.keys(descrs)) {
const descr = descrs[key];
if (descr.configurable && "value" in descr && typeof descr.value !== "function") {
// A simple data property; wrap it in getter/setter
let value = descr.value;
if (typeof value === "object") {
enhance(value);
} else {
Object.defineProperty(obj, key, {
get() {
console.log(`Getter called, returning ${key} = ${value}`);
return value;
},
set(newValue) {
console.log(`Setter called, setting ${key} = ${newValue}`);
value = newValue;
},
enumerable: descr.enumerable,
configurable: true,
});
}
}
}
}
enhance(state);
// Their code using the properties triggers your getter/setters:
console.log(state.a, state.b);
state.a = 42;
state.b = state.b.toUpperCase();
console.log(state.a, state.b);

'inStock' is assigned a value but never used no-unused-vars

I am trying to refactor some code and wanted to use destructuring to make the code a bit easier to read. I am passing in an object into the remove() function, however one of the variables is returning:
'inStock' is assigned a value but never used no-unused-vars
According to the eslint docs:
'A read for a modification of itself is not considered as used.'
https://eslint.org/docs/rules/no-unused-vars
I was wondering whether if there's a way to resolve this issue without:
modifying eslint (no-unused-vars) ?
adding /* eslint-disable no-unused-vars */ comments inline ?
Thanks in advance
before refactoring:
remove(product) {
if (product.quantity > 0) {
product.quantity --;
product.inStock ++;
}
}
after refactoring:
remove({ quantity, inStock }) {
if (quantity > 0) {
quantity --;
inStock ++;
}
}
Problem
Your refactor is based on the misunderstanding that quantity-- and inStock++ has the same behaviour as product.quantity-- and product.inStock++. The core of this misunderstanding is thinking that the destructured fields still refer to the fields on the object. Let's take a look at what destructuring really is.
Take this example, with destructuring:
const obj = { foo: 0 };
const { foo } = obj;
Without destructuring, it'd look like this:
const obj = { foo: 0 };
const foo = obj.foo;
The destructuring syntax is just a shortcut of the second example.
What this illustrates is destructuring defines a new variable and assigns the value (not the reference) of the field you're destructuring to that variable. When you make mutations to that variable, you'll only mutate the value of the variable but not the value of the object field. Here's a runnable example, you'll see that obj.foo hasn't changed from 0, even though we increment foo which has been destructured from obj:
const obj = { foo: 0 };
var { foo } = obj;
foo++;
console.log(obj); // { "foo": 0 }
Going back to your linting error: the linter is correct, not only raising the fact that the variable is unused but revealing this problem that I've explained.
Solution
There are two immediate solutions I can think of:
Don't destructure. With your example, while destructuring does shorten the amount of code there is, it could introduce confusion as to what is mutated and make the code less understandable – you've experienced it yourself.
Reassign the incremented/decremented value back to your object. You'll need to prefix the operator to the operand because this returns the value after the operation. Postfixing returns the value before the operation (aka the original value).
remove({ quantity, inStock }) {
if (quantity > 0) {
product.quantity = --quantity;
product.inStock = ++inStock;
}
}
If you ever learn about functional programming, specifically pure functional programming, there might be other patterns you can apply to your scenario but that's beyond the scope of this answer. These patterns won't do mutations which could make it easier to understand how data flows through and is changed by your system.

Convert var to const javascript

Is it possible to convert a var to a const?
Say in a scenario like this:
var appname = ''
function setAppName(name) {
appname = name // can I convert this to const now?
}
Basically, this is for a parameter level. Parameter needs to be accessible throughout the file and I want to make it const after first assignment. Is this possible?
To add: If there is any other way besides creating an object with to contain this parameter, it would be better (for a more straightforward solution). I will be working on multiple parameters like this and freezing an object will cause the entire object to freeze. I am hoping to do this on the parameter level.
So either change the parameter to become const. OR (this one I dont think is possible) change the scope of the paramter to global:
function setAppName(name) {
const appname = name // can I change the scope and make this global?
}
Thanks.
Put your app name in an object and freeze it.
let appSettings = { name: "" };
function setAppName(name) {
appSettings.name = name;
Object.freeze(appSettings);
}
Freezing prevents adding, removing and modifying the values of the properties in your object so you should call Object.freeze only once after all other settings (if there are more variables you want to make constant) have been set.
You can do this using a object.
const globals = {
appname: ''
}
function setAppName(name) {
globals.appname = name;
Object.freeze(globals)
// After this point, value of appname cannot be modified
}
Thank you for all your inputs.
I was able to find a workaround for what I was trying to achieve.
var appname = ''
function setAppName(name) {
if (appname === '') {
appname = name // can I convert this to const now?
}
}
Although this doesnt convert it to const, I just added a guard on the setter and it will not be able to overwrite the value now (unless otherwise I am going to initialize again to an empty string).
It is not fool-proof, but this will address my need.
Thanks all!

ES6 - Variable being defined inside closure with a name same as a variable in parent scope yields undefined

I spent hours looking at this piece of code:
1: const { services, lists } = this.props;
2:
3: const List = _.map(lists.list, (list) => {
4: const services = _.filter(services.list, (service) => lists.services.indexOf(service.id) > -1);
5: ...
120: ...
121: });
At line number 4, I get an error:
Cannot read property list of undefined
At line number 4, property list is being referenced by services, apparently. And, it is undefined. But, at line 1, it's defined which can be checked by logging its value.
I know the value of services becomes undefined in the closure because JS has its way of finding the variable declarations first and assigning undefined to them before executing the given function. My concern is that, should it be the behavior with the given piece of code considering it's being pre-compiled using Babel?
Edit: I realized that this was your actual question:
My concern is that, should it be the behavior with the given piece of code considering it's being pre-compiled using Babel?
The answer is no, Babel's job is to convert code into its older equivalent, the equivalent here being a var statement. The const statement you have there is perfectly valid syntactically, but is destined to throw an error semantically (note: if you actually executed that const statement in an environment that supports const, it would throw an error before you even tried to access service's list property).
Original answer:
If you define a variable in the same line that you use it, it will always be undefined the first time the statement tries to access it, even if there's a variable with the same name in an outer scope:
const a = 2;
function f() {
const a = a + 1; // error
console.log(a);
}
f();
The solution: don't reuse variable names in inner scopes. Just come up with new names for your variables:
const filteredServices = _.filter(services.list, (service) => lists.services.indexOf(service.id) > -1);

Persistence framework for JavaScript / Google v8

Is there any kind of persistence framework for JavaScript and/or the Google v8 engine?
I want to store (serialize) a whole graph of objects (including, e.g., functions) and re-load it later. JSON is not sufficient, since it does not permit functions to be stored and permits only a tree-like structure (i.e. no two objects referencing the same object).
I need to be able to do that generically (i.e. without knowing the JavaScript code at the time at which I write my program embedding v8), since I want the user of my program to be able to customize it with JavaScript, but I need to store the state of my program (including the state of the customization) and re-load it later. Hence I need to store the state of the JavaScript engine.
Edit:
Example:
Suppose we have the following code:
var obj = { a: 4, b: function (x) { return x + this.a; } }
// ...
if ( ... ) { obj.a = 5; }
// ...
if ( ... ) { var c = 1; obj.b = function (x) { return x + this.a + c; } }
// ...
// now I want to serialize obj
Then is it (without any meta-information about the logic of the program) possible to serialize obj and later deserialize it such that obj.b (2) delivers the same result after deserialization as it did before serialization?
Second Edit: Note the closure.
Unfortunately, what you're trying to do is not currently possible in Javascript. The reason is that closures are not just objects, they're objects bound to an execution context.
Getting past the "this can't be done in javascript" issue and moving into the "what if wrote a patch for V8 to allow this" phase of the answer, this is conceptually difficult. Essentially, for every closure you'd serialize, you would have to serialize the Context object that the closure exists in. It'd be nice to be able to just serialize the HandleScope, but the nature of closures is that you can't reach inside them.
Okay, so let's say you've written a function that can serialize the Context that the closure exists in, and you can even deserialize it. What do you do with it?
The answer to that is 'not much'. Javascript can only be executed in a single context at a time. The closure that you've deserialized doesn't exists in the context that you're trying to pull it back into. You can't really pass data between contexts, and if your function has data bound to free variables, do you use the ones that exist in the deserializer-invoking context, or do you overwrite it with the deserialized context? Conceptually, this is a nightmare.
Ecmascript Harmony had considered giving us nearly-first-class continuations, but it's been pushed form the discussion which I rant about here, but this isn't going to happen any time soon.
HTML5 local storage allows persistence at client level through javascript.
I'm not sure if it will fit your needings, as to being able to store a function you'll need to somewhat give it some markup that allows you to deserialize it when retrieving it from storage (or maybe just store it as plain text and try to eval it on retrieval)
http://diveintohtml5.info/storage.html
I don't think persisting functions is a good practice. I can suggest you the below approach. Turn your JSON data to lets say some class like "MyData". You can find two functions fromJSON, toJSON which will do the magic you want.
var MyData = function(props){
this.temp = "a";
this.getTemp = function(){
return this.temp;
}
this.fromJSON = function(props){
if(props){
this.temp = props.temp;
}
}
this.toJSON = function(){
var props = {};
props.temp = this.temp;
return props;
}
this.fromJSON(props);
}
var obj = new MyData({"temp" : "b"});
var state = obj.toJSON();
// persist state about the object as JSON string
LOCALSTORAGE.put(state); // You can write some HTML5 local storage stuff to persist
var persistedState = LOCALSTORAGE.get(); // You can use the above HTML5 local storage stuff to read the persisted stuff
var newBornObj = new MyData(persistedState);

Categories