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

Допустим, у меня есть следующее:

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

Они оба выражают одно и то же.

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

Что может сделать один, а другой - нет?

Fiddle Demo из вышеперечисленного: http://jsfiddle.net/maniator/YtBpe/

Ответы [ 7 ]

14 голосов
/ 22 ноября 2011

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

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

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;
    }

}

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


4 голосов
/ 22 ноября 2011

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

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

Из-за различия в копировании / создании экземпляров второй подход допускает реальное ОО-программирование с цепочками прототипов, тогда как первый делает такие вещи очень более трудный (и опрометчивый).

1 голос
/ 07 декабря 2011

[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-opera

[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-opera

0 голосов
/ 07 декабря 2011

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

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

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

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

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

0 голосов
/ 07 декабря 2011

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

Кроме того, в современных браузерах, таких как chrome, с компилятором V8, он компилирует js-код в машинный код ... в этом случае foo2 будет преобразован в объект нативного класса и, как в 20 раз быстрее (при создании, скажем, 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;
}();

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

0 голосов
/ 07 декабря 2011

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

Допустим, у нас есть одна страница, имеющая свой JavaScript с использованием Object (Second) и имеющая многоУтилиты используют синглтоны (Первый) и работают нормально.

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

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

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

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

0 голосов
/ 22 ноября 2011

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

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

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

Точная разница между использованием 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.это означает, что вам не нужно постоянно воссоздавать эти функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...