Использование 'return' при создании объектов с 'новым'

Сегодня я обнаружил что-то очень странное: если вы создаете объекты со своей конструкторской функцией и ключевым словом new, а return - функцию из конструктора, она ведет себя так:

  • Недавно созданный "объект" - это функция.
  • Эта новая функция может быть вызвана, как обычно,...
  • Если вы поддерживаете ссылку на this в функции конструктора, this ссылается на объект, который был правильно создан из конструктора. Это то, что вы ожидали получить от new.

Вот пример:

function Constructor() {
  var self = this;

  this.name = 'instance';
  return function() {
    return self;
  }
}

Итак, если вы создали его так: var instance = new Constructor() Приведем следующее:

typeof instance    //returns "function"
typeof instance()  //returns "object"
instance()         //returns { name: 'instance' }

Итак, у меня есть три вопроса:

  • Является ли это законным и работает он кросс-браузер? Это действительно потрясающе, и я думаю, что его можно использовать во многих отношениях, но является ли это поведение надежным?
  • Что происходит в фоновом режиме, которое вызывает это поведение?
  • (может быть, ответ на 2, но...) Является ли новый объект (тот, на который ссылается 'this') внутри нового экземпляра, так что он все самодостаточен и очищается сборщиком мусора?

Ответ 1

  • Да, хотя конструктор по умолчанию возвращает новый объект, который строится (на который ссылается this), вы можете переопределить это возвращаемое значение, пока вы возвращаете объект. Поскольку функция является объектом, вы можете вернуть ее, как вы в своем примере. Недавно созданный объект не является самой функцией, но возвращаемая функция ссылается на вновь созданный объект в области видимости переменной.

  • См. # 1

  • Это связано с тем, что функция создает замыкание, поэтому она продолжает ссылаться на переменную self, которая имеет место для ссылки на фактический объект, который создается. Поэтому я бы совсем не сказал, что это "внутри" ничего, а скорее является частью области видимости функции.

Чтобы понять, что ваша функция ничем не отличается от любой другой функции. Точно так же, как если бы вы вернули Array, у вас будет обычный Array, который может ссылаться на новый объект.

function Constructor() {

  this.name = 'instance';
  return [ this ];  // Instead return an Array that references the new object
}

Ответ 2

Ну, это действительно хороший вопрос, и, как вы, возможно, догадались, нелегко ответить.

Проще говоря:
1) Да и Да; это одна из удивительных особенностей, которых вы не найдете в "традиционных" языках программирования.
2), пожалуйста, прочитайте о закрытии (ссылки ниже)
3) Да (пожалуйста, прочитайте больше)

Вы должны узнать больше о закрытии Javascript: http://jibbering.com/faq/notes/closures/
http://www.javascriptkit.com/javatutors/closures.shtml (здесь у вас есть хорошие рабочие примеры)

и, более конкретно, модель паразитного наследования:
http://blog.higher-order.net/2008/02/21/javascript-parasitic-inheritance-power-constructors-and-instanceof/

Я надеюсь, что это поможет

Ответ 3

это то, что вы называете closure

то, что он делает, это создать автономную среду кода (обычно называемую объектом)

typeof instance    //returns "function" - since it not "fired" or called. just returns the function declaration (correct me if i'm wrong)
typeof instance()  //returns "object" - it returns an object since you called it
instance()         //returns an object also - you called it, but you didn't store it

пример объекта, построенного с использованием замыкания:

function Constructor() {
    var privateProperty = 'private';
    var privateMethod = function(){
        alert('called from public method');
    };

    //return only what to be seen in public
    return {
        publicProperty: 'im public',
        publicMethod: function(){
            alert('called from public method');
        }
        getter: privateMethod //assign to call the private method
    }
}

var myObj = Constructor();
var pubProp = myObj.publicProperty; // pubProp = 'im public'
myObj.publicMethod()                //alert: 'called from public method';
myObj.getter()                      //alert: 'called from public method';

//cannot access since "private":
myObj.privateProperty
myObj.privateMethod