#javascript #closures
#javascript #замыкания
Вопрос:
Например, взгляните на мою простую реализацию стека:
var MyStack = (function() {
var min;
var head;
// Constructor
function MyStack() {
this.size = 0;
}
MyStack.prototype.push = function(val) {
var node = new Node(val);
if (typeof min === 'undefined' || val < min) {
min = val;
}
this.size;
if (typeof head === 'undefined') {
head = node;
} else {
node.next = head;
head = node;
}
};
MyStack.prototype.pop = function() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
--this.size;
var data = head.data;
head = head.next;
return data;
};
MyStack.prototype.min = function() {
if (typeof min === 'undefined') {
throw new Error('Min not defined');
}
return min;
};
MyStack.prototype.peek = function() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
return head.data;
};
function Node(data) {
this.data = data;
this.next;
}
return MyStack;
})();
Используя этот метод, я могу убедиться, что никто не сможет (случайно или намеренно) манипулировать «частными» полями, такими как min и head. Я также могу использовать частные функции, такие как Node (), которые не нужно раскрывать.
Я читал, что это будет использовать больше памяти только из-за того факта, что он должен поддерживать дополнительную область для каждого нового объекта, который создается для MyStack. Требуется ли так много дополнительной памяти, что этот способ является плохой идеей?
Я пытался оптимизировать его, используя прототипы вместо создания функций каждый раз, когда создается новый объект. Другими словами, я не включал функции как часть конструктора MyStack.
Мой вопрос в том, является ли это плохим дизайном? Есть ли какие-либо серьезные недостатки в этой методологии?
Комментарии:
1. Под «эмулировать инкапсуляцию» вы, возможно, имеете в виду «реализовать инкапсуляцию»? Потому что я не понимаю, каким образом это не является инкапсуляцией.
Ответ №1:
Является ли использование замыканий для эмуляции инкапсуляции плохой идеей?
Нет. Хотя я бы не стал рассматривать это как «эмуляцию», замыкания реализуют инкапсуляцию.
Я пытался оптимизировать его, используя прототипы вместо создания функций каждый раз, когда создается новый объект. Другими словами, я не включал функции как часть конструктора MyStack.
Мой вопрос в том, является ли это плохим дизайном? Есть ли какие-либо серьезные недостатки в этой методологии?
Да, это на самом деле неправильно. Ваши переменные min
and head
(и MyStack
и Node
) по сути статичны. Они определены только один раз и будут совместно использоваться всеми экземплярами. Вы не можете создать два разных стека, они оба будут иметь одну и ту же head
ссылку.
Чтобы инкапсулировать состояние для каждого экземпляра, вам нужно будет объявить переменные в конструкторе, чтобы они создавались с каждым новым объектом. Для этого вам также придется объявить все методы, которым требуется доступ к ним («привилегированные») в области конструктора.
var MyStack = (function() {
function MyStack() {
var size = 0;
var head = undefined;
function checkNonEmpty() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
}
this.push = function(val) {
size ;
head = new Node(val, head);
};
this.pop = function() {
checkNonEmpty();
this.size--;
var data = head.data;
head = head.next;
return data;
};
this.peek = function() {
checkNonEmpty();
return head.data;
};
this.getSize = function() {
return size;
};
}
function Node(data, next) {
this.data = data;
this.next = next;
}
return MyStack;
})();
Если вы хотите поместить эти методы в прототип, вам нужно будет сделать head
и size
значения доступными в качестве свойств экземпляра.
Комментарии:
1. Ого, я не знал, что на самом деле создаю статические поля. Неудивительно, что я столкнулся с некоторыми проблемами при использовании моей реализации. Теперь, когда вы переработали мой код, для меня это имеет гораздо больше смысла. Итак, по сути, дилемма заключается в следующем? Либо иметь «частные» поля, но воссоздавать методы каждый раз при создании нового объекта, либо предоставлять поля, но пользоваться преимуществами памяти прототипа?
2. @gjvatsalya Да, именно. Хотя создание методов на самом деле довольно дешево, обычно вам не нужно беспокоиться об этом.
3. Фантастика, тогда приятно это слышать. Спасибо, что указали на критическую ошибку, которую я допустил.
Ответ №2:
Учитывая, что инкапсуляция является одним из принципов объектно-ориентированного программирования, я не думаю, что это плохая практика кодирования. Замыкания в JavaScript — это ваши средства инкапсуляции переменных и ограничения доступа к другим частям приложения.
Во многих отношениях ничто по-настоящему не безопасно в JavaScript. Даже при закрытии те переменные, которые вы хотите сохранить закрытыми, доступны разными способами в разных браузерах. Например, в Google Chrome вы можете установить точку останова в отладчике и получить доступ к любой переменной в активном приложении. Хотя мы можем многое сделать в браузере для обеспечения безопасности, мы все еще имеем дело с интерпретируемым языком, который компилируется на машине, на которой выполняется код.
И учитывая ваш пример:
Если вы когда-либо работали с typescript или любыми фреймворками быстрой разработки, вы увидите, что это метод кодирования вывода, используемый для объектов пространства имен при компиляции / переносе фреймворков.
var MyStack = (function(){...})();
Комментарии:
1. Я вижу, это имеет смысл. Что касается использования отладчика, это означает, что у них будет доступ только для чтения, верно? Или они все еще смогут изменять значение, устанавливая точку останова?
2. Кроме того, не могли бы вы объяснить, что вы имели в виду в своем последнем абзаце? Вы просто хотите сказать, что эти фреймворки используют тот же шаблон, что и тот, который я использовал в своем примере?
3. @gjvatsalya Они могут изменять что угодно в отладчике. Когда вы остановились на точке останова, вы можете дважды щелкнуть по любой из переменных в поле Scope, и это позволит вам ввести для нее новое значение.
4. Что касается последнего абзаца, да. Я имею в виду, что эти фреймворки используют тот же стиль кодирования. Что касается отладчика, да, вы можете изменять переменные на лету и манипулировать приложением. Вы действительно мало что можете с этим поделать. Вы также спрашивали о производительности. Накладные расходы на производительность, основанные на этом стиле кодирования, незначительны.
5. Спасибо за ответы. Если влияние на производительность незначительно, тогда я чувствую себя комфортно, написав больше JS-кода в этой методологии.