Рекомендации по "абстрактным" функциям в JavaScript?

Я просто написал код JavaScript, который следует вместе с тем, что я считаю хорошей практикой для создания объекта с закрытием и некоторыми функциями:

var myStuff = (function() {
 var number = 0;
 var fn = {};
 fn.increment = function() { number++; };
 fn.decrement = function() { number--; };
 fn.getNumber = function() { return number; };
 return fn;
})();

myStuff.increment();
myStuff.increment();
alert(myStuff.getNumber()); // alerts '2'

У меня нет проблем с написанием кода, как в предыдущем фрагменте. Я хотел бы написать код с функциональностью, подобной "абстрактному" классу ООП. Вот результат моих усилий:

var myStuff = (function () {
var number = 0;
var fn = {};
fn.increment = function () { number++; };
fn.decrement = function () { number--; };
fn.doSomethingCrazy = function () { throw new Error('not implemented'); }; // I want to specify later what this does.
fn.doSomethingCrazyTwice = function () { fn.doSomethingCrazy(); fn.doSomethingCrazy(); };
fn.getNumber = function () { return number; };
return fn;
})();

myStuff.doSomethingCrazy = function () { this.increment(); this.increment(); };
myStuff.doSomethingCrazyTwice();
alert(myStuff.getNumber()); // alerts '4'

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

Что такое хороший способ определить объект в JavaScript, чтобы функция этого объекта могла быть определена позже?

Ответы

Ответ 1

Просто не определяйте функцию.

Javascript - это утиный язык. Если это похоже на утку, и она уклоняется от утки, это утка.
Вам не нужно делать что-либо особенное для выполнения этой работы; пока функция существует, когда вы ее вызываете, она будет работать нормально.

Если вы вызываете его в экземпляре, который не имеет этой функции, вы получите сообщение об ошибке на call-сайте.

Ответ 2

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

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

/**
 * Called when data is received and should update the data buffer
 * for each of the charts 
 * 
 * @abstract
 * @param {cci.ads.Wave[]} waves
 * @void
 */
updateChartsData: function(waves){
    throw "Abstract method updateChartsData not implemented";
},

Ответ 3

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

В нашем абстрактном методе javascript мы просто бросаем ошибку или выставляем предупреждение. Это пример из объекта Page:

Page.initialLoad = function() { //abstract
    alert('Page.initialLoad not implemented');
};

В java-мире это аналогично:

public void abstract initialLoad();

Код Java дает время компиляции, однако в Javascript мы получаем ошибку времени выполнения. (грязное диалоговое окно с сообщением о том, что объект реализации еще не реализовал этот метод).

У нас есть несколько разрозненных команд, которые используют объект Page; философия "утиной печати" абсолютно не режет ее с нами. Без этих псевдо-абстрактных методов у нас вообще отсутствует связь API, и иногда мы получаем саботаж суперъекта (т.е. Потому что пользователь понятия не имеет, что они должны реализовывать метод).

Я устал от этой философии "утиной печати". Я не уверен, были ли сторонники когда-либо в сложном проекте Javascript с 10+ разработчиками.

Ответ 4

Если вы не найдете свой путь изящным, возможно, есть способ создать некоторые функции, чтобы упростить процесс, чтобы он выглядел лучше. Но вернемся к теме...

Да, Javascript имеет встроенное делегирование, а также наследование через прототипы.

Учитывая прототипный объект:

var proto = {
    f: function(){ console.log(this.x); }
}

Мы можем создать новый объект, который наследует от него:

var obj = Object.create(proto);
obj.x = 42;

obj.f(); //should work!

for(var i in obj) console.log(i);
//should print x, f and some other stuff perhaps

Просто помните, что выполнение вещей непосредственно через Object.create не всегда поддерживается (старые браузеры и т.д.). Старый (и некоторые могут сказать, нормальный) способ делать вещи через фанк новый оператор (не слишком много думайте о названии - его запутывает с целью отвлечь людей Java)

function Constructor(arg){
    this.x = arg;
}

Constructor.prototype = {
    f: function(){ ... }
};

var obj = new Constructor(17);
obj.f();

Важное отличие от прототипического наследования заключается в отсутствии частных переменных. Наследуются только общедоступные переменные! Из-за этого общим соглашением является использование подчеркивания в качестве префикса для частных и защищенных переменных.

Ответ 5

Возможно, вам стоит взглянуть на предыдущий пост Как создать абстрактный базовый класс в JavaScript?

Просто несколько сайтов для некоторого легкого чтения для вас в ООП и JavaScript, я предполагаю, что ваш новый для JavaScript как OOP langauge на основе комментария, который вы сказали

http://mckoss.com/jscript/object.htm

http://www.codeproject.com/KB/aspnet/JsOOP1.aspx

http://www.javascriptkit.com/javatutors/oopjs.shtml