Zasięg zmiennych lokalnych

0

Cześć, testuję sobie zasięg zmiennych w JS i jedna rzecz mi trochę nie pasuje, kod:


let obj = JSON.parse('[{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]');

let obj_copy = obj;


for (let k in obj_copy)
{
  obj_copy[k].new=1;
}


console.log(obj);

no i teraz, skoro pracuję na zmiennej "obj_copy" to dlaczego zmienna obj (sprawdzane w konsoli na dole kodu) również przybiera argument "new".

1

Bo nazwa twojej zmiennej ci źle podpowiada. Nie jest to kopia. Przypisujesz tylko obj do obj_copy, wskazuje na ten sam obiekt. By zrobić kopię trzeba użyć copy()

3
for (let k in obj_copy)
{

Przychodzisz z C++/C#? Zwykle programiści w JS stawiają klamerki w tej samej liniii

for (let k in obj_copy) {

Tak się przyjęło po prostu. Chociaż działa tak i tak.

let obj_copy = obj;

w JS obiekty są kopiowane przez referencje, czyli tym sposobem nie kopiujesz obiektu, a jedynie robisz nowy "alias" do niego.

Jak chcesz działać na kopii, to potrzebujesz ją sam sobie zrobić. Możesz zrobić płytką kopię:

let obj_copy = {...obj}; 
// albo 
let obj_copy = Object.assign({}, obj);

ale to ci nic nie da, bo chcesz zmienić głęboko właściwość [k].new, więc potrzebujesz głębokiej kopii np.

let obj_copy = structuredClone(obj);
// albo
let obj_copy = JSON.parse(JSON.stringify(obj));

Przy czym oba sposoby nie zawsze ci zadziałają (np. te sposoby nie kopiują funkcji). Więc jeśli faktycznie istnieje potrzeba głębokiego klonowania, to można pomyśleć o jakimś customowym sposobie albo użyciu biblioteki (np. _.cloneDeep z lodash, nie jestem zwolennikiem pakowania do każdej prostej rzeczy osobnej biblioteki, ale cóż, jest taki sposób. Chociaż myślę, że i tak warto się zastanowić "czy ja tego potrzebuję?". Przez większość czasu w JS wystarczy robienie płytkich kopii). Ew. w twoim przypadku powinno zadziałać:

let obj_copy = obj.map(item => ({...item}));

czyli ręczne zrobienie 2 poziomowej kopii.

BTW

for (let k in obj_copy)

for/in nie służy do tablic, tylko do iteracji po kluczach obiektów. Każda tablica to obiekt, więc działa, ale niech tylko w tablicy będzie jakaś customowa właściwość i już będzie problem, bo również zostanie przeiterowana. Lepiej użyć np. for...of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
zresztą pisałem ci już o tym w innym wątku.

0

Jak przesyłasz łańcuch znaków - przesyłasz wartość, jak przesyłasz obiekt, to przesyłasz referencje do niego. AI by Ci to bardzo precyzyjnie wytłumaczył.

0

Mówi się, że język, który nie wpływa na myślenie nie jest warty poznania.

W tym przypadku JS skutecznie zniechęca do kopiowania. Z tego powodu to praktycznie uniemożliwia Ci postrzeganie danych jak dane, a to wielka szkoda.

Jeśli nic z tym nie zrobisz, to naturalnie zamiast kopiować obiekt będziesz go współdzielił i powiększał dostęp do niego z wielu miejsc. Jeśli do obiektu będzie dostęp z wielu miejsc, to również z tych wszystkich miejsc musisz założyć, że może dojść do modyfikacji takiego obiektu. Później kod powiązany z tym obiektem bardzo trudno jest czytać ze zrozumieniem, bo jak czytasz fragment kodu to nie widać wprost co jeszcze może dany obiekt w miedzy czasie zmienić. To utrudnia zarządzanie ograniczeniami, zarządzanie spójność i w rezultacie prowadzi do błędów ze stanem.

Ostatecznie możesz przeciwdziałać temu ukrywając szczelnie obiekt w innym obiekcie do którego nie sięga się z wielu miejsc, ale to przesuwa ciężar dalej, bo to teraz na ten drugi obiekt musisz bardziej uważać, bo on przecież będzie współdzielony, no i w pewnym sensie nadal wracasz do punktu wyjścia, czyli modyfikacji stanu do którego może dojść z wielu powodów.

Pod tym względem JS jest bardziej obiektowy niż sama Java.

0
znowutosamo4 napisał(a):

Jeśli nic z tym nie zrobisz, to naturalnie zamiast kopiować obiekt będziesz go współdzielił i powiększał dostęp do niego z wielu miejsc. Jeśli do obiektu będzie dostęp z wielu miejsc, to również z tych wszystkich miejsc musisz założyć, że może dojść do modyfikacji takiego obiektu.

Dlatego pisząc w JS trzeba zachowywać dyscyplinę i najlepiej nie mutować obiektu z każdego możliwego miejsca, mimo że można.

no i w pewnym sensie nadal wracasz do punktu wyjścia, czyli modyfikacji stanu do którego może dojść z wielu powodów.

Dyscyplina pisania kodu - dobrze napisany JS nie modyfikuje obiektów byle gdzie i byle jak, a jedynie w pewnych ściśle określonych miejscach i okolicznościach. Chociaż w JS przydałoby się coś takiego jak borrow-checker w Rust, że to wymusza na poziomie kompilatora.

0

Reasumując i dodając co napisał @LukeJL
to są metody płytkie:

// Statyczna metoda Array.from() tworzy nową, płytko skopiowaną tablice
let immutableArray = Array.from(obj); 

/*
Statyczna metoda Object.assign() kopiuje wszystkie właściwości z jednego lub więcej obiektów źródłowych do obiektu docelowego (określonego w pierwszym parametrze) 
*/
let immutableObject = Object.assign({}, obj); 

// Statyczna metoda Object.freeze() zamraża obiekt. 
let freezesObject = Object.freeze({}, obj); 

// to dokładnie nazwa się spread
let spread = {...obj}; 

Jest jeszcze biblioteka ramda.js , która zawiera metodę wykonującą głęboka kopie
https://ramdajs.com/docs/#clone

instalacja biblioteki:
npm i ramda

zastosowanie:

const R = require('ramda');

const objects = [{}, {}, {}];
const objectsClone = R.clone(objects);
console.log(objects === objectsClone); //=> false
console.log(objects[0] === objectsClone[0]); //=> false

1 użytkowników online, w tym zalogowanych: 0, gości: 1