Does destructuring create a new variable? - javascript

Let's say I have this:
const something = {
someProp: {
moreProp = 5
}
}
with destructuring we get moreProp:
const { moreProp } = something.someProp
to get moreProp without destructuring:
const morePropAlias = something.someProp.moreProp
In order to access moreProp in the original object we need 2 accesses. My question essentially is that do we also need 2 accesses with the destructuring way or none at all?
Edit: Perhaps I wasn't clear enough, I meant accessing 1) the destructured moreProp and 2) the morePropAlias. In other words, the alias var doesn't do any access (with dots) when we access it, because we access it directly. On the other hand, is destructuring just syntax sugar for us, so behind the scenes it still gets accessed like something.someProp.moreProp?

In either case two accesses happen. The destructuring notation is not skipping steps somehow.
In the following snippet I replaced the properties with getters, so that we can log when an access is performed:
const something = {
get someProp() {
console.log("get someProp");
return {
get moreProp() {
console.log("get moreProp");
return 5;
}
};
}
};
// Using blocks to get local scopes
{
const moreProp = something.someProp.moreProp;
}
console.log("----");
{
const { moreProp } = something.someProp;
}
console.log("----");
{
const { someProp: { moreProp } } = something;
}
In all these different notations, the number of accesses is the same.

when You wrote [some variable = access to data in object] you created variable with variable (created dynamically or not) from object if you want some action everytime you have to use getter or simple function everytime, it's clear and logical... or maybe I don't understand ur question.

Related

export a variable inside a function to be used in other modules

I'm quite new to programming. I have spend last 3 days trying to figure this out. So any help really appreciate it.
I have access to clientID inside a for loop. (Please look at the last line in the code below). There is only one ClientID which I'm successfully able to print.
I want this client ID to be exported from this module so that I can use it in other modules.
import { Configurations } from './models';
type MethodNames = 'init' | 'event';
export const DEFAULT_NAME = '_hw';
interface LoaderObject {
q: Array<[MethodNames, {}]>;
}
export default (
win: Window,
defaultConfig: Configurations,
scriptElement: Element | null,
render: (element: HTMLElement, config: Configurations) => void
) => {
const instanceName =
scriptElement?.attributes.getNamedItem('id')?.value ?? DEFAULT_NAME;
const loaderObject: LoaderObject = win[instanceName];
if (!loaderObject || !loaderObject.q) {
throw new Error(
`Widget didn't find LoaderObject`);
}
if (win[`loaded-${instanceName}`]) {
throw new Error(
`Widget already loaded`)
);
}
let targetElement: HTMLElement;
for (let i = 0; i < loaderObject.q.length; i++) {
const item = loaderObject.q[i];
const methodName = item[0];
console.log(methodName);
if (i === 0 && methodName !== 'init') {
throw new Error(
`Failed to start Widget`);
} else if (i !== 0 && methodName === 'init') {
continue;
}
const valueObject = Object.assign(defaultConfig, item[1]);
const clientID = valueObject.clientID
console.log("ClientID", clientID)
}
//rest of the code....
I have also tried this. defined a variable clientID outside the for loop and then storing value from inside the for loop. But on printing, I'm getting undefined
var clientID;
console.log("....last Client ID", clientID)
const valueObject = Object.assign(defaultConfig, item[1]);
clientID = valueObject.clientID
Your issue is about your variables' scopes. Let's start with a simpler version, without for loop:
default export (a: object) => {
const variableToExport = a;
}
The reason why you can't directly access variableToExport is that it is defined inside the function, and not outside. To deal with it, you have 2 solutions:
1. Assign to a variable in the outer scope.
The code would look like:
/!\ Read the text below before using this snipplet /!\
export let variableToExport;
default export (a: object) => {
variableToExport = a;
}
Here, you're strictly speaking exporting the variable. Since it's defined outside the function, you can access it outside the function and thus, you can export it. HOWEVER, IT COULD BE A MISTAKE. If you call twice the exported function with different values for a, the variable variableToExport would only have the value corresponding to the second call, and not the first. If the value of variableToExport should not depend on a, it could be OK, but otherwise it seems risky.
2. Returning the value
Since your function would be called to get the variableToExport, you could return it:
default export (a: object) => {
const variableToExport = a;
return variableToExport;
}
In case you have multiple things to return, you can build an object to return them all:
default export (a: object) => {
const retValue = {
"variableToExport": a,
... other variables to return
}
return retValue;
}
Then, you can access it from another module (with or without a destructuring assignment):
import foo from 'path/to/module'
const { variableToExport, otherVariable } = foo(a);
This second way is safer than using a "global" variable, as the variableToExport can't have a value that correspond to another call to the function.
Now, for the case of your variable in the for loop, you have a similar issue: you can't access to the variable outside the for loop:
default export (bar: array) => {
for (const elem of bar) {
const clientID = elem.clientID;
}
return clientID; // Nope, clientID does not exist here
}
To deal with that, the first option works:
default export (bar: array) => {
let clientID; // Not const, because it will change
for (const elem of bar) {
clientID = elem.clientID;
}
return clientID;
}
This will thus return the last clientID (and since you said you have only one clientID, it should be ok), but it would be a little better of you could get the value of clientID outside the loop, except if you intend on exporting only the last value of clientID.
I hope that, even though you might not understand everything, you understand how to export the client ID you want to export, and that the key words I gave you allows you to easily find what you might need on the internet more easily than these 3 last days. Ask me if anything isn't clear enough, I'll answer when I'll have time
I think your problem is about your variable's scope. const is block-scoped so everything about clientID variable happens inside the for loop and does not affect outside. I guess you can use var or let up to your purpose. This is a quite explanatory article about difference between var, let, and const: https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/
MDN Document on const: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
I guess the way you can access the variable is by creating a let variable outside the loop then assign it a value in the loop, or create an array outside the loop and push values into it inside the loop, that way you can have access to the variable outside the loop's scope.

How to prevent an object from being accessed JS

I have a program in which I wish to prevent anyone from accessing String object or any of its prototypes and I can't seem to find how to do that.
Tried Object.seal, Object.freeze and they both obviously don't work (since they don't prevent you from accessing properties already present, so feeling a little lost on how to do that.
Tried looking it up on internet but after half an hour, all I have got is different way to access properties and 3 ways of adding new stuff and locking but 0 ways to make it be inaccessible
I tried to delete as well but that one was.....
You can use a symbol as a key and store your object in that object. So It will be accessible just in scope you defined the symbol.
function addPrivateKey(a, value) {
let sym1 = Symbol()
a[sym1] = value
console.log(a)
console.log(a[sym1])
}
let a = {};
addPrivateKey(a, 2)
console.log(Object.keys(a))
Define a private scope and store your keys there. The values are accessible just with their hints!
class PrivateScope {
glob = {};
#keyToSym = {};
counter = 0;
get(hint) {
return this.glob[this.#keyToSym[hint]]
}
set(value) {
let sym1 = Symbol()
this.glob[sym1] = value
this.#keyToSym[this.counter] = sym1;
return this.counter ++;
}
}
const myPrivateScope = new PrivateScope();
let hint = myPrivateScope.set(2)
console.log(
myPrivateScope.get(hint)
)
console.log(myPrivateScope.glob)

Purpose of getters and setters in Javascript classes

I have been learning how to use classes in JavaScript, and something that always confused me was the how getters and setters work. I think I now finally understand them, is the below explanation correct?
They are no different to normal methods, and simply provide an alternative syntax.
A getter is simply an alternative to a method which cannot have a parameter, and means you don't have to use () to call, e.g.:
get myGetter() { return { msg: "hello" } };
...
classInstance.myGetter.msg; // "hello"
Is equivalent to:
myGetter() { return { msg: "hello" } };
...
classInstance.myGetter().msg; // "hello"
A setter is simply an alternative for a method that does take a parameter, e.g.:
set mySetter(value) { this.value = value };
...
classInstance.mySetter = "hello";
Is equivalent to:
mySetter(value) { this.value = value };
...
classInstance.mySetter("hello");
Functionally, that explanation is mostly correct, however they also have a more semantic meaning. Getters/setters are very useful for updating things that depend on a value or calculating a value, but they shouldn't be used for triggering actions. For example, this is a wrong usage of a getter:
const alerter = new Alerter;
// [...]
alerter.alert = "Hi there!"; // Alerts "Hi there!"
This is a good one:
const player = new Player;
// [...]
player.health--; // Also updates the health bar
It's also worth noting that, while in most circumstances, they behave like methods, they aren't methods at all! They are part of properties.
In JS, properties can have data descriptors and accessor descriptors. Data descriptors are "normal" properties. They have a value and you can get/set it.
const obj = {
prop: 1;
};
console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set
Accessor descriptors don't hold a value, and allow for setting what happens when the property is get and set.
const obj = {};
Object.defineProperty(obj, "prop", {
get() {
console.log("Getter was called");
return 1;
},
set(v) {
console.log("Setter was called with the value %o.", v)
}
});
/* Alternative syntax:
class Example {
get prop() {
console.log("Getter was called");
return 1;
}
set prop(v) {
console.log("Setter was called with the value %o.", v)
}
}
const obj = new Example;
*/
console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set
That code logs:
Getter was called
1
Setter was called with the value 2.
There is a huge difference between getters/setters and normal properties, in their most simple form you could think of them as an alternative syntax. however getters/setters provide more convenient solutions for certain use cases - though eventually getters/setters and methods are properties, getters/setters has accessor descriptors while methods has data descriptors.
I'm gonna list some few use cases on top of my head
getters/setters enable you to trigger custom functionality when reading/setting a property without having to create two different methods
let xThatShouldBeHidden = 1;
const object = {
get x() {
return xThatShouldBeHidden
},
set x(newX) {
if (newX === 0) {
throw new Error('You can not set x to 0')
}
xThatShouldBeHidden = newX
}
}
Triggering custom functionality is a cool feature, it enables you to do optimizations while still abstracting that behind simple syntax.
Imagine you you have array of items that has values, and later you want to get the weight of the item (value / sum of all values of items)
const items = [{val: 2}, {val:4}]
one way to do it would be which required you to loop twice even if eventually the weight was read from only one item
const totalSum = items.reduce((acc,cur), acc + cur.val,0));
const itemsWithWeights = items.map(item => ({...item, weight: item.val / totalSum});
now with getters we do it in one loop plus number of actual reads
const getItemsWithWeightsGetter = () => {
let totalSum;
return items.map(item => ({
...item,
get weight() {
if (totalSum === undefined) {
totalSum = items.reduce((acc, cur) => acc + cur.val, 0);
}
return item.val / totalSum;
},
}));
};
const itemsWithWeightsGetter = getItemsWithWeightsGetter();
another use case is the example i just shared above, when you provide just a getter that makes the value read only, making code throws when you try to set the value - in strict mode only
The difference is, you can have a getter/setter pair with the same name. For example, when working with DOM, there is the innerHTML getter/setter pair.
const element = document.querySelector("div")
console.log(element.innerHTML) // Outputs HTML as string
element.innerHTML = "Hello!" // Sets the HTML of element to "Hello!"

How to create a Javascript object with index and values with variables?

I have a problem in my javascript code, i need to create an object with variables and those variables being objects too. Explanation:
I need this in my javascript code (similar to a Json structure):
var myObj = {
variableOne: {
variableOneA: 'someValue',
variableOneB: 'someValue'
}
variableTwo: {
variableTwoA: 'someValue',
variableTwoB: 'someValue'
}
variableThree: {
variableThreeA: 'someValue',
variableThreeB: 'someValue'
}
}
Now, my problem with this is that in Js i cannot do a 'push' method to an object and i can only add one level of variables to my object doing this:
myObj.variableOne = 'someValue';
Can anyone help me please? i believe the resolution could be easy but i am new to Js.
There are different ways to access your object in Javascript.
var myObj = {
variableOne: {
variableOneA: 'oneA',
variableOneB: 'oneB'
}
variableTwo: {
variableTwoA: 'twoA',
variableTwoB: 'twoB
}
variableThree: {
variableThreeA: 'threeA',
variableThreeB: 'threeB'
}
}
You can use "dot" to access a particular level of your object.
const valueVariableOneA = myObj.variableOne.variableOneA
console.log(valueVariableOneA) // output => "oneA"
You can use the square brackets in replacement of dot. Square brackets are usefull when you want to create an object's key with dash (eg: "cool-key")
const valueVariableThreeB = myObj['variableThree']['variableThreeB']
console.log(valueVariableThreeB) // output => "threeB"
You can also use the destructuration to access particular value
// Get value of variableTwoA key
const { variableTwoA } = myObj.variableTwo // first way
const { variableTwo : { variableTwoA } } = myObj // second way
console.log(variableTwoA) // output => "twoA"
Now to add a key to a nested object you can use either dot or square brackets method. Here's how to add key on the first level.
myObj.variableFour = { variableFourA: 'fourA', variableFourB: 'fourB' }
myObj['variableFour'] = { variableFourA: 'fourA', variableFourB: 'fourB' }
// add key on nested object
myObj.variableOne.variableOneC = 'oneC'
myObj['variableOne']['variableOneC'] = 'oneC'
// you can mix both
myObj['variableOne'].variableOneC = 'oneC'
myObj.variableOne['variableOneC'] = 'oneC'
Use this code:
myObj.variableOne['someValue'] = 'new value';

Disable property mutation in JS

I was creating a component and was trying to break my implementation. The idea is to not allow user to manipulate the exposed properties.
The implementation was like this:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data; },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, setting the property is handled but user is still able to mutate it using .push. This is because I'm returning a reference.
I have handled this case like:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data.slice(); },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, I'm returning a new array to solve this. Not sure how it will affect performance wise.
Question: Is there an alternate way to not allow user to mutate properties that are of type object?
I have tried using writable: false, but it gives me error when I use it with get.
Note: I want this array to mutable within class but not from outside.
Your problem here is that you are effectively blocking attempts to modify MyClass. However, other objects members of MyClass are still JavaScript objects. That way you're doing it (returning a new Array for every call to get) is one of the best ways, though of course, depending of how frequently you call get or the length of the array might have performance drawbacks.
Of course, if you could use ES6, you could extend the native Array to create a ReadOnlyArray class. You can actually do this in ES5, too, but you lose the ability to use square brackets to retrieve the value from a specific index in the array.
Another option, if you can avoid Internet Explorer, is to use Proxies (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
With a proxy, you can trap calls to get properties of an object, and decide what to return or to do.
In the example below, we create a Proxy for an array. As you see in the handler, we define a get function. This function will be called whenever the value of a property of the target object is accessed. This includes accessing indexes or methods, as calling a method is basically retrieving the value of a property (the function) and then calling it.
As you see, if the property is an integer number, we return that position in the array. If the property is 'length' then we return the length of the array. In any other case, we return a void function.
The advantage of this is that the proxyArray still behaves like an array. You can use square brackets to get to its indexes and use the property length. But if you try to do something like proxyArray.push(23) nothing happens.
Of course, in a final solution, you might want decide what to do based on which
method is being called. You might want methods like map, filter and so on to work.
And finally, the last advantage of this approach is that you keep a reference to the original array, so you can still modify it and its values are accessible through the proxy.
var handler = {
get: function(target, property, receiver) {
var regexp = /[\d]+/;
if (regexp.exec(property)) { // indexes:
return target[property];
}
if (property === 'length') {
return target.length;
}
if (typeof (target[property]) === 'function') {
// return a function that does nothing:
return function() {};
}
}
};
// this is the original array that we keep private
var array = [1, 2, 3];
// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);
console.log(proxyArray[1]);
console.log(proxyArray.length);
console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined
// but if we modify the old array:
array.push(23);
console.log(array);
// the proxy is modified
console.log(proxyArray[3]); // 32
Of course, the poblem is that proxyArray is not really an array, so, depending on how you plan to use it, this might be a problem.
What you want isn't really doable in JavaScript, as far as I'm aware. The best you can hope for is to hide the data from the user as best you can. The best way to do that would be with a WeakMap
let privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
data: []
});
}
addEntry(entry) {
privateData.get(this).data.push(entry);
}
getData() {
return privateData.get(this).data.concat();
}
}
So long as you never export privateData don't export from the module, or wrap within an IIFE etc.) then your MyClass instances will be able to access the data but external forces can't (other than through methods you create)
var myInstance = new MyClass();
myInstance.getData(); // -> []
myInstance.getData().push(1);
myInstance.getData(); // -> []
myInstance.addEntry(100);
myInstance.getData(); // -> [100]

Categories

Resources