Каждый вводит последний аргумент

#javascript

#javascript

Вопрос:

Привет, я действительно не знал, как написать заголовок для этого поста, но у меня есть вопрос. Допустим, у меня есть объект внутри функции, который я хочу заполнить данными, отправленными в качестве аргумента оператора распространения. Я перебираю данные с помощью forEach. Моя проблема в том, что только последние аргументы помещаются в конечный массив результатов.

Чтобы прояснить мою проблему, вот несколько демонстрационных кодов:

 let result = [];

function takeArgs(...args) {
  let obj = {
    string: "",
    num: 0
  }

  args.forEach(e => {
    if (typeof(e) === "string") obj.string = e;
    else {
      obj.num = e;
      result.push(obj);
    }
  });
}

takeArgs("Some string", 10, "Some other string", 15);
console.log(result);  

Итак, если бы я должен был регистрировать массив результатов, я бы получил «Какую-то другую строку», 15 дважды в результирующем массиве. Почему это происходит?

Ответ №1:

Ваша проблема в том, что вы создаете ссылку на объект obj в замыкании. obj передается по ссылке, поэтому он всегда один и тот же, и поэтому он переопределяется. Попробуйте создать новый объект на каждой итерации цикла:

 let result = []

function takeArgs(...args) {

  args.forEach(e => {
    let obj = {
      string: "",
      num: 0,
    }
    if (typeof(e) === "string") obj.string = e;
    else {
      obj.num = e;

    }
    result.push(obj);
  });
}

takeArgs("Some string", 10, "Some other string", 15)
console.log(result)  

РЕДАКТИРОВАТЬ: вероятно, в вашем ветвлении if-else также есть недостаток: вы нажимаете только obj result в том случае, если его тип не является строкой (или это ваше намерение?).

Комментарии:

1. Да, это недостаток в коде, спасибо, что указали на это: D

Ответ №2:

obj это просто указатель на какое-то место в памяти. Нажатие его дважды в массив — это то же самое, что нажатие двух указателей, независимо от того, насколько сильно вы меняете их с push на push — все они будут затронуты этим.

Используя очень чистый синтаксис es6 , вы можете предотвратить эту проблему, просто добавив синтаксис spread в строку 12:

 result.push({...obj});
  

 let result = [];

function takeArgs(...args) {
  let obj = {
    string: "",
    num: 0
  }

  args.forEach(e => {
    if (typeof(e) === "string") obj.string = e;
    else {
      obj.num = e;
      result.push({...obj});
    }
  });
}

takeArgs("Some string", 10, "Some other string", 15);
console.log(result);  

Синтаксис spread создает новый экземпляр объекта с текущим содержимым obj , или, другими словами, указатель на другое место в памяти. Таким образом, предыдущий obj и текущий больше не связаны

Ответ №3:

Это потому, что вы создаете один объект, который вы изменяете в своем цикле. Таким образом, конечное состояние этого объекта будет с последней итерации в цикле. Затем вы помещаете ссылку на объект в цикл. Это приведет к тому, что каждый объект в массиве является одним и тем же объектом.

Вместо этого создайте новый объект для каждого элемента в цикле и передайте его в массив результатов.

 let result = [];
function takeArgs(...args){
  args.forEach(e => {
    let obj = {
      string: "",
      num: 0
    }

    if (typeof(e) === "string") {
      obj.string = e;
    } else {
      obj.num = e;
      result.push(obj);
    }
  });
}

takeArgs("Some string", 10, "Some other string", 15);
  

Однако это приведет к перемещению объекта в массив результатов только в том случае, если тип значения равен a number . Поэтому вы, вероятно, захотите добавить некоторую логику для разделения аргументов на группы по два, чтобы у вас были пары строк и чисел.

 let result = [];
function takeArgs(...args){
  for (let i = 0; i < args.length; i  = 2) {
  
    let obj = {
      string: args[i],
      num: args[i   1]
    }
    
    result.push(obj);
    
  }
}

takeArgs("Some string", 10, "Some other string", 15);
console.log(result);  

Комментарии:

1. Я даже не думал об использовании obj для хранения таких данных, спасибо: D