Czy klasy / obiekty JavaScript mogą mieć konstruktory? Jak są tworzone?
Czy klasy / obiekty JavaScript mogą mieć konstruktory? Jak są tworzone?
Odpowiedzi:
Korzystanie z prototypów:
function Box(color) // Constructor
{
this.color = color;
}
Box.prototype.getColor = function()
{
return this.color;
};
Ukrywanie „koloru” (nieco przypomina prywatną zmienną członka):
function Box(col)
{
var color = col;
this.getColor = function()
{
return color;
};
}
Stosowanie:
var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue
var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
color. Sugeruję, że używasz w dużej mierze zależy od osobistych preferencji (ochrona kontra prostota)
vartworzy zmienną prywatną. thistworzy zmienną publiczną
Foo, podczas gdy w drugim przypadku będzie wiedział, że Foojest wywoływany. Bardzo pomocne przy debugowaniu.
Oto szablon, którego czasami używam do zachowania podobnego do OOP w JavaScript. Jak widać, można symulować członków prywatnych (zarówno statycznych, jak i instancji) przy użyciu zamknięć. To, new MyClass()co zwróci, to obiekt z jedynie właściwościami przypisanymi do thisobiektu i prototypeobiektu „klasy”.
var MyClass = (function () {
// private static
var nextId = 1;
// constructor
var cls = function () {
// private
var id = nextId++;
var name = 'Unknown';
// public (this instance only)
this.get_id = function () { return id; };
this.get_name = function () { return name; };
this.set_name = function (value) {
if (typeof value != 'string')
throw 'Name must be a string';
if (value.length < 2 || value.length > 20)
throw 'Name must be 2-20 characters long.';
name = value;
};
};
// public static
cls.get_nextId = function () {
return nextId;
};
// public (shared across instances)
cls.prototype = {
announce: function () {
alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
}
};
return cls;
})();
Zapytano mnie o dziedziczenie przy użyciu tego wzorca, więc oto:
// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
// We use an intermediary empty constructor to create an
// inheritance chain, because using the super class' constructor
// might have side effects.
var construct = function () {};
construct.prototype = superCls.prototype;
cls.prototype = new construct;
cls.prototype.constructor = cls;
cls.super = superCls;
}
var MyChildClass = (function () {
// constructor
var cls = function (surName) {
// Call super constructor on this instance (any arguments
// to the constructor would go after "this" in call(…)).
this.constructor.super.call(this);
// Shadowing instance properties is a little bit less
// intuitive, but can be done:
var getName = this.get_name;
// public (this instance only)
this.get_name = function () {
return getName.call(this) + ' ' + surName;
};
};
inherit(cls, MyClass); // <-- important!
return cls;
})();
I przykład wykorzystania tego wszystkiego:
var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"
var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"
alert(john instanceof MyClass); // true
Jak widać, klasy poprawnie komunikują się ze sobą (współdzielą statyczny identyfikator z MyClass, announcemetoda używa poprawnej get_namemetody itp.)
Należy zwrócić uwagę na potrzebę ukrywania właściwości instancji. W rzeczywistości można sprawić, aby inheritfunkcja przechodziła przez wszystkie właściwości instancji (za pomocą hasOwnProperty), które są funkcjami, i dodawała super_<method name>właściwość automatycznie . Pozwoliłoby to zadzwonić this.super_get_name()zamiast przechowywać go w wartości tymczasowej i nazwać go związanym za pomocą call.
W przypadku metod na prototypie nie musisz się jednak przejmować powyższym, jeśli chcesz uzyskać dostęp do metod prototypowych superklasy, możesz po prostu zadzwonić this.constructor.super.prototype.methodName. Jeśli chcesz, aby było mniej gadatliwe, możesz oczywiście dodać właściwości wygody. :)
cls.prototypeczęści: „współdzielone między instancjami” służy tylko do odczytu wartości (wywołanie announce). Jeśli ustawisz myClassInstance.announceinną wartość, tworzy ona nową właściwość w myClassInstance, więc ma zastosowanie tylko do tego obiektu, a nie do innych instancji klasy. Przypisanie do MyClass.prototype.announcewpłynie jednak na wszystkie instancje.
MyClass.get_nextId()
Wydaje mi się, że większość z was podaje przykłady programów pobierających i ustawiających, a nie konstruktorów, tj. Http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming) .
lunched-dan był bliżej, ale przykład nie działał w jsFiddle.
W tym przykładzie tworzona jest funkcja prywatnego konstruktora, która działa tylko podczas tworzenia obiektu.
var color = 'black';
function Box()
{
// private property
var color = '';
// private constructor
var __construct = function() {
alert("Object Created.");
color = 'green';
}()
// getter
this.getColor = function() {
return color;
}
// setter
this.setColor = function(data) {
color = data;
}
}
var b = new Box();
alert(b.getColor()); // should be green
b.setColor('orange');
alert(b.getColor()); // should be orange
alert(color); // should be black
Jeśli chcesz przypisać właściwości publiczne, konstruktor można zdefiniować jako taki:
var color = 'black';
function Box()
{
// public property
this.color = '';
// private constructor
var __construct = function(that) {
alert("Object Created.");
that.color = 'green';
}(this)
// getter
this.getColor = function() {
return this.color;
}
// setter
this.setColor = function(color) {
this.color = color;
}
}
var b = new Box();
alert(b.getColor()); // should be green
b.setColor('orange');
alert(b.getColor()); // should be orange
alert(color); // should be black
Box()funkcją :). Ale ten przykład, jak również przykłady w innych odpowiedziach, można łatwo rozszerzyć na parametry.
Boxfunkcji i możesz zacząć (jest to nadal „prywatny”). „Prywatne” w Javascript oznacza po prostu dostępne w zakresie leksykalnym; nie trzeba przypisywać członków. Dodatkowo: ten kod jest nieprawidłowy. Tworzy __constructzmienną globalną , co jest dość złe. varnależy stosować w celu ograniczenia zakresu __construct.
Jaki jest więc sens własności „konstruktora”? Nie możesz dowiedzieć się, gdzie może być przydatne, jakieś pomysły?
Celem właściwości konstruktora jest zapewnienie sposobu udawania, że JavaScript ma klasy. Jedną z rzeczy, których nie możesz zrobić, jest zmiana konstruktora obiektu po jego utworzeniu. To skomplikowane.
Kilka lat temu napisałem dość obszerny artykuł: http://joost.zeekat.nl/constructors-consisted-mildly-confusing.html
Przykład tutaj: http://jsfiddle.net/FZ5nC/
Wypróbuj ten szablon:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Name = Name||{};
Name.Space = Name.Space||{};
//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Name.Space.ClassName = function Name_Space_ClassName(){}
//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Name.Space.ClassName.prototype = {
v1: null
,v2: null
,f1: function Name_Space_ClassName_f1(){}
}
//============================================================
// Static Variables
//------------------------------------------------------------
Name.Space.ClassName.staticVar = 0;
//============================================================
// Static Functions
//------------------------------------------------------------
Name.Space.ClassName.staticFunc = function Name_Space_ClassName_staticFunc(){
}
</script>
Musisz dostosować przestrzeń nazw, jeśli definiujesz klasę statyczną:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
Shape.Rectangle = Shape.Rectangle||{};
// In previous example, Rectangle was defined in the constructor.
</script>
Przykładowa klasa:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Shape.Rectangle = function Shape_Rectangle(width, height, color){
this.Width = width;
this.Height = height;
this.Color = color;
}
//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Shape.Rectangle.prototype = {
Width: null
,Height: null
,Color: null
,Draw: function Shape_Rectangle_Draw(canvasId, x, y){
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
context.fillStyle = this.Color;
context.fillRect(x, y, this.Width, this.Height);
}
}
//============================================================
// Static Variables
//------------------------------------------------------------
Shape.Rectangle.Sides = 4;
//============================================================
// Static Functions
//------------------------------------------------------------
Shape.Rectangle.CreateSmallBlue = function Shape_Rectangle_CreateSmallBlue(){
return new Shape.Rectangle(5,8,'#0000ff');
}
Shape.Rectangle.CreateBigRed = function Shape_Rectangle_CreateBigRed(){
return new Shape.Rectangle(50,25,'#ff0000');
}
</script>
Przykładowa instancja:
<canvas id="painting" width="500" height="500"></canvas>
<script>
alert("A rectangle has "+Shape.Rectangle.Sides+" sides.");
var r1 = new Shape.Rectangle(16, 12, "#aa22cc");
r1.Draw("painting",0, 20);
var r2 = Shape.Rectangle.CreateSmallBlue();
r2.Draw("painting", 0, 0);
Shape.Rectangle.CreateBigRed().Draw("painting", 10, 0);
</script>
Funkcje powiadomień są zdefiniowane jako AB = funkcja A_B (). Ma to na celu ułatwienie debugowania skryptu. Otwórz panel Inspect Element w Chrome, uruchom ten skrypt i rozwiń ślad debugowania:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Fail = Fail||{};
//============================================================
// Static Functions
//------------------------------------------------------------
Fail.Test = function Fail_Test(){
A.Func.That.Does.Not.Exist();
}
Fail.Test();
</script>
To jest konstruktor:
function MyClass() {}
Kiedy to zrobisz
var myObj = new MyClass();
MyClass jest wykonywany i zwracany jest nowy obiekt tej klasy.
alert(valuePassedInAsArgument);że będzie to działać raz dla każdej instancji, więc cała klasa jest konstruktorem.
new object is returned of that class- czy to nie bardziej, że zwracany jest nowy obiekt tej funkcji?
Uważam ten samouczek za bardzo przydatny. Takie podejście jest stosowane przez większość wtyczek jQuery.
var Class = function(methods) {
var klass = function() {
this.initialize.apply(this, arguments);
};
for (var property in methods) {
klass.prototype[property] = methods[property];
}
if (!klass.prototype.initialize) klass.prototype.initialize = function(){};
return klass;
};
Teraz ,
var Person = Class({
initialize: function(name, age) {
this.name = name;
this.age = age;
},
toString: function() {
return "My name is "+this.name+" and I am "+this.age+" years old.";
}
});
var alice = new Person('Alice', 26);
alert(alice.name); //displays "Alice"
alert(alice.age); //displays "26"
alert(alice.toString()); //displays "My name is Alice and I am 26 years old" in most browsers.
//IE 8 and below display the Object's toString() instead! "[Object object]"
klass
Ten wzór mi dobrze służył. Za pomocą tego wzorca tworzysz klasy w osobnych plikach, ładujesz je do swojej ogólnej aplikacji „w razie potrzeby”.
// Namespace
// (Creating new if not instantiated yet, otherwise, use existing and just add to it)
var myApp = myApp || {};
// "Package"
// Similar to how you would establish a package in other languages
(function() {
// "Class"
var MyClass = function(params) {
this.initialize(params);
}
// "Private Static" vars
// - Only accessible to functions in this class.
// - Doesn't get wiped out when we create a new instance.
var countInstances = 0;
var allInstances = [];
// "Private Static" functions
// - Same as above, but it's a function accessible
// only to other functions in this class.
function doSomething(){
}
// "Public Static" vars
// - Everyone has access.
// - Doesn't get wiped out when we create a new instance.
MyClass.counter = 0;
// "Public Static" functions
// - Same as above, but anyone can call this "static method".
// - Kinda like a singleton class situation.
MyClass.foobar = function(){
}
// Public properties and methods are built into the "prototype"
// - This is how each instance can become unique unto itself.
// - Establishing "p" as "local" (Static Private) variable
// simply so we don't have to keep typing "MyClass.prototype"
// for each property and function.
var p = MyClass.prototype;
// "Public" vars
p.id = null;
p.firstname = null;
p.lastname = null;
// "Private" vars
// - Only used by "this" instance.
// - There isn't "true" privacy for each
// instance so we have to fake it.
// - By tradition, we indicate "privacy"
// by prefixing it with an underscore.
// - So technically, anyone can access, but we simply
// don't tell anyone about it (e.g. in your API)
// so no one knows about it :)
p._foo = null;
p.initialize = function(params){
this.id = MyClass.counter++;
this.firstname = params.firstname;
this.lastname = params.lastname;
MyClass.counter++;
countInstances++;
allInstances.push(this);
}
p.doAlert = function(theMessage){
alert(this.firstname + " " + this.lastname + " said: " + theMessage + ". My id:" + this.id + ". Total People:" + countInstances + ". First Person:" + allInstances[0].firstname + " " + allInstances[0].lastname);
}
// Assign class to app
myApp.MyClass = MyClass;
// Close the "Package"
}());
// Usage example:
var bob = new myApp.MyClass({ firstname : "bob",
lastname : "er"
});
bob.doAlert("hello there");
varw konstruktorze (lub argumencie funkcji lub w funkcji podobnej do konstruktora).
Tak, możesz zdefiniować konstruktor w deklaracji klasy w następujący sposób:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Myślę, że opublikuję to, co robię z zamknięciem javascript, ponieważ nikt jeszcze nie używa zamknięcia.
var user = function(id) {
// private properties & methods goes here.
var someValue;
function doSomething(data) {
someValue = data;
};
// constructor goes here.
if (!id) return null;
// public properties & methods goes here.
return {
id: id,
method: function(params) {
doSomething(params);
}
};
};
Komentarze i sugestie dotyczące tego rozwiązania są mile widziane. :)
Korzystając z powyższej próbki Nicka, możesz utworzyć konstruktor dla obiektów bez parametrów, używając instrukcji return jako ostatniej instrukcji w definicji obiektu. Zwróć swoją funkcję konstruktora jak poniżej, a będzie ona uruchamiała kod w __construct za każdym razem, gdy tworzysz obiekt:
function Box()
{
var __construct = function() {
alert("Object Created.");
this.color = 'green';
}
this.color = '';
this.getColor = function() {
return this.color;
}
__construct();
}
var b = new Box();
this.getColor();linii powyżej, alert("Object Created.");nic nie zostanie ostrzeżone. Wystąpi błąd w rodzaju „getColor nie jest zdefiniowany”. Jeśli chcesz, aby konstrukcja mogła wywoływać inne metody w obiekcie, musisz zdefiniować ją po wszystkich innych metodach. Więc zamiast wywoływać __construct();ostatnią linię, po prostu zdefiniuj konstrukt tam i wstaw ()po nim, aby wymusić automatyczne wykonanie.
()na końcu definicji __construct nadal powodowało błąd. Musiałem wywołać __construct();własną linię, tak jak w oryginalnym kodzie, aby uniknąć błędu.
Może stało się to trochę prostsze, ale poniżej przedstawiłem to, co wymyśliłem w 2017 roku:
class obj {
constructor(in_shape, in_color){
this.shape = in_shape;
this.color = in_color;
}
getInfo(){
return this.shape + ' and ' + this.color;
}
setShape(in_shape){
this.shape = in_shape;
}
setColor(in_color){
this.color = in_color;
}
}
Korzystając z powyższej klasy, mam następujące elementy:
var newobj = new obj('square', 'blue');
//Here, we expect to see 'square and blue'
console.log(newobj.getInfo());
newobj.setColor('white');
newobj.setShape('sphere');
//Since we've set new color and shape, we expect the following: 'sphere and white'
console.log(newobj.getInfo());
Jak widać, konstruktor przyjmuje dwa parametry, a my ustawiamy właściwości obiektu. Zmieniamy również kolor i kształt obiektu za pomocą setterfunkcji i udowadniamy, że jego zmiana pozostała po wywołaniu getInfo()po tych zmianach.
Trochę za późno, ale mam nadzieję, że to pomoże. Przetestowałem to za pomocą mochatestów jednostkowych i działa dobrze.
Robią to, jeśli używasz Typescript - open source od MicroSoft :-)
class BankAccount {
balance: number;
constructor(initially: number) {
this.balance = initially;
}
deposit(credit: number) {
this.balance += credit;
return this.balance;
}
}
Maszynopis pozwala „fałszować” konstrukcje OO, które są kompilowane w konstrukcje javascript. Jeśli zaczynasz duży projekt, możesz zaoszczędzić dużo czasu i właśnie osiągnął wersję milestone 1.0.
http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf
Powyższy kod zostaje „skompilowany” w celu:
var BankAccount = (function () {
function BankAccount(initially) {
this.balance = initially;
}
BankAccount.prototype.deposit = function (credit) {
this.balance += credit;
return this.balance;
};
return BankAccount;
})();
W JavaScript typ wywołania określa zachowanie funkcji:
func()obj.func()new func()func.call()lubfunc.apply()Funkcja jest wywoływana jako konstruktor podczas wywoływania za pomocą newoperatora:
function Cat(name) {
this.name = name;
}
Cat.prototype.getName = function() {
return this.name;
}
var myCat = new Cat('Sweet'); // Cat function invoked as a constructor
Każda instancja lub obiekt prototypowy w JavaScript ma właściwość constructor, która odnosi się do funkcji konstruktora.
Cat.prototype.constructor === Cat // => true
myCat.constructor === Cat // => true
Sprawdź ten post na temat właściwości konstruktora.
Korzystając z doskonałego szablonu Blixta z góry, dowiedziałem się, że nie działa dobrze z dziedziczeniem wielopoziomowym (MyGrandChildClass rozszerzającym MyChildClass rozszerzającym MyClass) - cyklicznie przywoływanie konstruktora pierwszego rodzica w kółko. Oto proste obejście - jeśli potrzebujesz dziedziczenia wielopoziomowego, zamiast this.constructor.super.call(this, surName);korzystać chainSuper(this).call(this, surName);z funkcji łańcucha zdefiniowanej w ten sposób:
function chainSuper(cls) {
if (cls.__depth == undefined) cls.__depth = 1; else cls.__depth++;
var depth = cls.__depth;
var sup = cls.constructor.super;
while (depth > 1) {
if (sup.super != undefined) sup = sup.super;
depth--;
}
return sup;
}
http://www.jsoops.net/ jest całkiem dobry dla oop w Js. Jeśli zapewnia prywatną, chronioną, publiczną zmienną i funkcję, a także funkcję dziedziczenia. Przykładowy kod:
var ClassA = JsOops(function (pri, pro, pub)
{// pri = private, pro = protected, pub = public
pri.className = "I am A ";
this.init = function (var1)// constructor
{
pri.className += var1;
}
pub.getData = function ()
{
return "ClassA(Top=" + pro.getClassName() + ", This=" + pri.getClassName()
+ ", ID=" + pro.getClassId() + ")";
}
pri.getClassName = function () { return pri.className; }
pro.getClassName = function () { return pri.className; }
pro.getClassId = function () { return 1; }
});
var newA = new ClassA("Class");
//***Access public function
console.log(typeof (newA.getData));
// function
console.log(newA.getData());
// ClassA(Top=I am A Class, This=I am A Class, ID=1)
//***You can not access constructor, private and protected function
console.log(typeof (newA.init)); // undefined
console.log(typeof (newA.className)); // undefined
console.log(typeof (newA.pro)); // undefined
console.log(typeof (newA.getClassName)); // undefined
żeby zaoferować trochę różnorodności. ds.oop to dobry sposób na deklarowanie klas za pomocą konstruktorów w javascript. Obsługuje każdy możliwy typ dziedziczenia (w tym 1 typ, który nie obsługuje nawet c #), a także interfejsy, co jest miłe.
var Color = ds.make.class({
type: 'Color',
constructor: function (r,g,b) {
this.r = r; /* now r,g, and b are available to */
this.g = g; /* other methods in the Color class */
this.b = b;
}
});
var red = new Color(255,0,0); // using the new keyword to instantiate the class
Tutaj musimy zauważyć jeden punkt w skrypcie java, jest to język bezklasowy, jednak możemy to osiągnąć za pomocą funkcji w skrypcie java. Najczęstszym sposobem osiągnięcia tego celu jest utworzenie funkcji w skrypcie Java i użycie nowego słowa kluczowego do utworzenia obiektu oraz użycie tego słowa kluczowego do zdefiniowania właściwości i metod. Poniżej znajduje się przykład.
// Function constructor
var calculator=function(num1 ,num2){
this.name="This is function constructor";
this.mulFunc=function(){
return num1*num2
};
};
var objCal=new calculator(10,10);// This is a constructor in java script
alert(objCal.mulFunc());// method call
alert(objCal.name);// property call
//Constructors With Prototypes
var calculator=function(){
this.name="Constructors With Prototypes";
};
calculator.prototype.mulFunc=function(num1 ,num2){
return num1*num2;
};
var objCal=new calculator();// This is a constructor in java script
alert(objCal.mulFunc(10,10));// method call
alert(objCal.name); // property call
W większości przypadków musisz jakoś zadeklarować właściwość, której potrzebujesz, zanim będziesz mógł wywołać metodę przekazującą te informacje. Jeśli nie musisz początkowo ustawiać właściwości, możesz po prostu wywołać metodę w obiekcie w ten sposób. Prawdopodobnie nie jest to najpiękniejszy sposób na zrobienie tego, ale nadal działa.
var objectA = {
color: '';
callColor : function(){
console.log(this.color);
}
this.callColor();
}
var newObject = new objectA();