Закрытие Javascript и видимость данных - PullRequest
12 голосов
/ 04 июня 2009

Я пытаюсь обдумать идею классов, видимости данных и замыканий (особенно в Javascript), и я на странице документов jQuery для типов, где упоминается, что замыкания используются для скрытия данных:

Шаблон позволяет создавать объекты с методами, которые работают с данными, которые не видны извне - сама основа объектно-ориентированного программирования.

Пример:

function create() {
  var counter = 0;
  return {
    increment: function() {
      counter++;
    },
    print: function() {
      console.log(counter);
    }
  }
}
var c = create();
c.increment();
c.print(); // 1

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

Во-вторых, есть ли преимущество в написании класса как выше, так и ниже:

function create() {
  var counter = 0;
  this.increment = function() {
      counter++;
  }
  this.print = function() {
      console.log(counter);
  }
  return this;
}
var c = create();
c.increment();
c.print(); // 1

Насколько я понимаю, это более или менее семантически одно и то же - первое - просто более "стиль jQuery". Мне просто интересно, есть ли преимущество или другой нюанс, который я не полностью оцениваю из первого примера. Если я прав, оба примера создают замыкания в том, что они обращаются к данным, объявленным вне их собственной области действия.

http://docs.jquery.com/Types#Closures

Ответы [ 9 ]

10 голосов
/ 04 июня 2009

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

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

Второй стиль, которого я на самом деле никогда не видел в Javascript. Обычно вы бы создавали экземпляр create с new вместо возврата this в функции create(), например:

function create() {
  var counter = 0;
  this.increment = function() {
      counter++;
  }
  this.print = function() {
      console.log(counter);
  }
}
var c = new create();
c.increment();
c.print(); // 1
4 голосов
/ 04 июня 2009

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

Дело не в том, что переменная counter недоступна снаружи функции для начала, а в том, что она доступна для функций increment и print после выхода из функции create, что делает замыкания так полезно.

2 голосов
/ 04 июня 2009

Вы должны сравнить пример с этим фрагментом

function create() {
  this.counter = 0;
  this.increment = function() {
      this.counter++;
  };
  this.print = function() {
      console.log(counter);
  }
}

var c = new create();
c.increment();
c.print(); // 1

Таким образом, когда вызывается new create (), он инициализирует новый объект двумя методами и одной переменной экземпляра (а именно: counter). Javascript не имеет инкапсуляции как таковой, поэтому вы можете получить доступ к c.counter следующим образом:

var c = new create();
c.increment();
c.counter = 0;
c.print(); // 0

При использовании замыканий (как показано в ваших примерах) счетчик теперь больше не поле экземпляра, а скорее локальная переменная. С одной стороны, вы не можете получить доступ извне функции create (). С другой стороны, increment () и print () могут получить к ним доступ, потому что они закрываются в пределах области видимости. Таким образом, мы получаем довольно хорошую эмуляцию объектной инкапсуляции.

2 голосов
/ 04 июня 2009

Ну, я не хочу вступать в религиозную войну из-за того, как создавать объекты в JavaScript, поскольку некоторые люди твердо убеждены, что есть правильный и неправильный способ сделать это.

Тем не менее, я хочу указать на что-то во втором наборе кода, которое не слишком остро, а именно на то, что вы присваиваете новые свойства объекту, содержащемуся в ключевом слове this, - понимаете ли вы, что этот объект? Это не пустой объект, если вы не используете синтаксис экземпляра, подобный этому:

var c = new create();

Когда вы это делаете, ключевому слову this внутри тела функции конструктора присваивается совершенно новый объект, как если бы первая строка в теле была похожа на:

this = {};

Но когда вы вызываете create() как функцию, как вы это делаете в этом примере, вы изменяете область действия вне определения функции (как на это указывает @seanmonster в комментариях).

1 голос
/ 06 ноября 2010
MYAPP = (function(){
   var v1,v2;
   return {
    method1:function(){},
    method2:function(){}
   };
})();

Я всегда использую замыкания в своем приложении, как это, все мои собственные определенные методы находятся в пространстве имен MYAPP, v1 и v2 доступны только для методов в MYAPP. В моем приложении я часто только пишу .js "файл, все мои js коды внутри. Я предполагаю, что вы можете определить метод с именем "registy" для определения закрытой переменной в MYAPP, затем вы можете использовать его в своих методах. Все дополнительные переменные и методы должны быть определены методом registy, когда вы хотите добавить дополнительные коды в html-файл, точно так же, как метод JQuery.extend. Я слышал, что если вы используете слишком много замыканий в браузере IE, вы легко получите переполнение стека. (На мой взгляд)

1 голос
/ 31 июля 2009

Этот синтаксис имеет больше смысла для меня, исходя из фона ООП:

Create = function {
  // Constructor info.

  // Instance variables
  this.count = 10;
}

Create.prototype = {

     // Class Methods
     sayHello : function() {
          return "Hello!";
     },

     incrementAndPrint : function() {
          this.count++;

          // Inner method call.
          this.print();
     },

     print : function() {
         return this.count;
     }


}

var c = new Create();
alert(c.incrementAndPrint());
1 голос
/ 04 июня 2009

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

1 голос
/ 04 июня 2009

Ваш второй пример все еще использует замыкания, потому что функции приращения и печати по-прежнему действуют на переменную, в противном случае это выходит за рамки & mdash; к тому времени, когда вы вызываете c.increment(), функция создания уже завершена.

Мне нравится первый пример, потому что он избегает ключевого слова "this", а в JavaScript "this" может быть хитрым & mdash; это не всегда относится к тому, что должно быть.

0 голосов
/ 04 июня 2009

Во втором примере, когда вы вызываете create(), в области действия функции this является глобальным объектом (что всегда имеет место, когда вы вызываете «голую» функцию, не используя ее в качестве конструктора). или доступ к нему как к свойству (например, вызов метода)). В браузерах глобальный объект - window. Поэтому, когда вы вызываете create последующее время, оно создает новые замыкания, но затем вы назначаете их тому же глобальному объекту, что и раньше, перезаписывая старые функции, а это не то, что вам нужно:

var c = create(); // c === window
c.increment();
c.print(); // 1
var c2 = create(); // c2 === c === window
c.print(); // 0
c2.print(); // 0
increment(); // called on the global object
c.print(); // 1 
c2.print(); // 1

Решения, как указывали другие, заключаются в использовании new create().

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