Возможно зациклить свойства для динамического определения геттеров / сеттеров? - PullRequest
0 голосов
/ 26 сентября 2018

Предполагая следующее:

var first = {
    bob: null,
    jim: null,
    sue: null
},
second = {};

Я хотел бы иметь возможность зацикливаться на свойствах first и определять методы получения / установки для second для каждого;что-то вроде следующего:

for (var name in first) {
    Object.defineProperty(second, ('' + name), {
        get: function() {
            return first[name];
        },
        set: function(value) {
            first[name] = value;
        }
    });
}

Проблема заключается в том, что методы получения / установки влияют только на последнее повторяемое свойство, в этом случае sue.

Однако, кажется, следующееработает просто отлично:

var second = {
    get bob() {
        return first['bob'];
    },
    set bob(value) {
        first['bob'] = value;
    },
    get jim() {
        return first['jim'];
    },
    set jim(value) {
        first['jim'] = value;
    },
    get sue() {
        return first['sue'];
    },
    set sue(value) {
        first['sue'] = value;
    }
};

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

Заранее спасибо!

1 Ответ

0 голосов
/ 27 сентября 2018

Как уже упоминалось в комментариях, это проблема области видимости.

Ваша переменная действительно "поднята", но это не совсем проблема.Это главным образом потому, что переменная name является частью глобальной области видимости.В JavaScript области видимости - это в основном функции.Управляющие структуры как циклы, условия ... не объявляют новые области.

Если вы сделаете это:

var first = {
  bob: 'text',
  jim: 42,
  sue: true
},
second = {};

for (var name in first) {
  Object.defineProperty(second, name, {
    value: first[name],
    enumerable: true
  });
}

console.log(second.bob); // "test"
console.log(second.jim); // 42
console.log(second.sue); // true

Это будет работать, потому что first [name] оценивается на каждой итерации цикла.

Теперь, если вы это сделаете:

var first = {
  bob: 'text',
  jim: 42,
  sue: true
},
second = {};

for (var name in first) {
  Object.defineProperty(second, name, {
    get: function() {
      return first[name];
    },
    set: function(value) {
      first[name] = value;
    },
    enumerable: true
  });
}

console.log(second.bob); // true
console.log(second.jim); // true
console.log(second.sue); // true

Это не будет работать, потому что first [name] оценивается только при вызове функции getter / setter.Если после цикла вы выполните: second.bob, будет вызвана функция-получатель, и она не найдет имя в своей области видимости.Затем он будет искать в родительской области, которая здесь является глобальной областью, где определено имя.Цикл завершен, имя равно последней итерации цикла.То же самое с сеттером, если вы делаете second.bob = 'new value';.Область действия функции-установщика не имеет переменной name и будет искать в глобальной области, где имя - это последняя итерация цикла.

Решение состоит в том, чтобы определить цикл структуры управления for как область дляпеременная name с ключевыми словами let или const.Вы также можете объявить свой процесс defineProperty в функции вне цикла и вызывать эту функцию только внутри цикла, передав ей параметр name.В этом случае значение будет скопировано и «привязано» к новой области действия функции.

// With 'let'
for (let name in first) {
  Object.defineProperty(second, name, {
    // ...
  });
}

// With 'const'
for (const name in first) {
  Object.defineProperty(second, name, {
    // ...
  });
}

// With function
var defineProp = function (name) {
  Object.defineProperty(second, name, {
    // ...
  });
};

for (var name in first) {
  defineProp(name);
}
...