«это» внутри анонимной функции? - PullRequest
8 голосов
/ 04 марта 2012

Внутри книги Джона Ресига "Техники Pro Javascript" он описывает способ генерации методов динамического объекта с помощью приведенного ниже кода:

// Create a new user object that accepts an object of properties
function User(properties) {
    // Iterate through the properties of the object, and make sure
    // that it's properly scoped (as discussed previously)
    for (var i in properties) {
        (function() {
            // Create a new getter for the property
            this["get" + i] = function() {
                return properties[i];
            };
            // Create a new setter for the property
            this["set" + i] = function(val) {
                properties[i] = val;
            };
        })();
    }
}

Проблема в том, что когда я пытаюсь создать экземпляр вышеупомянутого объекта, динамические методы присоединяются к объекту окна, а не к объекту. Кажется, что «это» относится к окну.

// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});

alert( user.getname() );

Запуск приведенного выше кода приводит к ошибке «user.getname не является функцией».

Как правильно генерировать динамические функции для каждого экземпляра объекта?

Ответы [ 4 ]

11 голосов
/ 04 марта 2012

Это код из книги?У меня есть книга, но я ее не прочитал.

Это ошибка в книге.Проверьте исправления: http://www.apress.com/9781590597279

Внутри анонимной функции this является глобальной window.

. Вы можете заставить ее работать, добавив .call(this, i) после нее.

function User(properties) {
    // Iterate through the properties of the object, and make sure
    // that it's properly scoped (as discussed previously)
    for (var i in properties) {
        (function(i) {
            // Create a new getter for the property
            this["get" + i] = function() {
                return properties[i];
            };
            // Create a new setter for the property
            this["set" + i] = function(val) {
                properties[i] = val;
            };
        }).call(this, i);
    }
} 
3 голосов
/ 04 марта 2012

this во внутренней самореализующей функции отличается от этого во внешней функции User. Как вы заметили, это относится к глобальному window.

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

function User(properties) {
  var self = this;
  for (var i in properties) { 
    (function() { 
      self["get" + i] = function() { /* ... */ }; 
      self["set" + i] = function() { /* ... */ }; 
    })();
  }
}

Тем не менее, я не уверен, зачем здесь нужна анонимная самовыполняющаяся функция, так что у вас есть более простой вариант - полностью ее исключить, например:

function User(properties) {
  for (var i in properties) { 
      this["get" + i] = function() { /* ... */ }; 
      this["set" + i] = function() { /* ... */ }; 
  }
}
1 голос
/ 04 марта 2012

Вы всегда можете вызвать другой this для любого вызова функции, используя метод apply.

(function() {
    // Create a new getter for the property
    this["get" + i] = function() {
        return properties[i];
    };
    // Create a new setter for the property
    this["set" + i] = function(val) {
        properties[i] = val;
    };
}).apply(this);
1 голос
/ 04 марта 2012

Вот как это сделать. Вам нужно сохранить контекст в другую переменную. Другой вариант - не выполнять эту внутреннюю функцию, которую вы выполняете в цикле for.

// Create a new user object that accepts an object of properties
function User( properties ) {
   // Iterate through the properties of the object, and make sure
   // that it's properly scoped (as discussed previously)
   var that = this;
   for ( var i in properties ) { (function(){
       // Create a new getter for the property
       that[ "get" + i ] = function() {
          return properties[i];
       };
       // Create a new setter for the property
       that[ "set" + i ] = function(val) {
           properties[i] = val;
       };
    })(); }
}

Вариант 2:

// Create a new user object that accepts an object of properties
function User( properties ) {
   // Iterate through the properties of the object, and make sure
   // that it's properly scoped (as discussed previously)
   for ( var i in properties ) {
       // Create a new getter for the property
       this[ "get" + i ] = function() {
          return properties[i];
       };
       // Create a new setter for the property
       this[ "set" + i ] = function(val) {
           properties[i] = val;
       };
    }
}
...