Jakich technik można użyć do zdefiniowania klasy w JavaScript i jakie są ich kompromisy?

Wolę używać OOP w dużych projektach, takich jak ten, nad którym obecnie pracuję. Muszę utworzyć kilka klas w JavaScript, ale jeśli się nie mylę, istnieje co najmniej kilka sposobów, aby to zrobić. Jaka byłaby składnia i dlaczego miałoby to być zrobione w ten sposób?

Chciałbym unikać korzystania z bibliotek innych firm-przynajmniej na początku.
Szukając innych odpowiedzi znalazłem artykuł Programowanie obiektowe za pomocą JavaScript, część i: dziedziczenie - Doc JavaScript omawia programowanie obiektowe w języku JavaScript. Czy istnieje lepszy sposób na dziedziczenie?

Author: Daniel X Moore, 2008-12-23

19 answers

Oto sposób, aby to zrobić bez użycia zewnętrznych bibliotek:

// 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"
Teraz prawdziwa odpowiedź jest o wiele bardziej złożona. Na przykład, nie ma czegoś takiego jak klasy w JavaScript. JavaScript używa schematu dziedziczenia opartego na prototype.

Ponadto istnieje wiele popularnych bibliotek JavaScript, które mają swój własny styl przybliżania funkcjonalności podobnych do klas w JavaScript. Będziesz chciał sprawdzić co najmniej Prototype i jQuery.

Decyzja, która z nich jest "najlepsza", to świetny sposób na rozpoczęcie świętej wojny z przepełnieniem stosu. Jeśli rozpoczynasz większy projekt obciążający JavaScript, zdecydowanie warto nauczyć się popularnej biblioteki i zrobić to po swojemu. Jestem facetem od prototypów, ale Stack Overflow wydaje się skłaniać do jQuery.

Jeśli chodzi o to, że istnieje tylko "jeden sposób, aby to zrobić", bez żadnych zależności od zewnętrznych bibliotek, sposób, w jaki napisałem, jest w zasadzie tym.

 724
Author: Triptych,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-01 14:06:53

Najlepszym sposobem definiowania klasy w JavaScript jest nie definiowanie klasy.

Poważnie.

Istnieje kilka różnych smaków orientacji obiektowej, niektóre z nich to:

  • OO oparte na klasach (po raz pierwszy wprowadzone przez Smalltalk)
  • prototype-based OO (first introduced by Self)
  • oo oparte na multimetodzie (chyba po raz pierwszy wprowadzone przez CommonLoops)
  • oo (brak pomysłu)

I prawdopodobnie inni, których nie znam około.

JavaScript implementuje OO oparte na prototypach. W OO opartym na prototypach, nowe obiekty są tworzone przez kopiowanie innych obiektów (zamiast tworzenia instancji z szablonu klasy), A metody żyją bezpośrednio w obiektach zamiast w klasach. Dziedziczenie odbywa się poprzez delegację: jeśli obiekt nie ma metody lub właściwości, jest sprawdzany na jego prototypie (tzn. obiekcie, z którego został sklonowany), wtedy prototypy prototypu i tak dalej.

Innymi słowy: nie ma klasy.

JavaScript rzeczywiście ma ładny tweak tego modelu: constructors. Nie tylko można tworzyć obiekty kopiując istniejące, ale można również konstruować je" z powietrza", że tak powiem. Jeśli wywołasz funkcję za pomocą słowa kluczowego new, funkcja ta staje się konstruktorem, a słowo kluczowe this nie wskazuje na bieżący obiekt, ale na nowo utworzony "pusty". Możesz więc skonfigurować obiekt w dowolny sposób. W ten sposób konstruktory JavaScript mogą przyjąć jedną z role klas w tradycyjnym oo opartym na klasach: służącym jako szablon lub schemat dla nowych obiektów.

Teraz JavaScript jest bardzo potężnym językiem, więc dość łatwo jest zaimplementować system oo oparty na klasach W JavaScript, jeśli chcesz. Jednak powinieneś to robić tylko wtedy, gdy naprawdę masz do tego potrzebę, a nie tylko dlatego, że tak robi Java.

 210
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-23 16:44:21

Klasy ES2015

W specyfikacji ES2015 możesz użyć składni klasy, która jest tylko cukrem nad prototypowym systemem.

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

Korzyści

Główną korzyścią jest to, że narzędzia analizy statycznej łatwiej ukierunkować tę składnię. Łatwiej jest również innym pochodzącym z języków opartych na klasach używać tego języka jako poligloty.

Caveats

Uważaj na jego obecne ograniczenia. Aby osiągnąć prywatne własności, należy uciekać się do za pomocą symboli lub WeakMaps . W przyszłych wydaniach klasy najprawdopodobniej zostaną rozszerzone o te brakujące funkcje.

Wsparcie

Obsługa przeglądarki nie jest obecnie zbyt dobra (obsługiwana przez prawie wszystkich oprócz IE), ale możesz teraz korzystać z tych funkcji z transpilerem, takim jak Babel.

Zasoby

 74
Author: Dale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-01 15:27:13

Wolę używać Daniela X. Moore ' a {SUPER: SYSTEM}. Jest to dyscyplina, która zapewnia korzyści, takie jak zmienne rzeczywistych instancji, dziedziczenie oparte na cechach, hierarchie klas i opcje konfiguracji. Poniższy przykład ilustruje użycie prawdziwych zmiennych instancji, co moim zdaniem jest największą zaletą. Jeśli nie potrzebujesz zmiennych instancji i jesteś zadowolony tylko z publicznych lub prywatnych zmiennych, to prawdopodobnie istnieją prostsze systemy.

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"

Wow, to nie bardzo przydatne na jest własny, ale spójrz na dodanie podklasy:

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"

Kolejną zaletą jest możliwość posiadania modułów i dziedziczenia opartego na cechach.

// 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);
        });
      }
    },
  };
}

Przykład, że Klasa person zawiera moduł bindowalny.

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!

Ujawnienie: Jestem Daniel X. Moore i to jest mój {SUPER: SYSTEM}. Jest to najlepszy sposób na zdefiniowanie klasy w JavaScript.

 56
Author: Daniel X Moore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-16 22:14:51
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'});
 41
Author: liammclennan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-23 04:46:05

Poniżej przedstawiono sposoby tworzenia obiektów w javascript, z których dotychczas korzystałem

Przykład 1:

obj = new Object();
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}

Przykład 2:

obj = {};
obj.name = 'test';
obj.sayHello = function() {
    console.log('Hello '+ this.name);
}
obj.sayHello();

Przykład 3:

var obj = function(nameParam) {
    this.name = nameParam;
}
obj.prototype.sayHello = function() {
    console.log('Hello '+ this.name);
}

Przykład 4: rzeczywiste korzyści z obiektu.create (). zapraszam [ten link]

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();

Przykład 5 (dostosowany obiekt Crockforda.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();


Aby być na bieżąco z ES6 / ES2015

Klasa jest zdefiniowana w następujący sposób:

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());
 29
Author: Amol M Kulkarni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-03 09:12:52

Myślę, że powinieneś przeczytać prototypowe dziedziczenie w JavaScript i Klasyczne dziedziczenie w JavaScript .

Przykłady z jego strony:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

Efekt? Pozwoli Ci dodawać metody w bardziej elegancki sposób:

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});
Polecam również jego filmiki: Zaawansowany JavaScript . Więcej filmów znajdziesz na jego stronie: http://javascript.crockford.com / W książce Johna Reisiga można znaleźć wiele przykładów z Douglas Strona internetowa crockfora.
 24
Author: Jarek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-23 17:51:29

Ponieważ nie przyznam się do planu fabryki YUI/Crockford, a ponieważ lubię trzymać rzeczy samodzielnie i rozszerzalnie, to jest moja odmiana:

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'});

Gdzie najlepiej typ testu jest na czymś takim jak pierwsza metoda prototypu

 16
Author: annakata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-28 22:11:29

Jeśli wybierasz proste, możesz całkowicie uniknąć słowa kluczowego "nowy" i po prostu użyć metod fabrycznych. Wolę to czasami, ponieważ lubię używać JSON do tworzenia obiektów.

function getSomeObj(var1, var2){
  var obj = {
     instancevar1: var1,
     instancevar2: var2,
     someMethod: function(param)
     {  
          //stuff; 
     }
  };
  return obj;
}

var myobj = getSomeObj("var1", "var2");
myobj.someMethod("bla");

Nie jestem jednak pewien, jaki jest hit wydajności dla dużych obiektów.

 15
Author: Sam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-28 07:12:25
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();

Tak TypeScript kompiluje klasę z konstruktorem do JavaScript.

 12
Author: Mick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 20:58:33

Prosty sposób to:

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

Powodem that jest to, że this może być powiązane z czymś innym, jeśli podasz metodę jako obsługę zdarzenia, więc zapisujesz wartość podczas tworzenia instancji i używasz jej później.

Edit: to zdecydowanie nie jest najlepszy sposób, po prostu prosty sposób. Ja też czekam na dobre odpowiedzi!

 10
Author: orip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-22 23:33:03

Prawdopodobnie chcesz utworzyć typ, używając wzoru składania:

    // 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';
            };
        }());
    }());

Ten kod da ci Typ o nazwie myType . Będzie miał wewnętrzne pola prywatne o nazwie togglei text. Będzie również miał te odsłonięte elementy: pola count i numbers; właściwości przełączają, tekst i numberLength ; Metody incrementNumbersByCount i tweak.

Wzór składania jest szczegółowe informacje znajdują się tutaj: Javascript Folding Pattern

 9
Author: intrepidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-07 00:43:50

Kod golf dla @liammclennan ' S Odpowiedź .

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());
 3
Author: tponthieux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:27

MooTools (Moje narzędzia obiektowe) skupia się na idei klas . Można nawet rozszerzyć i wdrożyć z dziedziczenia.

Po opanowaniu tworzy absurdalnie wydajny javascript wielokrotnego użytku.

 2
Author: Ryan Florence,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-30 02:22:36

Klasy oparte na obiektach z dziedziczeniem

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
Proste, słodkie i gotowe.
 2
Author: Ulad Kasach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-05 15:23:45

A base

function Base(kind) {
    this.kind = kind;
}

A klasa

// 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);
}

Akcja

var c = new Class("Joe", "Object");
c.log(); // "Good afternoon! Me name is Joe and I'm a Object"
 1
Author: Mikael Dúi Bolinder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 11:28:01

Na przykładzie tryptyku może to być nawet prostsze:

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

To tworzy tylko pojedynczą instancję obiektu, ale nadal jest przydatne, jeśli chcesz zamknąć kilka nazw zmiennych i metod w klasie. Zwykle nie ma argumentów "Bob, M" do konstruktora, na przykład, jeśli metody byłyby wywołaniami systemu z własnymi danymi, takimi jak Baza Danych lub sieć.

Nadal jestem zbyt nowy w JS, aby zobaczyć, dlaczego to nie używa prototype rzeczy.

 1
Author: Roland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-23 08:39:38

JavaScript jest zorientowany obiektowo , ale radykalnie różni się od innych OOP języków takich jak Java, C# czy c++. Nie próbuj tego tak zrozumieć. Wyrzuć tę starą wiedzę i zacznij od nowa. JavaScript wymaga innego myślenia.

Proponowałbym jakąś dobrą instrukcję czy coś w tym temacie. sam znalazłemtutoriale ExtJS najlepsze dla mnie, chociaż nie korzystałem z frameworka przed lub po jego przeczytaniu. Ale to daje dobre wyjaśnienie o tym, co jest w świecie JavaScript. Przepraszam, wygląda na to, że ta treść została usunięta. Oto link do archive.org zamiast tego Kopiuj . Dzisiaj działa. : P

 0
Author: Vilx-,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-03 06:26:54

//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.log("hello I am"+obj.name);
 }; 
  return obj;
}

var gita=createPerson('Gita');
gita.greeting();
 0
Author: Avinash Maurya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-13 03:03:51