Я предпочитаю использовать ООП в крупномасштабных проектах, подобных тому, над которым я сейчас работаю. Мне нужно создать несколько классов в JavaScript, но, если я не ошибаюсь, есть как минимум несколько способов сделать это. Каков будет синтаксис и почему это должно быть сделано именно так?
Я бы хотел избежать использования сторонних библиотек - по крайней мере, на первых порах. В поисках других ответов я нашел статью Объектно-ориентированное программирование с помощью JavaScript, часть I. Наследование - Doc JavaScript, в которой обсуждается объектно-ориентированное программирование на JavaScript. Есть ли лучший способ наследования?
Лично мне нравится объявлять члены класса внутри тела функции. Я использую технику «исправления этого», чтобы создать замыкание, чтобы заставить его вести себя как класс. В моем блоге есть подробный пример: ncombo.wordpress.com/2012/12/30/…
Я перенес большую часть функциональных возможностей C++ ООП на JavaScript с простым и естественным синтаксисом. Смотрите мой ответ здесь: stackoverflow.com/a/18239463/1115652
В JavaScript нет классов. Но если вы хотите смоделировать поведение класса в JS, вы можете. См. Подробности в: symfony-world.blogspot.com/2013/10/…
Возможный дубликат Как я могу эмулировать «классы» в JavaScript? (со сторонней библиотекой или без нее)
Простой подход к определению класса Javascript с использованием объектов javascript: wapgee.com/story/i/203
Обратите внимание, что с 2015 года этот вопрос решается напрямую с помощью собственных возможностей класса. См. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@karim Ссылка на статью, которую вы упомянули в своем вопросе, не работает.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вот способ сделать это без использования каких-либо внешних библиотек:
// Define a class like this
function Person(name, gender){
// Add object properties like this
this.name = name;
this.gender = gender;
}
// Add methods like this. All Person objects will be able to invoke this
Person.prototype.speak = function(){
alert("Howdy, my name is" + this.name);
};
// Instantiate new objects with 'new'
var person = new Person("Bob", "M");
// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"
Настоящий ответ намного сложнее. Например, в JavaScript нет такого понятия, как классы. JavaScript использует схему наследования на основе prototype.
Кроме того, существует множество популярных библиотек JavaScript, которые имеют свой собственный стиль аппроксимации функциональных возможностей классов в JavaScript. Вы захотите проверить как минимум Опытный образец и jQuery.
Решение, какое из них является «лучшим», - отличный способ начать священную войну против Stack Overflow. Если вы приступаете к большему проекту с большим количеством JavaScript, определенно стоит изучить популярную библиотеку и делать это по-своему. Я сторонник прототипов, но Stack Overflow, похоже, склоняется к jQuery.
Поскольку существует только «один способ сделать это», без каких-либо зависимостей от внешних библиотек, способ, которым я написал, в значительной степени соответствует.
Но он работает не так, как язык X, где я узнал единственный верный способ, которым должна работать штука, которая используется для создания экземпляров объектов :(
Согласно developer.mozilla.org/en-US/docs/Web/JavaScript/… свойства также должны быть добавлены к прототипу ("Person.prototype.name = '';")
@DaveD - может быть, но, похоже, больше не ..?
@ kieren-johnstone Похоже, они удалили это
Прототип часто упускают из виду из-за методов ... Обратите внимание на большую разницу в производительности. Создание прототипов свойств объекта - не такая уж важная задача, и сложнее понять, когда вам следует / не следует этого делать. jsperf.com/asdffafaal/2
jQuery даже не дает возможности создать классовую функциональность ??? (Все классы, которые он имеет, являются классами CSS) Вы должны удалить его из этой части ответа.
Со второй половины 2015 года был выпущен новый стандарт EcmaScript 6, поэтому я предлагаю сделать это по-новому (намного чище и проще) es6-features.org/#ClassDefinition
Ваша ссылка "склоняться к jQuery" не работает.
Если вы стремитесь к простоте, вы можете полностью отказаться от ключевого слова «new» и просто использовать фабричные методы. Иногда я предпочитаю это, потому что мне нравится использовать JSON для создания объектов.
function getSomeObj(var1, var2){
var obj = {
instancevar1: var1,
instancevar2: var2,
someMethod: function(param)
{
//stuff;
}
};
return obj;
}
var myobj = getSomeObj("var1", "var2");
myobj.someMethod("bla");
Однако я не уверен, насколько сильно снизится производительность для больших объектов.
Строка obj.instancevar1 = var1 не нужна, поскольку внутренний объект будет иметь доступ к параметрам getSomeObj ().
Ух ты. У меня болит мозг, но в этом есть определенная элегантность. Полагаю, часть «obj.instancevar1 = var1» - это начало своего рода конструктора?
Только что видел комментарий Триптиха. Я понимаю. Итак, вы можете просто сделать что-то вроде instancevar1: var1, где создается экземпляр внутреннего объекта.
Точно ... когда вы используете {} для определения объекта, он имеет доступ к переменным, которые в настоящее время находятся в области видимости.
Кроме того, нет ключевого слова private, поэтому даже вне getSomeObj () вы можете получить / установить instancevar1. Как только вы получите закрытие и ключевое слово «this», вы станете золотым. Javascript - довольно красивый и гибкий язык.
При таком подходе вы теряете возможность наследования, и, поскольку вы не используете obj.prototype.something, вы определяете функции каждый раз, когда используете объект = больше памяти и медленнее.
Это очень полезно. Похоже, что это больше похоже на естественный поток JavaScript, если вы принимаете его как есть, который затем настраивает вас для создания и связывания объектов теперь, когда фабрика отправляет их!
JavaScript - это объектно-ориентированный, но он радикально отличается от других языков ООП, таких как Java, C# или C++. Не пытайтесь так понять это. Выбросьте старые знания и начните заново. JavaScript требует другого мышления.
Я бы посоветовал получить хорошее руководство или что-то по этому поводу. Я сам нашел Руководства по ExtJS лучшим для меня, хотя я не использовал фреймворк до или после его прочтения. Но он дает хорошее объяснение того, что есть что в мире JavaScript. Извините, похоже, что этот контент был удален. Вот вместо этого ссылка на archive.org копия. Работает сегодня. :П
Объектно-ориентированный? Я думал, что это функциональный.
Ссылка "ExtJS Tutorials" не работает.
Я думаю, было бы более пояснительно объяснить, что функции в javascript являются объектами, а правила javascript с ограниченными скобками делают каждый функциональный блок инкапсулированным.
Самый простой способ:
function Foo(a) {
var that=this;
function privateMethod() { .. }
// public methods
that.add = function(b) {
return a + b;
};
that.avg = function(b) {
return that.add(b) / 2; // calling another public method
};
}
var x = new Foo(10);
alert(x.add(2)); // 12
alert(x.avg(20)); // 15
Причина для that заключается в том, что this можно связать с чем-то еще, если вы укажете метод в качестве обработчика событий, поэтому вы сохраняете значение во время создания экземпляра и используете его позже.
Обновлено: это определенно не лучший способ, просто простой способ. Жду и хороших ответов!
Конструкция that = this здесь не нужна. Кроме того, методы add () и avg () будут скопированы для каждого «экземпляра» класса Foo, а не будут совместно использоваться ими.
Это необходимо (вроде как) в таком случае, но не в том простом случае, который вы указали.
Я думаю, вам следует прочитать Прототипное наследование в JavaScript и Классическое наследование в JavaScript Дугласа Крокфорда.
Примеры с его страницы:
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Эффект? Это позволит вам более элегантно добавлять методы:
function Parenizor(value) {
this.setValue(value);
}
Parenizor.method('setValue', function (value) {
this.value = value;
return this;
});
Также рекомендую его видео: Продвинутый JavaScript.
Больше видео вы можете найти на его странице: http://javascript.crockford.com/ В книге Джона Рейзига вы можете найти множество примеров с веб-сайта Дугласа Крокфора.
Это только я? Как, черт возьми, это элегантнее? Я бы назвал определения функций фактическим 'strings', так как называет многие вещи, но элегантность не входит в их число ...
@JAB, но отражение - это исключение, а не правило. С помощью вышеуказанного метода вы должны объявите все свои методы со строками.
Лучший способ определить класс в JavaScript - не определять класс.
Шутки в сторону.
Существует несколько разновидностей объектной ориентации, некоторые из них:
И, вероятно, другие, о которых я не знаю.
JavaScript реализует объектно-ориентированный объект на основе прототипов. В объектно-ориентированном методе на основе прототипов новые объекты создаются путем копирования других объектов (вместо создания экземпляров из шаблона класса), а методы живут непосредственно в объектах, а не в классах. Наследование осуществляется через делегирование: если объект не имеет метода или свойства, он просматривается по его прототипу (-ам) (то есть по объекту, из которого он был клонирован), затем по прототипам прототипа и так далее.
Другими словами: классов нет.
На самом деле в JavaScript есть хорошая доработка этой модели: конструкторы. Вы можете не только создавать объекты, копируя существующие, вы также можете создавать их, так сказать, «из воздуха». Если вы вызываете функцию с ключевым словом new, эта функция становится конструктором, а ключевое слово this будет указывать не на текущий объект, а на вновь созданный «пустой» объект. Итак, вы можете настроить объект как хотите. Таким образом, конструкторы JavaScript могут взять на себя одну из ролей классов в традиционном объектно-ориентированном пространстве на основе классов: служить шаблоном или схемой для новых объектов.
Итак, JavaScript - очень мощный язык, поэтому при желании довольно легко реализовать объектно-ориентированную систему в JavaScript на основе классов. Однако вы должны делать это только в том случае, если вам это действительно нужно, а не только потому, что это делает Java.
«Если вы вызываете функцию с ключевым словом new, эта функция становится конструктором, и ключевое слово this будет указывать не на текущий объект, а на вновь созданный« пустой »объект. Если вы вызываете функцию без ключевого слова new, это будет относиться к контексту вызова, по умолчанию глобальному объекту (окну). В строгом режиме по умолчанию используется значение undefined. call, apply и bind принимает контекст вызова в качестве первого параметра. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Поскольку я не соглашусь с планом фабрики YUI / Crockford и потому, что мне нравится сохранять все самодостаточными и расширяемыми, это мой вариант:
function Person(params)
{
this.name = params.name || defaultnamevalue;
this.role = params.role || defaultrolevalue;
if (typeof(this.speak)=='undefined') //guarantees one time prototyping
{
Person.prototype.speak = function() {/* do whatever */};
}
}
var Robert = new Person({name:'Bob'});
где в идеале тип теста - это что-то вроде прототипа первого метода
Мне это нравится. Я чаще всего использую стандартный синтаксис JS, потому что мне не нравится идея копирования функций в каждый экземпляр объекта. Я всегда скучал по красоте автономного решения, и это неплохо его решает.
Не уверен, но я понял, что определение функции-прототипа внутри области (что-то вроде закрытия) функции приводит к утечке памяти, поскольку сборщик мусора не может попасть туда в экземпляре этих классов.
var Animal = function(options) {
var name = options.name;
var animal = {};
animal.getName = function() {
return name;
};
var somePrivateMethod = function() {
};
return animal;
};
// usage
var cat = Animal({name: 'tiger'});
Это очень элегантный способ создать пригодную для использования структуру объекта без необходимости чего-либо импортировать. Я использовал систему классов Resig, но, возможно, она мне больше понравится. Спасибо.
Проблема с этим подходом заключается в том, что каждый раз, когда вы создаете новый экземпляр Animal, он будет переопределять функции, а не определять их только один раз с помощью прототипа.
Я предпочитаю использовать {SUPER: SYSTEM} Дэниела X. Мура. Это дисциплина, которая обеспечивает такие преимущества, как истинные переменные экземпляра, наследование на основе признаков, иерархии классов и параметры конфигурации. В приведенном ниже примере показано использование истинных переменных экземпляра, что, по моему мнению, является самым большим преимуществом. Если вам не нужны переменные экземпляра и вас устраивают только общедоступные или частные переменные, возможно, существуют более простые системы.
function Person(I) {
I = I || {};
Object.reverseMerge(I, {
name: "McLovin",
age: 25,
homeState: "Hawaii"
});
return {
introduce: function() {
return "Hi I'm " + I.name + " and I'm " + I.age;
}
};
}
var fogel = Person({
age: "old enough"
});
fogel.introduce(); // "Hi I'm McLovin and I'm old enough"
Вау, это само по себе не очень полезно, но взгляните на добавление подкласса:
function Ninja(I) {
I = I || {};
Object.reverseMerge(I, {
belt: "black"
});
// Ninja is a subclass of person
return Object.extend(Person(I), {
greetChallenger: function() {
return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
}
});
}
var resig = Ninja({name: "John Resig"});
resig.introduce(); // "Hi I'm John Resig and I'm 25"
Еще одно преимущество - возможность наследования на основе модулей и признаков.
// The Bindable module
function Bindable() {
var eventCallbacks = {};
return {
bind: function(event, callback) {
eventCallbacks[event] = eventCallbacks[event] || [];
eventCallbacks[event].push(callback);
},
trigger: function(event) {
var callbacks = eventCallbacks[event];
if (callbacks && callbacks.length) {
var self = this;
callbacks.forEach(function(callback) {
callback(self);
});
}
},
};
}
Пример того, что класс person включает привязываемый модуль.
function Person(I) {
I = I || {};
Object.reverseMerge(I, {
name: "McLovin",
age: 25,
homeState: "Hawaii"
});
var self = {
introduce: function() {
return "Hi I'm " + I.name + " and I'm " + I.age;
}
};
// Including the Bindable module
Object.extend(self, Bindable());
return self;
}
var person = Person();
person.bind("eat", function() {
alert(person.introduce() + " and I'm eating!");
});
person.trigger("eat"); // Blasts the alert!
Раскрытие информации: я Дэниел Икс Мур, а это мой {SUPER: SYSTEM}. Это лучший способ определить класс в JavaScript.
@DanielXMoore «Переменные экземпляра совместно используются отдельными экземплярами класса» Это не переменные экземпляра, это статические переменные / переменные класса.
@JAB Это неверно, статические переменные / переменные класса совместно используются экземплярами все класса. У каждого экземпляра есть свои собственные переменные экземпляра.
(Другими словами, используя обычное значение термина «переменная экземпляра», независимо от того, является ли переменная единицей, ортогональна уровню доступности переменной.)
Вы почти походили на супергероя за то, что претендовали на лучший xD
Простой подход к определению класса Javascript с использованием объектов javascript: wapgee.com/story/i/203
Ниже приведены способы создания объектов в javascript, которые я использовал до сих пор.
Пример 1:
obj = new Object();
obj.name = 'test';
obj.sayHello = function() {
console.info('Hello '+ this.name);
}
Пример 2:
obj = {};
obj.name = 'test';
obj.sayHello = function() {
console.info('Hello '+ this.name);
}
obj.sayHello();
Пример 3:
var obj = function(nameParam) {
this.name = nameParam;
}
obj.prototype.sayHello = function() {
console.info('Hello '+ this.name);
}
Пример 4: Фактические преимущества Object.create (). пожалуйста, обратитесь [эта ссылка]
var Obj = {
init: function(nameParam) {
this.name = nameParam;
},
sayHello: function() {
console.info('Hello '+ this.name);
}
};
var usrObj = Object.create(Obj); // <== one level of inheritance
usrObj.init('Bob');
usrObj.sayHello();
Пример 5 (настроенный объект Крокфорда Object.create):
Object.build = function(o) {
var initArgs = Array.prototype.slice.call(arguments,1)
function F() {
if ((typeof o.init === 'function') && initArgs.length) {
o.init.apply(this,initArgs)
}
}
F.prototype = o
return new F()
}
MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example
var userB = {
init: function(nameParam) {
this.id = MY_GLOBAL.nextId();
this.name = nameParam;
},
sayHello: function() {
console.info('Hello '+ this.name);
}
};
var bob = Object.build(userB, 'Bob'); // Different from your code
bob.sayHello();
Класс определяется так:
class Person {
constructor(strName, numAge) {
this.name = strName;
this.age = numAge;
}
toString() {
return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')';
}
}
let objPerson = new Person("Bob",33);
console.info(objPerson.toString());
@Justin: Пожалуйста, дайте мне знать, что недействительно?
Изучая эти обозначения, я также наткнулся на this.set (). Например: this.set ('порт', 3000). Я предполагаю, что это используется для установки свойства порта для объекта. Если да, то почему мы не используем напрямую: {порт: 3000}. Есть ли документация, по которой я могу получить более подробную информацию.
Вероятно, вы захотите создать шрифт с помощью шаблона складывания:
// Here is the constructor section.
var myType = function () {
var N = {}, // Enclosed (private) members are here.
X = this; // Exposed (public) members are here.
(function ENCLOSED_FIELDS() {
N.toggle = false;
N.text = '';
}());
(function EXPOSED_FIELDS() {
X.count = 0;
X.numbers = [1, 2, 3];
}());
// The properties below have access to the enclosed fields.
// Careful with functions exposed within the closure of the
// constructor, each new instance will have it's own copy.
(function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
Object.defineProperty(X, 'toggle', {
get: function () {
var before = N.toggle;
N.toggle = !N.toggle;
return before;
}
});
Object.defineProperty(X, 'text', {
get: function () {
return N.text;
},
set: function (value) {
N.text = value;
}
});
}());
};
// Here is the prototype section.
(function PROTOTYPE() {
var P = myType.prototype;
(function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
Object.defineProperty(P, 'numberLength', {
get: function () {
return this.numbers.length;
}
});
}());
(function EXPOSED_METHODS() {
P.incrementNumbersByCount = function () {
var i;
for (i = 0; i < this.numbers.length; i++) {
this.numbers[i] += this.count;
}
};
P.tweak = function () {
if (this.toggle) {
this.count++;
}
this.text = 'tweaked';
};
}());
}());
Этот код даст вам тип под названием мой тип. У него будут внутренние частные поля, называемые переключать и текст. Он также будет иметь следующие открытые элементы: поля считать и числа; свойства переключать, текст и numberLength; методы incrementNumbersByCount и поправить.
Схема складывания подробно описана здесь: Шаблон складывания Javascript
В спецификации ES2015 вы можете использовать синтаксис класса, который просто сахар по сравнению с системой-прототипом.
class Person {
constructor(name) {
this.name = name;
}
toString() {
return `My name is ${ this.name }.`;
}
}
class Employee extends Person {
constructor(name, hours) {
super(name);
this.hours = hours;
}
toString() {
return `${ super.toString() } I work ${ this.hours } hours.`;
}
}
Основное преимущество заключается в том, что инструментам статического анализа легче ориентироваться на этот синтаксис. Другим, пришедшим из языков на основе классов, также проще использовать этот язык в качестве многоязычного.
Остерегайтесь его текущих ограничений. Чтобы получить частные свойства, нужно прибегнуть к с использованием символов или WeakMaps. В будущих выпусках классы, скорее всего, будут расширены за счет включения этих недостающих функций.
Поддержка браузера на данный момент не очень хорош (поддерживается почти всеми, кроме IE), но теперь вы можете использовать эти функции с транспилером, таким как Вавилон.
Объектные классы с наследованием
var baseObject =
{
// Replication / Constructor function
new : function(){
return Object.create(this);
},
aProperty : null,
aMethod : function(param){
alert("Heres your " + param + "!");
},
}
newObject = baseObject.new();
newObject.aProperty = "Hello";
anotherObject = Object.create(baseObject);
anotherObject.aProperty = "There";
console.info(newObject.aProperty) // "Hello"
console.info(anotherObject.aProperty) // "There"
console.info(baseObject.aProperty) // null
Просто, мило, и все готово.
var Student = (function () {
function Student(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
this.fullname = firstname + " " + lastname;
}
Student.prototype.sayMyName = function () {
return this.fullname;
};
return Student;
}());
var user = new Student("Jane", "User");
var user_fullname = user.sayMyName();
Так TypeScript компилирует класс с конструктором в JavaScript.
Используйте гольф для отвечать @ liammclennan.
var Animal = function (args) {
return {
name: args.name,
getName: function () {
return this.name; // member access
},
callGetName: function () {
return this.getName(); // method call
}
};
};
var cat = Animal({ name: 'tiger' });
console.info(cat.callGetName());База
function Base(kind) {
this.kind = kind;
}
Класс
// Shared var
var _greeting;
(function _init() {
Class.prototype = new Base();
Class.prototype.constructor = Class;
Class.prototype.log = function() { _log.apply(this, arguments); }
_greeting = "Good afternoon!";
})();
function Class(name, kind) {
Base.call(this, kind);
this.name = name;
}
// Shared function
function _log() {
console.info(_greeting + " Me name is " + this.name + " and I'm a " + this.kind);
}
Действие
var c = new Class("Joe", "Object");
c.log(); // "Good afternoon! Me name is Joe and I'm a Object"
На примере Триптиха это может быть еще проще:
// Define a class and instantiate it
var ThePerson = new function Person(name, gender) {
// Add class data members
this.name = name;
this.gender = gender;
// Add class methods
this.hello = function () { alert('Hello, this is ' + this.name); }
}("Bob", "M"); // this instantiates the 'new' object
// Use the object
ThePerson.hello(); // alerts "Hello, this is Bob"
Это создает только один экземпляр объекта, но все же полезно, если вы хотите инкапсулировать набор имен для переменных и методов в классе. Обычно для конструктора не было бы аргументов «Bob, M», например, если бы методы были вызовами системы с ее собственными данными, такой как база данных или сеть.
Я все еще слишком новичок в JS, чтобы понять, почему здесь не используется prototype.
//new way using this and new
function Persons(name) {
this.name = name;
this.greeting = function() {
alert('Hi! I\'m ' + this.name + '.');
};
}
var gee=new Persons("gee");
gee.greeting();
var gray=new Persons("gray");
gray.greeting();
//old way
function createPerson(name){
var obj = {};
obj.name=name;
obj.greeting = function(){
console.info("hello I am"+obj.name);
};
return obj;
}
var gita=createPerson('Gita');
gita.greeting();отлично, что действительно немного похоже на синтаксис класса. почти дерьмо @ user1115652 предлагает: «Я перенес большую часть функциональных возможностей C++ ООП на JavaScript с помощью простого и естественного синтаксиса. См. мой ответ здесь: stackoverflow.com/a/18239463/1115652»
примечание: это дубликат stackoverflow.com/questions/355848