Какие методы можно использовать для определения класса в JavaScript и каковы их компромиссы?
Я предпочитаю использовать ООП в крупномасштабных проектах, таких как тот, над которым я сейчас работаю. Мне нужно создать несколько классов в JavaScript, но если я не ошибаюсь, есть по крайней мере несколько способов сделать это. Каким будет синтаксис и почему это будет сделано таким образом?
Я бы хотел избежать использования сторонних библиотек - по крайней мере, сначала.
В поисках других ответов я нашел статью Объектно-ориентированное программирование с JavaScript, часть I: Наследование - Doc JavaScript, в котором обсуждается объектно-ориентированное программирование в JavaScript, Есть ли лучший способ сделать наследование?
Ответы
Ответ 1
Здесь можно сделать это без использования каких-либо внешних библиотек:
// 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. Вы захотите проверить как минимум Prototype и jQuery.
Определение того, какой из них является "лучшим", - отличный способ начать святую войну с помощью Stack Overflow. Если вы приступаете к большому проекту с тяжелым дизайном JavaScript, то определенно стоит изучить популярную библиотеку и сделать это по-своему. Я парень-прототип, но Stack Overflow, похоже, склоняется к jQuery.
Поскольку существует только "один способ сделать это", без каких-либо зависимостей от внешних библиотек, то, как я писал, в значительной степени.
Ответ 2
Лучший способ определить класс в JavaScript - не определять класс.
Серьезно.
Существует несколько различных вариантов объектной ориентации, некоторые из которых:
- класс OO (впервые введенный Smalltalk)
- прототип OO (впервые введенный Self)
- многооходный OO (впервые представленный CommonLoops, я думаю)
- предикат OO (без понятия)
И, возможно, другие, о которых я не знаю.
JavaScript реализует прототип OO. В OO на основе прототипа новые объекты создаются путем копирования других объектов (вместо создания экземпляра из шаблона класса), а методы живут непосредственно в объектах, а не в классах. Наследование выполняется путем делегирования: если у объекта нет метода или свойства, он просматривается на его прототипе (то есть объекте, с которым он клонирован), затем прототипе прототипов и т.д.
Другими словами: нет классов.
У JavaScript действительно есть хорошая настройка этой модели: конструкторы. Не только вы можете создавать объекты, копируя существующие, вы также можете построить их "из воздуха", так сказать. Если вы вызываете функцию с ключевым словом new
, эта функция становится конструктором, а ключевое слово this
не указывает на текущий объект, а вместо этого на вновь созданный "пустой". Таким образом, вы можете настроить объект любым способом. Таким образом, конструкторы JavaScript могут использовать одну из ролей классов в традиционном OO на основе классов: служить в качестве шаблона или плана для новых объектов.
Теперь JavaScript - очень мощный язык, поэтому довольно легко реализовать систему OO на основе классов в JavaScript, если вы хотите. Тем не менее, вы должны делать это только в том случае, если вам действительно нужно это, а не только потому, что это делает Java.
Ответ 3
Классы ES2015
В спецификации 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.`;
}
}
Преимущества
Основное преимущество заключается в том, что инструменты статического анализа легче ориентировать на этот синтаксис. Кроме того, для других людей, использующих языки на основе классов, легче использовать язык в качестве полиглота.
Предостережения
Будьте осторожны с его текущими ограничениями. Чтобы получить частные свойства, нужно прибегнуть к Поддержка браузеров на данный момент не очень хороша (поддерживается почти всеми, кроме IE), но теперь вы можете использовать эти функции с помощью transpiler как Babel.
Ресурсы
Ответ 4
Я предпочитаю использовать Daniel X. Moore {SUPER: SYSTEM}
. Это дисциплина, которая обеспечивает такие преимущества, как истинные переменные экземпляра, наследство на основе признаков, иерархии классов и параметры конфигурации. В приведенном ниже примере показано использование переменных истинного экземпляра, что, по моему мнению, является самым большим преимуществом. Если вам не нужны переменные экземпляра и они довольны только общедоступными или частными переменными, то, вероятно, существуют более простые системы.
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!
Раскрытие: Я Daniel X. Moore, и это мой {SUPER: SYSTEM}
. Это лучший способ определить класс в JavaScript.
Ответ 5
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'});
Ответ 6
Ниже приведены способы создания объектов в javascript, которые я использовал до сих пор
Пример 1:
obj = new Object();
obj.name = 'test';
obj.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
Пример 2:
obj = {};
obj.name = 'test';
obj.sayHello = function() {
console.log('Hello '+ this.name);
}
obj.sayHello();
Пример 3:
var obj = function(nameParam) {
this.name = nameParam;
}
obj.prototype.sayHello = function() {
console.log('Hello '+ this.name);
}
Пример 4: Фактические преимущества Object.create(). обратитесь пожалуйста [эта ссылка]
var Obj = {
init: function(nameParam) {
this.name = nameParam;
},
sayHello: function() {
console.log('Hello '+ this.name);
}
};
var usrObj = Object.create(Obj); // <== one level of inheritance
usrObj.init('Bob');
usrObj.sayHello();
Пример 5 (настраиваемый объект Crockford 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.log('Hello '+ this.name);
}
};
var bob = Object.build(userB, 'Bob'); // Different from your code
bob.sayHello();
<ч/" > Чтобы сохранить ответ, обновленный с помощью ES6/ES2015
Класс определяется следующим образом:
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.log(objPerson.toString());
Ответ 7
Я думаю, вы должны прочитать Douglas Crockford Прототипное наследование в 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/
В книге Джона Рейсига вы можете найти много примеров с сайта Douglas Crockfor.
Ответ 8
Потому что я не буду признавать план YUI/Crockford factory, и потому, что мне нравится держать все в ярости и расширяемо, это мое изменение:
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'});
где в идеале тест типа находится на чем-то вроде первого метода, прототипированного
Ответ 9
Если вы собираетесь просто, вы можете полностью исключить "новое" ключевое слово и просто использовать методы factory. Иногда я предпочитаю это, потому что мне нравится использовать 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");
Я не уверен, что производительность удалась для больших объектов.
Ответ 10
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.
Ответ 11
Вероятно, вы захотите создать тип, используя шаблон Folding:
// 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 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';
};
}());
}());
Этот код даст вам тип с именем myType. Он будет иметь внутренние частные поля, называемые toggle и текст. Он также будет иметь этих открытых участников: поля count и числа; свойства toggle, текст и numberLength; методы incrementNumbersByCount и настроить.
Складной паттерн подробно описан здесь:
Javascript Folding Pattern
Ответ 12
Простым способом является:
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
может быть привязан к чему-то другому, если вы даете метод как обработчик события, поэтому вы сохраняете значение во время создания экземпляра и используете его позже.
Изменить: это определенно не лучший способ, просто простой способ. Я тоже жду хороших ответов!
Ответ 13
Код для гольфа для @liammclennan answer.
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.log(cat.callGetName());
Ответ 14
MooTools (Мои объектно-ориентированные инструменты) сосредоточен на идее . Вы можете даже расширить и реализовать с наследованием.
При освоении он делает смехотворно многоразовый, мощный javascript.
Ответ 15
Объектно-ориентированные классы с наследованием
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.log(newObject.aProperty) // "Hello"
console.log(anotherObject.aProperty) // "There"
console.log(baseObject.aProperty) // null
Простой, сладкий и получится.
Ответ 16
JavaScript объектно-ориентированный, но он радикально отличается от другого OOP, такие как Java, С# или С++. Не пытайтесь это понять. Бросьте старые знания и начните заново. JavaScript нуждается в другом мышлении.
Я бы посоветовал получить хорошее руководство или что-то по этому поводу. Я сам нашел ExtJS Tutorials лучшим для меня, хотя я не использовал рамки до или после прочтения. Но это дает хорошее объяснение тому, что есть в мире JavaScript. Извините, похоже, что это содержимое было удалено. Здесь вместо этого ссылка на archive.org copy. Работает сегодня.: P
Ответ 17
База
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.log(_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"
Ответ 18
На основе примера Triptych это может быть даже проще:
// 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
.