Я бачив пару подібних відповідей, але хочу зазначити, що цей пост найкраще описує його, тому я хотів би поділитися ним з вами.
Ось якийсь код, взятий з нього, який я змінив, щоб отримати повний приклад, який, сподіваємось, приносить користь спільноті, оскільки його можна використовувати як шаблон дизайну для класів.
Він також відповідає на ваше запитання:
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 + ' ...');
};
Враховуючи цей приклад, ви можете отримати доступ до статичних властивостей / функції наступним чином:
// access static properties/functions
console.log(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
Зауважте, що у podcast.immutableProp () ми маємо закриття : Посилання на _somePrivateVariable зберігається всередині функції.
Ви навіть можете визначити геттерів та сеттерів . Погляньте на цей фрагмент коду (де d
прототип об'єкта, для якого потрібно оголосити властивість, y
є приватною змінною, не видно за межами конструктора):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
Він визначає властивість d.year
через get
та set
функції - якщо ви не вказуєте set
, то властивість доступна лише для читання і не може бути змінена (пам’ятайте, що ви не отримаєте помилку, якщо спробуєте встановити її, але це не має ефекту). Кожна властивість має атрибути writable
, configurable
(дозволяють зміни після декларації) і enumerable
(дозволяють використовувати його як інтерв'юер), які за умовчанням false
. Ви можете встановити їх за defineProperty
допомогою 3-го параметра, наприклад enumerable: true
.
Чинним є також цей синтаксис:
// getters and setters - alternative syntax
var obj = { a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}
};
яка визначає властивість для читання / запису, властивість a
лише для читання b
та властивість лише для запису c
, за допомогою якої a
можна отримати доступ до власності .
Використання:
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
Примітки:
Щоб уникнути несподіваної поведінки, якщо ви забули new
ключове слово, пропоную вам додати в функцію наступне Podcast
:
// instantiation helper
function Podcast() {
if(false === (this instanceof Podcast)) {
return new Podcast();
}
// [... same as above ...]
};
Тепер обидві наступні моменти працюватимуть, як очікувалося:
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast(); // you can omit the new keyword because of the helper
Оператор 'new' створює новий об'єкт і копіює всі властивості та методи, тобто
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"
Зауважимо також, що в деяких ситуаціях може бути корисним використання return
оператора у функції конструктора Podcast
для повернення користувальницьких функцій захисту об'єкта, на який клавіатура покладається внутрішньо, але які потрібно піддавати впливу. Це пояснено далі у розділі 2 (Об'єкти) серії статей.
Ви можете сказати це a
і b
успадкувати від Podcast
. Тепер, якщо ви хочете , щоб додати метод до Podcast , який відноситься до всіх з них після того, як a
і b
було інстанціірован? У цьому випадку використовуйте .prototype
наступне:
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
Тепер дзвонити a
і b
знову:
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
Більш детальну інформацію про прототипи ви можете знайти тут . Якщо ви хочете зробити більше спадщини, пропоную розібратися в цьому .
У серії статей я вже згадував вище, настійно рекомендується читати, вони включають в себе також такі питання:
- Функції
- Об'єкти
- Прототипи
- Застосування нового для функцій конструктора
- Підйомник
- Автоматичне вставлення крапки з комою
- Статичні властивості та методи
Зауважте, що функція автоматичного вставлення крапки з комою в JavaScript (як зазначено в 6.) дуже часто є причиною виникнення дивних проблем у вашому коді. Отже, я б швидше розглядав це як помилку, ніж як особливість.
Якщо ви хочете прочитати більше, тут є досить цікава стаття MSDN про ці теми, деякі з них, описані там, містять ще більше деталей.
Що цікаво також читати (також висвітлюючи вищезазначені теми) - це статті з посібника з JavaScript по MDN :
Якщо ви хочете знати, як емулювати out
параметри c # (наприклад, в DateTime.TryParse(str, out result)
) у JavaScript, ви можете знайти зразок коду тут.
Тим із вас, хто працює з IE (у якого немає консолі для JavaScript, якщо ви не відкриєте інструменти для розробників за допомогою F12та відкрийте вкладку консолі), може бути корисний наступний фрагмент. Це дозволяє використовувати, console.log(msg);
як було використано в прикладах вище. Просто вставте його перед Podcast
функцією.
Для вашої зручності, ось наведений вище код в одному повному фрагменті одного коду:
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>
Примітки:
Деякі хороші поради, підказки та рекомендації щодо програмування JavaScript в цілому ви можете знайти тут (кращі практики JavaScript) і там ("var" порівняно з "дозволити") . Також рекомендується ця стаття про неявні типові передачі (примус) .
Зручним способом використання класів та компіляції їх у JavaScript є TypeScript. Ось майданчик, де ви можете знайти кілька прикладів, що показують, як це працює. Навіть якщо на даний момент ви не використовуєте TypeScript, ви можете ознайомитися, оскільки ви можете порівнювати TypeScript з результатом JavaScript на поданні збоку. Більшість прикладів прості, але є також приклад Raytracer, який можна спробувати миттєво. Рекомендую особливо вивчити приклади "Використання класів", "Використання успадкування" та "Використання загальної інформації", вибравши їх у комбінаційному вікні - це приємні шаблони, які ви можете миттєво використовувати в JavaScript. Друкарський текст використовується з кутовим.
Щоб досягти інкапсуляції локальних змінних, функцій тощо у JavaScript, я пропоную використовувати такий зразок, як наступний (JQuery використовує ту саму техніку):
<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>
Звичайно, ви можете - і повинні - помістити код сценарію в окремий *.js
файл; це просто написано в рядку, щоб приклад був коротким.
Тут викладено більш докладно функції самозвання (також відомі як IIFE = Виявлення функцій негайно викликаних функцій) .