Widziałem kilka podobnych odpowiedzi, ale chciałbym wspomnieć, że ten post najlepiej to opisuje, więc chciałbym się z tobą podzielić.
Oto pobrany z niego kod, który zmodyfikowałem, aby uzyskać kompletny przykład, który, mam nadzieję, daje korzyść społeczności, ponieważ można go użyć jako szablonu projektu dla klas.
Odpowiada również na twoje pytanie:
function Podcast() {
// private variables
var _somePrivateVariable = 123;
// object properties (read/write)
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
// for read access to _somePrivateVariable via immutableProp
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
Biorąc pod uwagę ten przykład, możesz uzyskać dostęp do właściwości / funkcji statycznej w następujący sposób:
// access static properties/functions
console.log(Podcast.FILE_EXTENSION); // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
A właściwości / funkcje obiektu po prostu jako:
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
Zauważ, że w podcast.immutableProp () mamy zamknięcie : Odwołanie do _somePrivateVariable jest przechowywane wewnątrz funkcji.
Możesz nawet zdefiniować obiekty pobierające i ustawiające . Spójrz na ten fragment kodu (gdzie d
jest prototyp obiektu, dla którego chcesz zadeklarować właściwość, y
to zmienna prywatna niewidoczna poza konstruktorem):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
Definiuje właściwość d.year
poprzez get
i set
funkcje - jeśli nie określisz set
, właściwość jest tylko do odczytu i nie można jej modyfikować (pamiętaj, że nie pojawi się błąd, jeśli spróbujesz ją ustawić, ale nie da to efektu). Każda nieruchomość ma atrybuty writable
, configurable
(pozwala na zmianę po zgłoszeniu) i enumerable
(pozwalają na zastosowanie go jako wyliczający), które są domyślnie false
. Możesz ustawić je za defineProperty
pomocą trzeciego parametru, np enumerable: true
.
Prawidłowa jest również następująca składnia:
// getters and setters - alternative syntax
var obj = { a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}
};
która definiuje właściwość do odczytu / zapisu, właściwość a
tylko do odczytu b
i właściwość tylko do zapisu c
, za pośrednictwem której a
można uzyskać dostęp do właściwości .
Stosowanie:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
Uwagi:
Aby uniknąć nieoczekiwanego zachowania w przypadku zapomnienia new
słowa kluczowego, sugeruję dodanie do funkcji następujących elementów Podcast
:
// instantiation helper
function Podcast() {
if(false === (this instanceof Podcast)) {
return new Podcast();
}
// [... same as above ...]
};
Teraz obie następujące instancje będą działać zgodnie z oczekiwaniami:
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast(); // you can omit the new keyword because of the helper
Instrukcja „new” tworzy nowy obiekt i kopiuje wszystkie właściwości i metody, tj
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Należy również zauważyć, że w niektórych sytuacjach użyteczne może być użycie return
instrukcji w funkcji konstruktora, Podcast
aby zwrócić niestandardowy obiekt chroniący funkcje, na których klasa wewnętrznie polega, ale które należy ujawnić. Jest to wyjaśnione dalej w rozdziale 2 (Przedmioty) serii artykułów.
Możesz to powiedzieć a
i b
odziedziczyć po Podcast
. Co teraz, jeśli chcesz dodać do Podcastu metodę, która będzie obowiązywała dla wszystkich po tym, jak została a
i b
została utworzona? W takim przypadku użyj .prototype
następujących opcji:
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
Teraz zadzwoń a
i b
jeszcze raz:
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
Więcej informacji na temat prototypów można znaleźć tutaj . Jeśli chcesz zrobić więcej spadków, sugeruję przyjrzeć się temu .
W serii artykułów mam wymienione powyżej są bardzo zalecane , aby przeczytać, obejmują one również następujące tematy:
- Funkcje
- Obiekty
- Prototypy
- Wymuszanie nowości w funkcjach konstruktora
- Podnoszenie
- Automatyczne wstawianie średnika
- Właściwości statyczne i metody
Pamiętaj, że „funkcja” automatycznego wstawiania średnika w JavaScript (jak wspomniano w 6.) bardzo często powoduje dziwne problemy w kodzie. Dlatego wolę traktować to jako błąd niż funkcję.
Jeśli chcesz przeczytać więcej, oto dość interesujący artykuł MSDN na te tematy, niektóre z nich opisane zawierają jeszcze więcej szczegółów.
Co jest interesujące, aby przeczytać , jak również (także obejmujące zagadnienia wymienione powyżej) są te artykuły z MDN JavaScript Przewodnik :
Jeśli chcesz wiedzieć, jak emulować out
parametry c # (np. In DateTime.TryParse(str, out result)
) w JavaScript, możesz znaleźć przykładowy kod tutaj.
Ci z was, którzy pracują z IE (który nie ma konsoli JavaScript, chyba że otworzysz narzędzia programistyczne za pomocą F12i otworzysz kartę konsoli), mogą skorzystać z następującego fragmentu kodu . Pozwala na użycie console.log(msg);
jak w powyższych przykładach. Wystarczy wstawić przed Podcast
funkcją.
Dla Twojej wygody oto powyższy kod w jednym pełnym pojedynczym fragmencie kodu:
let console = { log: function(msg) {
let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
canvas.innerHTML += (br + (msg || "").toString());
}};
console.log('For details, see the explaining text');
function Podcast() {
// with this, you can instantiate without new (see description in text)
if (false === (this instanceof Podcast)) {
return new Podcast();
}
// private variables
var _somePrivateVariable = 123;
// object properties
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {
return this.getFullYear()
},
set: function(y) {
this.setFullYear(y)
}
});
// getters and setters - alternative syntax
var obj = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
<div id="log"></div>
Uwagi:
Kilka dobrych wskazówek, wskazówek i zaleceń dotyczących programowania JavaScript można znaleźć tutaj (najlepsze praktyki JavaScript) i tam („var” kontra „let”) . Zalecany jest również ten artykuł o niejawnych typecastach (przymus) .
Wygodnym sposobem używania klas i kompilowania ich w JavaScript jest TypeScript. Oto plac zabaw, na którym można znaleźć przykłady pokazujące, jak to działa. Nawet jeśli obecnie nie używasz TypeScript, możesz rzucić okiem, ponieważ możesz porównać TypeScript z wynikiem JavaScript w widoku obok siebie. Większość przykładów jest prosta, ale jest też przykład Raytracer, który możesz wypróbować natychmiast. Szczególnie polecam zapoznanie się z przykładami „Korzystanie z klas”, „Korzystanie z dziedziczenia” i „Korzystanie z ogólnych” poprzez wybranie ich w oknie combobox - są to ładne szablony, które można natychmiast wykorzystać w JavaScript. Maszynopis jest używany z Angular.
Aby uzyskać enkapsulację zmiennych lokalnych, funkcji itp. W JavaScript, sugeruję użycie wzoru podobnego do następującego (JQuery używa tej samej techniki):
<html>
<head></head>
<body><script>
'use strict';
// module pattern (self invoked function)
const myModule = (function(context) {
// to allow replacement of the function, use 'var' otherwise keep 'const'
// put variables and function with local module scope here:
var print = function(str) {
if (str !== undefined) context.document.write(str);
context.document.write("<br/><br/>");
return;
}
// ... more variables ...
// main method
var _main = function(title) {
if (title !== undefined) print(title);
print("<b>last modified: </b>" + context.document.lastModified + "<br/>");
// ... more code ...
}
// public methods
return {
Main: _main
// ... more public methods, properties ...
};
})(this);
// use module
myModule.Main("<b>Module demo</b>");
</script></body>
</html>
Oczywiście możesz - i powinieneś - umieścić kod skryptu w osobnym *.js
pliku; jest to po prostu napisane w wierszu, aby skrót był krótki.
Funkcje samo-wywołujące (znane również jako IIFE = wyrażenie funkcji natychmiastowego wywołania) opisano tutaj bardziej szczegółowo .