2013-05-19 27 views
5

Staram się opanować OO JavaScript i stworzyłem prosty przykład.OO Metoda macierzysta wywołania JavaScriptu

function BasePage(name) { 
    this.init(name); 
} 

BasePage.prototype = { 
    init: function(name) { 
     this.name = name; 
    }, 
    getName: function() { 
     return this.name; 
    } 
} 

function FaqPage (name, faq) { 
    this.init(name, faq); 
} 

FaqPage.prototype = new BasePage(); 

FaqPage.prototype = { 
    init: function(name, faq) { 
     BasePage.prototype.init.call(this, name); 
     this.faq = faq; 
    }, 
    getFaq: function() { 
     return this.faq; 
    } 
} 

var faqPage = new FaqPage('Faq Page', 'Faq'); 

var text = faqPage.getName() + ' ' + faqPage.getFaq(); 
$('body').text(text); 

Wynikiem działania skutkuje następującym komunikatem:

Uncaught TypeError: Object #<Object> has no method 'getName'

Co chciałbym wiedzieć, w jaki sposób można wywołać metodę getName() w super klasy bez konieczności zastąpić i nazwać w podklasie?

Również jeśli moje, jeśli uważasz, że to podejście jest złe/dobre.

Odpowiedz

1

czuję ból. Jak wspomnieli inni, getName jest undefined, ponieważ zastępujesz prototype z . Dlatego nie będę powtarzać tego wyjaśnienia.

W związku z powyższym zgadzam się, że dobrze jest enkapsulować metody prototype w jednym zakresie. Być może w tym celu powinieneś użyć biblioteki JavaScript. Najmniejszy z nich to augment.W rzeczywistości jest to tylko 17 linii Long:

Function.prototype.augment = function (body) { 
    var base = this.prototype; 
    var prototype = Object.create(base); 
    body.apply(prototype, Array.from(arguments, 1).concat(base)); 
    if (!Object.ownPropertyOf(prototype, "constructor")) return prototype; 
    var constructor = prototype.constructor; 
    constructor.prototype = prototype; 
    return constructor; 
}; 

(function funct() { 
    var bind = funct.bind; 
    var bindable = Function.bindable = bind.bind(bind); 
    var callable = Function.callable = bindable(funct.call); 
    Object.ownPropertyOf = callable(funct.hasOwnProperty); 
    Array.from = callable([].slice); 
}()); 

Oto jak kod będzie wyglądać, jeśli użyto augment:

var BasePage = Object.augment(function() { 
    this.constructor = function (name) { 
     this.name = name; 
    }; 

    this.getName = function() { 
     return this.name; 
    }; 
}); 

var FaqPage = BasePage.augment(function (base) { 
    this.constructor = function (name, faq) { 
     base.constructor.call(this, name); 
     this.faq = faq; 
    }; 

    this.getFaq = function() { 
     return this.faq; 
    }; 
}); 

Wtedy można go używać tak jak zwykle:

var faqPage = new FaqPage("Faq Page", "Faq"); 
var text = faqPage.getName() + " " + faqPage.getFaq(); 
$("body").text(text); 

Nadzieja, która pomaga.

+0

Dzięki Aadit, widzę, że Augment dobrze się spisał w benchmarku, który zrobiłeś, ale co się stało w Chrome? Wykres pokazał zero operacji? – Stokedout

+0

@Stokedout - Testy należy wykonywać z przymrużeniem oka. Bez wątpienia 'augment' jest szybki. Jednak większość programistów JavaScriptu to amatorzy, którzy piszą niechlujny kod. Aby zrekompensować to większość silników JavaScript, takich jak V8, należy go zoptymalizować. Powoduje to, że nawet zły kod działa niesamowicie szybko (czasami nawet szybciej niż dobry kod, ponieważ złe wzorce projektowe są dobrze zoptymalizowane). To jednak nie jest wymówka, aby napisać zły kod. Dobry kod jest nie tylko szybki, ale także czytelny, ekspresyjny i łatwy do zrozumienia i utrzymania; a 'augment' trafia we wszystkie te punkty. Jest naprawdę szybki w Chrome 26. Powolne w 27. –

+0

Fajne dzięki, moje powody zmiany na OO JS to wszystko, o czym właśnie wspomniałeś. Nie mogę nadążyć za wydaniami Chrome i nie mogę się doczekać, kiedy zobaczę premiery IE ;-). Jeden końcowy komentarz na temat powyższego kodu. Jeśli używam konstruktora, to na pewno init jest duplikacją tego samego? – Stokedout

6

Ten błąd występuje, ponieważ pomimo ustawienia prototypu FaqPage na instancję BasePage, następna linia natychmiast go zastąpi. Tak więc FaqPage nie dziedziczy po BasePage. Dodaj Właściwości/metod prototypu FaqPage „s zamiast definiowania go po raz drugi:

FaqPage.prototype.init = function(name, faq) { 
    BasePage.prototype.init.call(this, name); 
    this.faq = faq; 
} 
FaqPage.prototype.getFaq = function() { 
    return this.faq; 
} 
+0

Dzięki, miałem przeczucie, że to byłby powód. Czy jest to jedyny sposób/zalecany sposób dodawania metod w OO JS? Dla mnie pochodzące z C# wydaje się brzydkie, nie jest w stanie grupować metod klas w jednym {scope} – Stokedout

+0

Wiem, co masz na myśli, ale tak, to jest zalecany sposób. Może wyrosnąć na tobie. Pamiętaj, że możesz umieszczać metody na stronie 'BasePage'. – sh0ber

2

Pierwsza linia ma rację, stworzenie prototypu z obiektu bazowego.

FaqPage.prototype = new BasePage(); 

Ale potem go nadpisać, tak samo nie dziedziczy obiekt bazowy już

FaqPage.prototype = { 
    init: function(name, faq) { 
     BasePage.prototype.init.call(this, name); 
     this.faq = faq; 
    }, 
    getFaq: function() { 
     return this.faq; 
    } 
} 

Zamiast definiować całą prototyp, zastąpić tylko poszczególne funkcje:

FaqPage.prototype.init = function(name, faq) { 
    BasePage.prototype.init.call(this, name); 
    this.faq = faq; 
}; 

FaqPage.prototype.getFaq = function() { 
    return this.faq; 
}; 
1

Jeśli dziedziczenie jest to, o co prosisz, nie jest to łatwe do osiągnięcia w JavaScript, o ile wiem. Użyłem tego fragmentu przez Johna Resiga i działa jak czar. http://ejohn.org/blog/simple-javascript-inheritance/

W twoim przypadku to jest, jak to działa,

BasePage

var BasePage = Class.extend({ 
    init: function(name){ 
    this.name = name; 
    }, 
getName: function(){ 
    return this.name; 
} 
}); 

klasę Faqpage dziedziczenie BasePage

var FaqPage = BasePage.extend({ 
    init: function(name, faq){ 
    this._super(name); 
    this.faq = faq; 
    }, 
    getFaq: function() { 
    return this.faq; 
    } 
}); 

A potem

var faqPage = new FaqPage('Faq Page', 'Faq'); 
var text = faqPage.getName() + ' ' + faqPage.getFaq(); 
$('body').text(text); 

Oczywiście, aby to działało, musisz dołączyć kod implementacji na stronie, którą połączyłem.

Oto skrzypce, http://jsfiddle.net/c8yjY/

+0

Kod Johna Resiga jest odrobinę nieaktualny. Istnieje wiele lepszych bibliotek OOP: http://jsperf.com/oop-benchmark/127 Przeczytaj moją odpowiedź powyżej. –