Привет Хабр!
Вчера натолкнулся на некую особенность в переопределении геттеров и сеттеров в цикле.

Вот пример кода. Я не нашел явного ответа на ситуацию. Если кто поможет, то буду рад.

function Obj(){
  this.a = 10;
  this.b = 20;
  
  for( ident in this ){
    Object.defineProperty(this, ident , {
      get : function(){
	return ident ;
      }
    })
  }
  return this;
}

var a = new Obj();

console.log( a.a , a,b )


Логично предположить, что результатом будет «a» и «b». Я тоже так думал, но к сожалению ответ будет «b» и «b».


Затем я убрал циклы

function Obj(){
  this.a = 10;
  this.b = 20;
  
  Object.defineProperty(this, 'a' , {
    get : function(){
      return 'a';
    }
  })
  
  Object.defineProperty(this, 'b' , {
    get : function(){
      return 'b';
    }
  })
  
  return this;
}

var a = new Obj();

console.log( a.a , a.b )


Результат оказался предсказуем — «a» и «b».

Я не знаю как описать такое поведение, но решается довольно просто —

function Obj(){
  this.a = 10;
  this.b = 20;
  
  Object.keys(this).map(function(__prop){
    Object.defineProperty(this, __prop , {
      get : function(){
	return __prop;
      }
    })
  },this)
  return this;
}

var a = new Obj();

console.log( a.a , a.b )


Получим «a» и «b».

Если кто в комментариях сможет описать причину, то дополню статью.
Поделиться с друзьями
-->

Комментарии (5)


  1. vintage
    03.10.2016 10:13
    +2

    Лучше удалите статью, пока вам не слили карму за не знание основ. Почитайте любой учебник по яваскрипту, главу про замыкания. Внимательно.


    1. Fr3nzy
      03.10.2016 10:18

      Ну и ссылка вдогонку на эту тему (ответ, конечно же, не полный): http://stackoverflow.com/a/750506


  1. napa3um
    03.10.2016 10:18
    +1

    «Статья» для Тостера, а не Хабры.


  1. AndreyRubankov
    03.10.2016 10:24
    +1

    Если по быстрому и по простому: замыкание берет «ссылку» на переменную (а не ее значение).
    Следовательно, когда эта переменная изменяется (в цикле) то и в замыкании значение будет другим (в случае с циклом всегда будет последнее значение). Грубо говоря, в вашем случае на все геттеры всего одна функция, а не N, как вы ожидали.

    Если сильно хочется, можете попробовать такой вариант:

    (function (value) {
      return function () {
        return value;
      }
    })(ident)
    


  1. ertaquo
    03.10.2016 10:25

    Во-первых, с вопросами вам на Toster надо.
    Во-вторых, вы действительно не знаете основы языка, как подметил vintage. Геттер в вашем случае всегда будет использовать текущее (на момент вызова) значение переменной цикла. Так как он вызывается после окончания цикла, то берется последнее значение, т. е. b.
    Чтобы избежать этого, можно обернуть итерацию цикла в функцию, либо использовать bind().