Закрытие функций автоматического выполнения против объектов

Скажем, у меня есть следующее:

var foo = (function(){
    var bar = 0;
    return {
       getBar: function(){
           return bar;
       },
       addOne: function(){
           bar++;
       },
       addRandom: function(rand){
           bar += rand;
       }
    }
})();

И у меня есть следующее:

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
           return bar;
       };
    this.addOne = function(){
           bar++;
       };
    this.addRandom = function(rand){
           bar += rand;
       }
};

Единственная разница в выполнении функций a new?

alert(foo.getBar()); //0
foo.addOne();
foo.addRandom(32);
alert(foo.getBar()); //33

var foo2_obj = new foo2;
alert(foo2_obj.getBar());//0
foo2_obj.addOne();
foo2_obj.addRandom(32);
alert(foo2_obj.getBar());//33

Оба они поставили ту же самую вещь.

Так в чем же разница в долгосрочной перспективе?

Что можно сделать, чтобы другой не мог?

Скрипка Демо выше: http://jsfiddle.net/maniator/YtBpe/

Ответы

Ответ 1

В первом вы можете создать объект один раз, а со вторым вы можете создать столько объектов, сколько хотите. И.Е. первый из них эффективно является одиночным.

Обратите внимание, что закрытие не подходит для второго. Каждый раз, когда вы создаете экземпляр, вы снова создаете функции и теряете массу памяти. Объект-прототип предназначен для противодействия этому, где вы можете создавать функции сразу за пределами области действия и не создавать случайных замыканий.

function foo2(){
    this._bar = 0;
}

foo2.prototype = {

    constructor: foo2,

    getBar: function(){
        return this._bar;
    },

    addOne: function(){
        this._bar++;
    },

    addRandom:function(rand){
        this._bar += rand;
    }

};

Тогда:

var a = new foo2, b = new foo2, c = new foo2;

Создает три экземпляра, у которых есть свой _bar, но имеют одинаковую функциональность.

jsperf

Вы можете "сравнить" все это с PHP, некоторые из кода даже не будут выполняться, но это "эквивалентно" в принципе:


var foo = (function(){
    var bar = 0;
    return {
       getBar: function(){
           return bar;
       },
       addOne: function(){
           bar++;
       },
       addRandom: function(rand){
           bar += rand;
       }
    }
})();

примерно эквивалентен этому в PHP:

$foo = new stdClass;

$foo->bar = 0;

$foo->getBar = function(){
    return $this->bar;
};

$foo->addOne = function(){
    $this->bar++;
}

$foo->addRandom = function($rand){
    $this->bar += $rand;
}

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
        return bar;
    };
    this.addOne = function(){
        bar++;
    };
    this.addRandom = function(rand){
        bar += rand;
    }
};

Примерно эквивалентен этому в PHP:

Class foo2 {


    public function __construct(){
    $bar = 0;

        $this->getBar = function(){
            return $bar;
        };
        $this->addOne = function(){
            $bar++;
        };
        $this->addRandom = function($rand){
            $bar += rand;
        };


    }

}

function foo2(){
    this._bar = 0;
}

foo2.prototype = {

    constructor: foo2,

    getBar: function(){
        return this._bar;
    },

    addOne: function(){
        this._bar++;
    },

    addRandom:function(rand){
        this._bar += rand;
    }

};

Примерно эквивалентен этому в PHP:

Class foo2 {

    public $_bar;

    public function __construct(){
        $this->_bar = 0;    
    }

    public function getBar(){
        return $this->_bar;    
    }

    public function addOne(){
        $this->_bar++
    }

    public function addRandom($rand){
        $this->_bar += $rand;
    }

}

... и является единственной, которая близка к ООП в трех приведенных выше примерах


Ответ 2

Единственное отличие состоит в том, что foo будет общим Object, тогда как foo2_obj будет идентифицировать как foo2 при проверке его типа (т.е. foo2_obj.constructor == foo2 будет true, тогда как эквивалент на foo foo.constructor == Object).

Конечно, существует важное различие между foo и foo2 - foo - это объект, а foo2 - это функция (предназначенная для использования в качестве конструктора). Таким образом, тривиально сделать так много экземпляров foo2 (из которых foo2_obj является одним), а идея создания "экземпляров" foo на самом деле не имеет смысла - лучшее, что вы можете сделать, это копии (что сложнее, чем вызов конструктора).

В связи с различием копирования/создания экземпляров второй подход позволяет осуществлять реальное программирование OO с помощью прототипных цепей, в то время как первый делает такие вещи намного сложнее (и не рекомендуется).

Ответ 3

[1] сначала, но не важно: эффективность

function Foo1() {
    var bar = 0;
    return {
        getBar: function () {
            return bar;
        }
    }
}
var o = Foo1();
o.getBar();


function Foo2() {
    var bar = 0;
    this.getBar = function () {
        return bar;
    }
}
var o = new Foo2();
o.getBar();

который быстрее?, посмотрите object-literal-vs-new-operate

[2] шаблон программы: у первого нет шаблона программы, но последний получит форму прототипное наследование. Если мы хотим добавить метод с именем" logBar",

бывший:

1: продлить каждый экземпляр Foo1:

o.logBar = function () {
    console.log(this.getBar());
}
o.logBar();

плохой способ!

2: найдите, где Foo1 определен и добавьте:

function Foo1() {
    var bar = 0;
    return {
        getBar: function () {
            return bar;
        },
        logBar:function () {
            console.log(this.getBar());
        }
    }
}
var o = Foo1();
o.logBar = o.logBar();

вы хотите вернуться к этому, когда хотите добавить больше времени для запуска метода?

последняя:

Foo2.prototype.logBar = function () {
    console.log(this.getBar());
}

var o = Foo2();
o.logBar = o.logBar();

это будет нормально работать.

[3] назад к эффективности: в способе Foo1, продукт logBar выполняет функцию экстренного времени, когда создается экземпляр Foo1. object-literal-vs-new-operate

Ответ 4

foo и foo2_obj Они одинаковы. В обоих случаях у вас есть функция, которая создает новый объект, ссылается на переменную в области закрытия и возвращает этот объект.

У вас есть 4 вещи

  • анонимная функция, которая является factory для "foos"
  • объект foo создан из анонимного factory
  • foo2, который является именем factory для "foo2_objs"
  • объект foo2_obj, созданный с помощью foo2 factory

Точная разница между использованием new и возвращаемыми функциональными литералами из функции является пренебрежимой, если вы не касаетесь <Function>.prototype

Вероятно, вы захотите сравнить

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
           return bar;
       };
    this.addOne = function(){
           bar++;
       };
    this.addRandom = function(rand){
           bar += rand;
       };
};

Для

var Foo = {
  addOne: function () { this.bar++; },
  addRandom: function (x) { this.bar+=x; }
};

var foo3 = function () {
  return Object.create(Foo, { bar: { value: 0 } });
}

foo3 использует прототипный OO. это означает, что вам не нужно постоянно воссоздавать эти функции.

Ответ 5

Я думаю, что в моем личном взгляде на эти два типа
1- Singleton
2- Объект

Скажем, у нас есть одна страница с их javascript, используя Object (Second), и имея много utils, использующих одиночные (First), и отлично работает.

Но в один прекрасный день нам нужна новая страница, которая вызывает первую страницу через AJAX, эта новая страница имеет свой javascript, используя Object (Second) и имеет одинаковые utils с использованием singleton, но мы добавляем некоторые новые функции в одиночные пакеты utils.

Оказывается, унитаты utils на новой странице переопределяются для загруженных синглтонов utils на первой странице, поэтому, когда новая страница выполняется, некоторые из этих новых функций не существуют, генерируя ошибки...

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

Приветствия.

Ответ 6

Простыми словами, если вы создаете 10 экземпляров foo и foo2, функция getBar foo будет существовать 10 раз в памяти, а функция foo2 будет только один раз.

Кроме того, современные браузеры, такие как хром с компилятором V8, компилируют js в машинный код... в этом случае foo2 будет переведена в собственный объект класса и его как в 20 раз быстрее (когда вы создаете say 1000 экземпляры в цикле)


Обычно я использую простой метод объекта, когда требуется только один экземпляр этого класса/модуля. Структура, за которой я следую,

var myInstance = function(){
   var self = {};
   self.bar = null;
   self.gerBar = function(){
      return self.bar
   }
   return self;
}();

это очень похоже на подход foo, но я считаю эту структуру более удобной.


Еще одно отличие (в практическом использовании), которое я обычно встречаю, - это когда у меня есть callback функции или timeouts внутри класса,

var foo2 = function(){
    this.doSomething = function(){
        var temp = this;
        $.someAsyncCall(function(){
           // 'this' in current scope is the inline function itself, not the class instance
           // so have to store the instance ref in a local var in outer scope and then use that to get the class instance
           temp.callAfterAsyncCall();
        });
    };
    this.callAfterAsyncCall = function(){
    };
};

поскольку вы можете видеть, что локальная временная переменная не очень хороша, когда у вас много таких случаев.

где в другом подходе вы всегда ссылаетесь на self всюду внутри области модуля,

var myInstance = function(){
   var self = {};
   self.doSomething = function(){
      $.someAsyncCall(function(){
          self.callAfterAsyncCall();
      });
   }
   self.callAfterAsyncCall = function(){
   };
   return self;
}();

Я не уверен, что это важно для вас, но просто стоит упомянуть.

Ответ 7

Основное отличие состоит в том, что foo - это объект, тогда как foo2 - это функция.

Это означает, что вы не сможете создать другой объект, например foo, который фактически не является foo, кроме случаев, когда вы копируете/вставляете его код.

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

Чтобы сделать короткий, foo является экземпляром, а foo2 может рассматриваться как класс (даже если это просто функция, строящая объект).

Это зависит от того, что вы хотите сделать в своей программе, но я бы рекомендовал использовать вторую форму, которая позволяет повторно использовать ваш код, создавая другие экземпляры.