Jak "poprawnie" utworzyć obiekt niestandardowy w JavaScript?

Zastanawiam się, jaki jest najlepszy sposób na stworzenie obiektu JavaScript, który ma właściwości i metody.

Widziałem przykłady, w których osoba używała var self = this, a następnie używa self. we wszystkich funkcjach, aby upewnić się, że zakres jest zawsze poprawny.

Potem widziałem przykłady używania .prototype do dodawania właściwości, podczas gdy inni robią to w linii.

Czy ktoś może podać mi odpowiedni przykład obiektu JavaScript z jakimiś właściwościami i metodami?

 452
Author: dakab, 2009-10-20

15 answers

Istnieją dwa modele implementacji klas I instancji w JavaScript: sposób prototypowania i sposób zamknięcia. Oba mają zalety i wady, i istnieje wiele rozszerzonych odmian. Wielu programistów i bibliotek ma różne podejścia i funkcje użytkowe do obsługi klas w niektórych brzydszych częściach języka.

Rezultatem jest to, że w mieszanym towarzystwie będziecie mieli miszmasz metaklasy, wszystkie zachowujące się nieco inaczej. Co gorsze, większość materiałów samouczek JavaScript jest straszna i serwuje jakiś kompromis pomiędzy pokryciem wszystkich baz, pozostawiając cię bardzo zdezorientowanym. (Prawdopodobnie autor też jest zdezorientowany. Model obiektowy JavaScript jest bardzo różny od większości języków programowania, a w wielu miejscach wprost źle zaprojektowany.)

Zacznijmy od drogi prototypu . Jest to najbardziej natywny Język JavaScript, jaki można uzyskać: istnieje minimum kodu nadrzędnego i instanceof będzie działać z instancjami tego rodzaj obiektu.

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

Możemy dodać metody do instancji utworzonej przez new Shape, zapisując je do prototype lookup tej funkcji konstruktora:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Teraz do podklasowania, w jak można nazwać to, co JavaScript robi podklasowanie. Robimy to, całkowicie zastępując tę dziwną magiczną właściwość prototype:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

Przed dodaniem do niego metod:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

Ten przykład będzie działał i zobaczysz podobny kod w wielu samouczkach. Ale stary, to jest brzydkie: tworzymy instancję klasy bazowej, nawet jeśli nie zostanie utworzony żaden rzeczywisty kształt. Tak się składa, że w tym prostym przypadku JavaScript jest tak niechlujny: pozwala przekazać zero argumentów, w którym to przypadku x i y stają się undefined i są przypisane do this.x i this.y prototypu. Gdyby funkcja konstruktora robiła coś bardziej skomplikowanego, spadłaby płasko na jego twarz.

Musimy więc znaleźć sposób na stworzenie prototypowego obiektu, który zawiera metody i innych członków chcemy na poziomie klasy, bez wywoływania funkcji konstruktora klasy bazowej. Aby to zrobić, będziemy musieli zacząć pisać kod pomocniczy. Jest to najprostsze podejście, jakie znam:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

To przenosi elementy klasy bazowej w jej prototypie do nowej funkcji konstruktora, która nic nie robi, a następnie używa tego konstruktora. Teraz możemy napisać po prostu:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

Zamiast new Shape() nieprawości. Mamy teraz akceptowalny zestaw prymitywów do zbudowania klasy.

Istnieje kilka udoskonaleń i rozszerzeń, które możemy rozważyć w ramach tego modelu. Na przykład tutaj jest wersja składniowa-sugar:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

Każda z wersji ma tę wadę, że funkcja konstruktora nie może być dziedziczona, jak to ma miejsce w wielu językach. Więc nawet jeśli twoja podklasa nie dodaje nic do procesu budowy, musi pamiętać, aby wywołać konstruktor bazowy z dowolnymi argumentami, jakich chciała baza. Można to nieco zautomatyzować za pomocą apply, ale nadal masz do napisania:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

Więc powszechnym rozszerzeniem jest rozdzielenie rzeczy inicjalizacji do własnej funkcji, a nie samego konstruktora. Funkcja ta może następnie dziedziczyć z bazy po prostu dobrze:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Teraz mamy ten sam konstruktor funkcji kotła dla każdej klasy. Może możemy przenieść to do jego własnej funkcji pomocniczej, więc nie musimy jej ciągle wpisywać, na przykład zamiast Function.prototype.subclass, obracając ją i pozwalając funkcji klasy bazowej wypluć podklasa:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

...który zaczyna wyglądać nieco bardziej jak inne języki, choć z nieco niezgrabniejszą składnią. Możesz posypać kilka dodatkowych funkcji, jeśli chcesz. Może chcesz makeSubclass wziąć i zapamiętać nazwę klasy i podać domyślną toString używając jej. Może chcesz, aby konstruktor wykrył, kiedy został przypadkowo wywołany bez operatora new (co w przeciwnym razie często powodowałoby bardzo irytujące debugowanie):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Może chcesz prześlij wszystkich nowych członków i dodaj makeSubclass do prototypu, aby zaoszczędzić Ci konieczności pisania Class.prototype... dość dużo. Wiele systemów klasowych tak robi, np.:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

Istnieje wiele potencjalnych cech, które można uznać za pożądane w systemie obiektowym i nikt tak naprawdę nie zgadza się na jedną konkretną formułę.


Sposób zamknięcia , następnie. Pozwala to uniknąć problemów dziedziczenia opartego na prototypach JavaScript, nie używając w ogóle dziedziczenia. Zamiast:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Teraz każda instancja Shape będzie miała własną kopię metody toString (oraz wszelkich innych metod lub innych członków klasy, które dodamy).

Zła rzecz w tym, że każda instancja ma własną kopię każdego członka klasy, jest taka, że jest mniej wydajna. Jeśli masz do czynienia z dużą liczbą podklasowanych instancji, dziedziczenie prototypowe może Ci lepiej służyć. Również wywołanie metody klasy bazowej jest nieco irytujące, jak widać: musimy pamiętać, co metoda była zanim konstruktor podklasy ją przerobił, albo się zgubił.

[również dlatego, że nie ma tu dziedziczenia, operator instanceof nie będzie działał; będziesz musiał podać swój własny mechanizm sniffingu klas, jeśli tego potrzebujesz. Podczas gdy ty mógłbyś manipulować prototypowymi obiektami w podobny sposób jak w przypadku dziedziczenia prototypów, jest to trochę trudne i nie warto po prostu zacząć działać {36]}.]

Dobrą rzeczą w każdej instancji posiadającej własną metodę jest że metoda może być powiązana z konkretną instancją, która jest jej właścicielem. Jest to przydatne ze względu na dziwny sposób wiązania this w wywołaniach metod, który ma wynik, że jeśli odłączysz metodę od jej właściciela:

var ts= mycircle.toString;
alert(ts());

Wtedy this Wewnątrz metody nie będzie wystąpieniem Okręgu zgodnie z oczekiwaniami (w rzeczywistości będzie to obiekt globalny window, powodując powszechne niedogodności debugowania). W rzeczywistości zwykle dzieje się tak, gdy metoda jest brana i przypisana do setTimeout, onclick lub EventListener w Generale.

W sposób prototypowy, musisz dołączyć zamknięcie dla każdego takiego zadania:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

Lub, w przyszłości (lub teraz, jeśli włamać funkcji.prototype) można to również zrobić za pomocą function.bind():

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

Jeśli Twoje instancje są wykonywane w sposób zamknięcia, Wiązanie jest wykonywane za darmo przez closure nad zmienną instancji (Zwykle nazywaną that lub self, choć osobiście odradzałbym to drugie, ponieważ self ma już inne znaczenie w JavaScript). Nie dostajesz argumentów 1, 1 W powyższym fragmencie za darmo, więc nadal potrzebujesz innego zamknięcia lub bind(), jeśli chcesz to zrobić.

Istnieje wiele wariantów metody zamykania. Zamiast używać operatora new: {[54], możesz całkowicie pominąć this, tworząc nowythat i zwracając go]}

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Która droga jest "właściwa"? Jedno i drugie. Która jest najlepsza? To zależy od twojej sytuacji. FWIW mam tendencję do prototypowania dla prawdziwego JavaScript dziedziczenie, gdy robię mocno oo rzeczy, i zamknięcia dla prostych efektów strony wyrzucania.

Ale oba sposoby są dość intuicyjne dla większości programistów. Oba mają wiele potencjalnych niechlujnych odmian. Spotkasz oba (jak również wiele pomiędzy i ogólnie zepsutych schematów), jeśli używasz kodu/bibliotek innych osób. Nie ma jednej ogólnie przyjętej odpowiedzi. Witamy we wspaniałym świecie obiektów JavaScript.

[to była część 94 dlaczego JavaScript nie jest moim Ulubiony Język Programowania.]

 861
Author: bobince,
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-03-09 20:55:14

Używam tego wzoru dość często-odkryłem, że daje mi on dość dużą elastyczność, gdy tego potrzebuję. W użyciu jest raczej podobny do klas w stylu Java.

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

Używa funkcji anonimowej, która jest wywoływana podczas tworzenia, zwracając nową funkcję konstruktora. Ponieważ funkcja anonimowa jest wywoływana tylko raz, można w niej utworzyć prywatne zmienne statyczne (są one wewnątrz zamknięcia, widoczne dla innych członków klasy). Funkcją konstruktora jest zasadniczo standardowy obiekt Javascript-definiujesz prywatne atrybuty wewnątrz niego, a atrybuty publiczne są dołączane do zmiennej this.

Zasadniczo, to podejście łączy podejście Crockfordian ze standardowymi obiektami Javascript, aby stworzyć potężniejszą klasę.

Możesz go używać tak jak każdy inny obiekt Javascript:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method
 89
Author: ShZ,
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-10-20 21:57:38

Douglas Crockford omawia ten temat obszernie w The Good Parts . Zaleca unikanie operatora new do tworzenia nowych obiektów. Zamiast tego proponuje tworzenie niestandardowych konstruktorów. Na przykład:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

W Javascript funkcja jest obiektem i może być używana do konstruowania obiektów razem z operatorem new . Zgodnie z konwencją, funkcje przeznaczone do użycia jako konstruktory rozpoczynają się od wielkiej litery. Często widzisz rzeczy like:

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

W przypadku, gdy zapomnisz użyć operatora new podczas tworzenia instancji nowego obiektu, otrzymasz zwykłe wywołanie funkcji, a to jest powiązane z obiektem globalnym zamiast z nowym obiektem.

 24
Author: Diego Pino,
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-10-20 16:33:34

Aby kontynuować z odpowiedź bobince

W es6 możesz teraz utworzyć class

Więc teraz możesz zrobić:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

Więc rozciągnij na okrąg (tak jak w drugiej odpowiedzi) możesz zrobić:

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

Kończy się nieco czystszym w es6 i trochę łatwiejszym do odczytania.


Oto dobry przykład tego w działaniu:

class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `Shape at ${this.x}, ${this.y}`;
  }
}

class Circle extends Shape {
  constructor(x, y, r) {
    super(x, y);
    this.r = r;
  }

  toString() {
    let shapeString = super.toString();
    return `Circular ${shapeString} with radius ${this.r}`;
  }
}

let c = new Circle(1, 2, 4);

console.log('' + c, c);
 11
Author: Neal,
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 11:55:19

Możesz to zrobić również w ten sposób, używając struktur:

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

Wtedy:

var counter1 = createCounter();
counter1.increaseBy(4);
 6
Author: Eino Gourdin,
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-10-20 16:11:56

Kiedy używa się sztuczki zamknięcia NA "this" podczas wywoływania konstruktora, jest to w celu napisania funkcji, która może być używana jako wywołanie zwrotne przez inny obiekt, który nie chce wywoływać metody na obiekcie. Nie jest to związane z "poprawieniem zakresu".

Oto waniliowy obiekt JavaScript:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};
Możesz wiele wyciągnąć z lektury tego, co Douglas Crockford ma do powiedzenia na temat JavaScript. John Resig jest również genialny. Powodzenia!
 4
Author: Jonathan Feinberg,
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-10-20 15:56:43

Innym sposobem byłoby http://jsfiddle.net/nnUY4 / (Nie wiem, czy tego rodzaju obsługa tworzenia obiektów i funkcji ujawniających podąża za określonym wzorcem)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"
 4
Author: Fluchtpunkt,
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-06-07 01:20:28

Closure jest wszechstronny. bobince dobrze podsumowało podejścieprototype vs.closure podczas tworzenia obiektów. Można jednak naśladować niektóre aspekty OOP używając zamknięcia w sposób programowania funkcyjnego. Zapamiętaj funkcje są obiektami w JavaScript ; więc używaj funkcji jako obiektu w inny sposób.

Oto przykład zamknięcia:
function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}
Jakiś czas temu natknąłem się na artykuł Mozilli o zamknięciu. Oto co rzuca mi się w oczy: "zamknięcie pozwala niektóre dane (środowisko) kojarzysz z funkcją, która na nich działa. ma to oczywiste podobieństwa do programowania obiektowego, gdzie obiekty pozwalają nam skojarzyć niektóre dane (Właściwości obiektu) z jedną lub kilkoma metodami ". To był pierwszy raz, kiedy czytałem równoległość między closure i classic OOP bez odniesienia do prototypu. Jak?

Załóżmy, że chcesz obliczyć VAT niektórych pozycji. VAT prawdopodobnie pozostanie stabilny podczas żywotność aplikacji. Jeden sposób na zrobienie tego w OOP (pseudo kod):

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

Zasadniczo przekazujesz wartość VAT do swojego konstruktora i twoja metoda obliczania może na niej działać poprzez zamknięcie . Teraz zamiast używać klasy/konstruktora, Przekaż swój VAT jako argument do funkcji. Ponieważ jedyne, co Cię interesuje, to samo obliczenie, zwraca nową funkcję, która jest metodą obliczania:

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

W Twoim projekcie określ wartości najwyższego poziomu, które są dobre kandydat co to jest VAT do obliczenia. Z reguły za każdym razem, gdy przekazujesz te same argumenty dalej i dalej, istnieje sposób na poprawę go za pomocą zamknięcia. Nie ma potrzeby tworzenia tradycyjnych obiektów.

Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

 4
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
2016-04-20 20:51:19

Tworzenie obiektu

Najprostszym sposobem tworzenia obiektu w JavaScript jest użycie następującej składni:

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

To świetnie sprawdza się przy przechowywaniu danych w uporządkowany sposób.

W przypadku bardziej złożonych przypadków użycia, często lepiej jest tworzyć instancje funkcji :]}

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

To pozwala na tworzenie wielu obiektów, które mają ten sam "plan", podobny do tego, jak używasz klas w np. Java.

Można to jednak zrobić bardziej efektywnie, używając prototypu.

Gdy różne instancje funkcji mają te same metody lub właściwości, można przenieść je do prototypu tego obiektu. W ten sposób każda instancja funkcji ma dostęp do tej metody lub właściwości, ale nie musi być duplikowana dla każdej instancji.

W naszym przypadku sensowne jest przeniesienie metody f do prototypu :

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

Spadek

Jest to prosty, ale skuteczny sposób na dziedziczenie w JavaScript, jest użycie następującej dwulinijki:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

To jest podobne do robienia tego:

B.prototype = new A();

Główna różnica między nimi polega na tym, że konstruktor A nie jest uruchamiany przy użyciu Object.create, który jest bardziej intuicyjny i bardziej podobny do dziedziczenia opartego na klasach.

Zawsze możesz wybrać opcjonalne uruchomienie konstruktora A podczas tworzenia nowej instancji B przez dodanie jej do konstruktora B:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

Jeśli chcesz przekazać wszystkie argumenty B do A, możesz również użyć Function.prototype.apply() :

function B() {
    A.apply(this, arguments); // This is optional
}

Jeśli chcesz mieszać inny obiekt do łańcucha konstruktora B, możesz połączyć Object.create z Object.assign :

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

Demo

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());

Uwaga

Object.create można bezpiecznie używać w każdej nowoczesnej przeglądarce, w tym IE9+. Object.assign Nie działa w żadnej wersji IE ani w niektórych przeglądarkach mobilnych. Zaleca się polyfill Object.create i / lub Object.assign jeśli chcesz ich używać i wspierać przeglądarki, które ich nie implementują.

Możesz znaleźć polyfill dla Object.create tutaj i jeden dla Object.assign tutaj .

 3
Author: John Slegers,
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-02-14 11:41:39

Oprócz zaakceptowanej odpowiedzi z 2009 roku. Jeśli można kierować nowoczesne przeglądarki, można skorzystać z obiektu .defineProperty .

Obiekt.metoda defineProperty() definiuje nową właściwość bezpośrednio na obiekt lub modyfikuje istniejącą właściwość obiektu i zwraca obiekt. Źródło: Mozilla

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();
Aby zobaczyć listę kompatybilności komputerów i urządzeń mobilnych, zobacz listę kompatybilności przeglądarek Mozilli. Tak, IE9+ obsługuje it, a także Safari mobile.
 0
Author: Alex Nolasco,
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-10-20 18:16:49

Możesz również spróbować tego

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();
 0
Author: G Naga Subrahmanyam,
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-10-18 18:26:44

Wzór, Który Dobrze Mi Służy

var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

Po pierwsze, możesz zmienić preferencje dodawania metod do instancji zamiast obiektu prototype konstruktora. Prawie zawsze deklaruję metody wewnątrz konstruktora, ponieważ bardzo często używam konstruktora do celów związanych z dziedziczeniem i dekoratorami.

Oto jak decyduję, gdzie są wypisywane deklaracje:

  • nigdy nie deklaruj metody bezpośrednio w obiekcie context (this)
  • niech var deklaracje mają pierwszeństwo przed function deklaracje
  • niech prymitywy mają pierwszeństwo przed obiektami ({} i [])
  • niech public deklaracje mają pierwszeństwo przed private deklaracje
  • preferuj Function.prototype.bind ponad thus, self, vm, etc
  • unikaj deklarowania klasy w ramach innej klasy, chyba że:
    • powinno być oczywiste, że te dwa są nierozłączne
    • Klasa wewnętrzna implementuje polecenie Wzór
    • Klasa wewnętrzna implementuje wzorzec Singletona
    • Klasa wewnętrzna implementuje wzorzec stanu
    • Klasa wewnętrzna implementuje inny wzorzec projektowy, który gwarantuje to]}
  • zawsze zwracaj this zzakresu leksykalnego przestrzeni zamknięcia.

Oto dlaczego pomagają:

Porwanie Konstruktora
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
Projekt Modelu
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
Wzorce Projektowe
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

Jak widzisz, naprawdę nie mam potrzeby thus ponieważ ja wolę Function.prototype.bind (lub .call lub .apply) nad thus. W naszej klasie Singleton, nawet nie nazwaliśmy jej thus, ponieważ INSTANCE przekazuje więcej informacji. Dla Model, zwracamy this tak, że możemy wywołać Konstruktor używając .call, aby zwrócić instancję, którą do niego przekazaliśmy. Redundantnie przypisaliśmy go do zmiennej updated, choć jest przydatny w innych scenariuszach.

Obok, wolę konstruowanie obiektów literałów za pomocą słowa kluczowego new {nawiasy}:

Preferowane
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
Nie Preferowane
var klass = Super.apply({
    override: x
});

Jak widzisz, ten ostatni nie ma możliwości nadpisania właściwości "override" swojej superklasy.

Jeśli dodam metody do obiektu prototype klasy, preferuję obiekt dosłowny -- ze słowem kluczowym new lub bez niego:

Preferowane
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
Nie Preferowane
Klass.prototype.method = function m() {...};
 0
Author: Cody,
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-02-26 22:50:00

Chciałbym wspomnieć, że możemy użyć a Title lub A String do zadeklarowania obiektu.
Istnieją różne sposoby na wywołanie każdego z nich. Patrz poniżej:

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));
 0
Author: Chetabahana,
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-06-01 14:51:11

Zasadniczo nie ma pojęcia o klasie w JS, więc używamy funkcji jako konstruktora klasy, który jest istotny z istniejącymi wzorami projektowymi.

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

Do tej pory JS nie ma pojęcia, że chcesz utworzyć obiekt, więc nadchodzi nowe słowo kluczowe.

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

Ref: Professional JS for web developers-Nik Z

 -1
Author: Airwind711,
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
2015-11-11 00:46:44
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);
 -1
Author: adeola olanrewaju,
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
2015-11-18 23:43:53