Что выведет этот сниппет с мутацией и переприсваиванием и почему?
Предскажите каждый console.log и объясните разницу между мутацией объекта через параметр и переприсваиванием параметра, а также между параметром и переменной внешней области видимости.
let x = { a: 1, b: 2 };
function fn1(x) { x.a = 5; } // мутирует через параметр
function fn2() { x.a = 5; } // мутирует внешний x
function fn3(x) { x = 5; } // переприсваивает локальный параметр
function fn4() { x = 5; } // переприсваивает внешний x
fn1(x); console.log(x);
fn3(x); console.log(x);
fn4(); console.log(x);
Определите вывод и объясните его.
{ a: 5, b: 2 }, затем { a: 5, b: 2 }, затем 5. JavaScript передаёт ссылку на объект по значению: fn1 мутирует общий объект через параметр, поэтому изменение видно снаружи. fn3 лишь переприсваивает свой локальный параметр в 5 — это перепривязывает локальное имя, не трогая внешний объект. fn4 переприсваивает сам внешний x (параметр его не затеняет), поэтому x становится примитивом 5.
- ✗Думать, что переприсваивание параметра (
x = 5) меняет переменную вызывающего, как мутация свойства - ✗Называть передачу объектов в JavaScript чистой передачей по ссылке — передаётся ссылка по значению
- ✗Считать, что параметр с именем как у внешней переменной делит с ней одну привязку
- →Почему мутация свойства видна снаружи функции, а переприсваивание параметра — нет?
- →Как
fn4достаёт до внешнегоx, аfn3не может?
Решение
Вывод: { a: 5, b: 2 }, { a: 5, b: 2 }, 5.
let x = { a: 1, b: 2 };
function fn1(x) { x.a = 5; }
function fn3(x) { x = 5; }
function fn4() { x = 5; }
fn1(x); console.log(x); // { a: 5, b: 2 } — мутация через ссылку
fn3(x); console.log(x); // { a: 5, b: 2 } — переприсваивание параметра локально
fn4(); console.log(x); // 5 — переприсваивание самого внешнего x
Как это работает
JavaScript передаёт аргументы по значению, но для объекта это значение — ссылка. Поэтому fn1, обращаясь к x.a, идёт по ссылке к тому же объекту и меняет его свойство — изменение видно снаружи.
fn3 делает x = 5: это присваивание перепривязывает локальный параметр на новое значение, а исходная ссылка вызывающего не затрагивается, поэтому объект остаётся { a: 5, b: 2 }. fn4 параметра не имеет, поэтому x в нём — это та самая внешняя переменная; её переприсваивание делает x примитивом 5.