Why I Can Change/Reassigned a Constant Value That Instantiated from a Class

Why I can change/reassigned a constant value that Instantiated from a class

As @Wain has said – it's due to the nature of reference types. The instance being a let constant only means you cannot assign a new reference to it – but says nothing about the actual mutability of the instance itself.

If you change your class to a struct, you'll see how the behaviour differs with value types, as changing a property changes the actual value of your Person – therefore you are unable to do so if it's a let constant. However I somewhat doubt you'll want to make your Person a struct, as two people with the same name shouldn't be considered to be the same person.

If you only wish your properties to be assigned upon initialisation (and then read-only for the lifetime of the instance), then I would recommend making them let constants (instead of making their setters private). This will ensure that you cannot even change their value from within your class, once assigned.

The rule is as long you give a property a value before the super.init() call – you can make it a let constant (in this case, you just have to assign them in the initialiser before using self).

class Person {
let firstName: String
let lastName: String

init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}

...

Why constant instances of classes can be changed in properties value?

As per the Swift document.

if Struct instance is declared as a constant (with the let
keyword), it is not possible to change its property, even
though its property is a variable property.

This behavior is due to structures being value types. When an instance
of a value type is marked as a constant, so are all of its properties.

The same is not true for classes, which are reference types. If you
assign an instance of a reference type to a constant, you can still
change that instance’s variable properties.

This actually states that when we have constant (let) class instance we can change its property's value as its a reference type.

And in case of constant (let) struct instance we cannot change its property's value as its value type.

Everything actually boils down to heap memory and stack memory.

Struct instances are stored in stack memory whereas Class instances are stored in heap memory which enables to change its property's value.

Why can I change a constant object in javascript

The documentation states:

...constant cannot change through re-assignment

...constant cannot be re-declared

When you're adding to an array or object you're not re-assigning or re-declaring the constant, it's already declared and assigned, you're just adding to the "list" that the constant points to.

So this works fine:

const x = {};

x.foo = 'bar';

console.log(x); // {foo : 'bar'}

x.foo = 'bar2';

console.log(x); // {foo : 'bar2'}

and this:

const y = [];

y.push('foo');

console.log(y); // ['foo']

y.unshift("foo2");

console.log(y); // ['foo2', 'foo']

y.pop();

console.log(y); // ['foo2']

but neither of these:

const x = {};
x = {foo: 'bar'}; // error - re-assigning

const y = ['foo'];
const y = ['bar']; // error - re-declaring

const foo = 'bar';
foo = 'bar2'; // error - can not re-assign
var foo = 'bar3'; // error - already declared
function foo() {}; // error - already declared

Why does .then() chained to Promise.resolve() allow const declaration to be reassigned?

You are not assigning to the const variable. You are instead assigning to the function parameter that you gave the same name. That function parameter is a non-const copy of the variable so you are allowed to assign to it.

I will try to collect all my comments into a more fully explained answer.

In code here:

"use strict"
const state = "123";
Promise.resolve(state).then(state => {
console.log(state); // `"123"`
state = 456; // reassign `const` variable `state` to `"456"`
return state
}).then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));

First, you define your const state = "123" variable. Any attempt to change the contents of that exact state variable will throw an exception.

Then, when you do this:

Promise.resolve(state).then(state => {

That declares a .then() handler function that takes one argument and the name of that argument is state. When the .then() handler is called, whatever is passed as that one argument to the .then() handler is copied into this new argument variable named state. Function arguments are not const. They can be assigned to.

Because you've now created TWO separate variables with the same name and one is at a higher scope, when you are inside the .then() handler, that function argument named state "overrides" or "hides" the other variable of the same name. When you attempt to access state inside the .then() handler, the ONLY variable you can access when using that name is the function parameter. That function parameter is a copy of the other state variable by virtue of being passed to the .then() handler as an argument. All function arguments are copies. Javascript has no true reference variable types.

Furthermore function arguments are not const so you can assign to them.

So, when you state = "456"; inside that .then() handler, you are just assigning to the function argument. Because you've created a naming conflict, there is actually no way to access the higher scoped const state variable. The JS interpreter finds the definition that is closest in scope to where you are attempting to access it.


I think your confusion will be cleared up if you just stop creating a conflicting variable name. If you do it like this (name the parameter localState):

"use strict"
const state = "123";
Promise.resolve(state).then(localState => {
console.log(state); // `"123"`
state = 456; // reassign `const` variable `state` to `"456"`
return state
}).then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));

Then, you will see an exception when you attempt to assign to state because you have not created a conflicting local variable with that same name so your attempt to assign state = 456 will indeed be attempting to assign to a const variable and the interpreter will object.


As best I know, Javascript has no way to prevent overriding a higher scoped variable with a newly declared variable of the same name in the local scope. That just isn't a language feature. When the interpreter resolves a variable name, it searches the scope hierarchy from local to global so local definitions are found (and used) first. Higher scoped definitions are "overriden" or "hidden" within that scope. That's just how they designed variable name resolution to work in the language.

There are many benefits to this too in that somebody suddenly declaring a higher scoped variable that you aren't using or aren't even aware of will never accidentally break your lower scoped declarations. When you yourself declaring a conflict and you actually want to use the higher scoped named, that's just a coding mistake. You have to not declare a conflicting name if you intend to use the higher scoped variable of the same name.

Why object const can be changed after definition in JavaScript?

A variable declared with const means one thing: the standalone variable name cannot be reassigned with = later.

In contrast, o.a = 5; is not reassigning the variable name - it's mutating the content of the object, but it's not changing what the o variable points to in memory.

To prevent reassignment of a variable name, use const. To prevent mutation of an object is something entirely different - for that, you'd need something like Object.freeze or manipulate objects using immutable-js.

Why did Xcode warn me about making this a constant, and why does it still change?

That is because BlobPos is a class and changing a class's properties doesn't change its location in memory, which is how classes are passed around (by reference to their location in memory). If BlobPos were a structure, then you would have to declare it a variable because structures are passed around by their values (not references to their locations in memory).



Related Topics



Leave a reply



Submit