Какие методы можно использовать для определения класса в JavaScript, и каковы их компромиссы? - PullRequest
679 голосов
/ 23 декабря 2008

Я предпочитаю использовать ООП в крупных проектах, таких как тот, над которым я сейчас работаю. Мне нужно создать несколько классов в JavaScript, но, если я не ошибаюсь, есть по крайней мере несколько способов сделать это. Каков будет синтаксис и почему это будет сделано таким образом?

Я бы хотел избежать использования сторонних библиотек - по крайней мере, сначала.
В поисках других ответов я нашел статью Объектно-ориентированное программирование на JavaScript, часть I: Наследование - Doc JavaScript , в которой обсуждается объектно-ориентированное программирование на JavaScript. Есть ли лучший способ сделать наследство?

Ответы [ 19 ]

734 голосов
/ 23 декабря 2008

Вот способ сделать это без использования каких-либо внешних библиотек:

// 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 .

Решение, какой из них является «лучшим», является отличным способом начать священную войну с переполнением стека. Если вы начинаете большой JavaScript-проект, то определенно стоит изучить популярную библиотеку и делать это по-своему. Я опытный парень, но переполнение стека, похоже, склоняется к jQuery.

Поскольку существует только «один способ сделать это», без каких-либо зависимостей от внешних библиотек, то, как я написал, в значительной степени так и есть.

212 голосов
/ 23 декабря 2008

Лучший способ определить класс в JavaScript - это не определять класс.

Серьезно.

Существует несколько различных вариантов объектно-ориентированной ориентации, некоторые из них:

  • OO на основе класса (впервые представлен Smalltalk)
  • ОО на основе прототипа (впервые представленный Self)
  • мультиметодная ОО (впервые представленная CommonLoops, я думаю)
  • ОО на основе предикатов (без понятия)

И, вероятно, другие, о которых я не знаю.

JavaScript реализует ОО на основе прототипов. В ОО на основе прототипов новые объекты создаются путем копирования других объектов (вместо создания экземпляров из шаблона класса), а методы живут непосредственно в объектах, а не в классах. Наследование осуществляется с помощью делегирования: если у объекта нет метода или свойства, выполняется поиск его прототипа (т. Е. Объекта, из которого он был клонирован), затем прототипы прототипа и т. Д.

Другими словами: нет классов.

JavaScript на самом деле имеет приятную настройку этой модели: конструкторы. Вы можете не только создавать объекты путем копирования существующих, но и создавать их, так сказать, «из воздуха». Если вы вызываете функцию с ключевым словом new, эта функция становится конструктором, а ключевое слово this не будет указывать на текущий объект, а вместо этого на вновь созданный «пустой». Таким образом, вы можете настроить объект так, как вам нравится. Таким образом, конструкторы JavaScript могут играть одну из ролей классов в традиционной ОО на основе классов: служить шаблоном или планом для новых объектов.

Теперь, JavaScript - очень мощный язык, поэтому довольно просто реализовать OO-систему на основе классов в JavaScript , если хотите. Однако вам следует делать это только в том случае, если вам это действительно нужно, а не только потому, что так делает Java.

82 голосов
/ 08 июня 2015

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.`;
  }
}

Преимущества

Основным преимуществом является то, что инструментам статического анализа легче ориентироваться на этот синтаксис. Для других, приходящих из языков классов, также проще использовать этот язык как полиглот.

Предостережения

Остерегайтесь его текущих ограничений. Чтобы получить частные свойства, нужно прибегнуть к , используя Symbols или WeakMaps . В будущих выпусках классы, скорее всего, будут расширены для включения этих отсутствующих функций.

Поддержка

Поддержка браузера на данный момент не очень хороша (поддерживается почти всеми, кроме IE), но теперь вы можете использовать эти функции с транспортом, таким как Babel .

Ресурсы

56 голосов
/ 17 сентября 2010

Я предпочитаю использовать Даниэля X. Мура {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!

Раскрытие: я Даниэль X. Мур, и это мой {SUPER: SYSTEM}. Это лучший способ определить класс в JavaScript.

41 голосов
/ 23 июля 2009
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'});
32 голосов
/ 04 марта 2013

Ниже приведены способы создания объектов в javascript, которые я использовал до сих пор

Пример 1:

obj = new Object();
obj.name = 'test';
obj.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 (пользовательский объект Crocford's 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());
24 голосов
/ 23 декабря 2008

Я думаю, вам следует прочитать Дуглас Крокфорд Прототип наследования в 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/ В книге Джона Рейсига вы можете найти много примеров с сайта Дугласа Крокфора.

16 голосов
/ 29 декабря 2008

Потому что я не допущу план фабрики 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'});

где в идеале тест typeof выполняется на чем-то похожем на первый прототип метода

15 голосов
/ 23 декабря 2008

Если вы идете по простому, вы можете полностью избежать ключевого слова «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");

Хотя я не уверен, что производительность падает для больших объектов.

12 голосов
/ 15 марта 2016
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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...