JavaScript'te sınıf ve statik yöntem


262

Bunun işe yarayacağını biliyorum:

function Foo() {};
Foo.prototype.talk = function () {
    alert('hello~\n');
};

var a = new Foo;
a.talk(); // 'hello~\n'

Ama eğer aramak istersem

Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly

Foo.talkİş yapmak için bazı yöntemler buluyorum ,

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

Bunu yapmanın başka yolları var mı? Bunun doğru olup olmadığını bilmiyorum. JavaScript kodunuzda sınıf yöntemleri veya statik yöntemler kullanıyor musunuz?


14
Foo.talk = function ...
Erken Optimizasyon

1
@downvoterstepintothelight Foo.walk = function() {}Prototip zincirinde olmadığı için örneklerini etkilemez. Çapraz tarayıcı bir işlevin amacına [[prototype]]işaret etmek için bir yöntem var prototypemı?
lostyzd

3
sınıf yöntemleri tanımlarına göre örnekleri etkilemez çünkü muhtemelen ne istediğiniz hakkında hiçbir fikrim yok .
Erken Optimizasyon

@downvoterstepintothelight Metod , python gibi bir dilde, bir örnek kendi sınıf yöntemini çağırabildiğinden, farkın thisişaretçi olduğundan şüpheliyim .
lostyzd

Yanıtlar:


410

Öncelikle, JavaScript'in sınıf tabanlı bir dil yerine öncelikle bir prototip dili olduğunu unutmayın 1 . bir sınıf değil, bir işlev, bir nesne. Bir nesne örneğini gelen kullanarak bu fonksiyonu standart bir cepten dilde bir sınıfa benzer bir şey yaratmak sağlayacak anahtar kelime.Foonew

__proto__Çapraz tarayıcı desteği zayıf olduğu için çoğu zaman görmezden gelmeyi öneririm ve bunun yerine nasıl prototypeçalıştığını öğrenmeye odaklanırım .

İşlev 2'den oluşturulan bir nesnenin örneğine sahipseniz ve üyelerinden birine (yöntemler, öznitelikler, özellikler, sabitler vb.) Herhangi bir şekilde erişirseniz, erişim (a) işlevini bulana kadar prototip hiyerarşisinden aşağı akar veya (b) başka bir prototip bulamaz.

Hiyerarşi çağrılan nesnede başlar ve sonra prototip nesnesini arar. Prototip nesnesinin bir prototipi varsa, prototip yoksa tekrarlanır undefined.

Örneğin:

foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"

foo = {};
console.log(foo.bar); // logs undefined

function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set

Bana en azından bir şekilde bu "temel" parçaları daha önce anladığınız gibi geliyor, ama emin olmak için onları açık hale getirmem gerekiyor.

JavaScript'te her şey bir nesnedir 3 .

her şey bir nesnedir.

function Foo(){}sadece yeni bir fonksiyon tanımlamakla kalmaz, aynı zamanda erişilebilecek yeni bir fonksiyon nesnesi tanımlar Foo.

Bu yüzden Foo'in prototipine erişebilirsiniz Foo.prototype.

Ayrıca ayarlandığında yapabilirsiniz Ne fazla işlevleri üzerinde Foo:

Foo.talk = function () {
  alert('hello world!');
};

Bu yeni işleve aşağıdakiler kullanılarak erişilebilir:

Foo.talk();

Umarım şimdi bir işlev nesnesindeki işlevler ve statik bir yöntem arasında bir benzerlik fark edersiniz.

f = new Foo();Bir sınıf örneği oluşturmak, sınıf Foo.prototype.bar = function(){...}için paylaşılan bir yöntem tanımlamak ve sınıf Foo.baz = function(){...}için genel bir statik yöntem tanımlamak olarak düşünün .


ECMAScript 2015, okunması daha kolay olmakla birlikte, bunların uygulanmasını kolaylaştırmak için bu tür bildirimler için çeşitli sözdizimsel şeker tanıttı. Dolayısıyla önceki örnek şu şekilde yazılabilir:

class Foo {
  bar() {...}

  static baz() {...}
}

hangi barolarak adlandırılmasını sağlar :

const f = new Foo()
f.bar()

ve bazşu şekilde adlandırılmalıdır:

Foo.baz()

1: classECMAScript 5 belirtiminde bir "Gelecek Ayrılmış Word'dür " , ancak ES6 classanahtar kelimeyi kullanarak sınıfları tanımlama yeteneğini sunar .

2: temelde bir kurucu tarafından yaratılan bir sınıf örneği, ancak sizi yanıltmak istemediğim birçok nüanslı farklılık var

3: İlkel değerler şunlardır -ki undefined, null, boolean, sayı ve dizeleri-etmediler teknik olarak çünkü onlar konum alt düzey dil uygulamaları nesneleri. Booleanlar, sayılar ve karakter dizileri hala prototip zinciri ile nesneymiş gibi etkileşime girer, bu nedenle bu cevabın amaçları için, tam olarak olmasalar bile onları "nesneler" olarak değerlendirmek daha kolaydır.


1
@ lostyzd - buna üzerinden erişebilirler Foo.talk(). İsterseniz bunu yapıcıda atayabilirsiniz: this.talk = Foo.talk- veya not ettiğiniz gibi, atayarak Foo.prototype.talk = Foo.talk. Ancak bunun iyi bir fikir olduğundan emin değilim - prensip olarak, örnek yöntemleri örneğe özgü olmalıdır.
nrabinowitz

2
@ Doug Avery, Foo.talk()sadece isim boşluklu bir işlev çağırıyor. Java / C # gibi OOP dillerinde statik yöntemlerin nasıl çağrıldığına benzer durumlarda kullanırsınız. Bir kullanım senaryosuna iyi bir örnek gibi bir işlev olacaktır Array.isArray().
zzzzBov

7
PS null nesne türü null == 'object'
mvladk

1
Eksik olduğunuz temel nokta, statik yöntemlerin miras alınmasıdır. Foo.talk = function ()...orada kendi sınıf adı alt sınıflar için mevcut olmayacak. Bu, alt sınıfları "genişleterek" çözülebilir, ancak yine de daha zarif bir yol arıyorum.

1
@nus, yalnızca bazı diller statik yöntemlerin devralınmasına izin verir. Kalıtım isteniyorsa, başlamak için statik yöntemler kullanmamalısınız.
zzzzBov

67

Aşağıdaki gibi başarabilirsiniz:

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

Artık "talk" fonksiyonunu aşağıdaki gibi başlatabilirsiniz:

Foo.talk();

Bunu yapabilirsiniz çünkü JavaScript'te işlevler de nesnelerdir.


37

Bir örnekten statik yöntemi çağırma:

function Clazz() {};
Clazz.staticMethod = function() {
    alert('STATIC!!!');
};

Clazz.prototype.func = function() {
    this.constructor.staticMethod();
}

var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"

Basit Javascript Sınıfı Projesi: https://github.com/reduardo7/sjsClass


13
Bu statik bir çağrı değildir. var obj = yeni Clazz (); yeni bir Clazz örneği oluşturur . Ancak Clazz.staticMethod () sonucu diğer tüm öğeler olmadan elde eder.
mpemburn

5
@mpemburn: Eduardo da cevabında doğru. Size gösterdiği şey, sadece "dışarıdan" statik yöntemi çağırmakla Clazz.staticMethodkalmaz, aynı zamanda somutlaştırılmış bir nesneden bu statik yöntemlere nasıl bağlanacağınızı gösterir. Bu özellikle Node.js gibi ortamlarda kullanıldığında, gerektiğinde orijinal kurucuya doğrudan erişiminiz olmayabilir. Ekleyeceğim tek şeythis.constructor.staticMethod.apply(this, arguments);
Mauvis Ledford

1
Kesinlikle harika, hatta bir kahve senaryosu kurucu içinde çalışıyor: constructor: (a) -> @constructor.add @(neredeyse, zaten)
Orwellophile

31

Javascript'in statik / örnek değişkenleri ve yöntemleri ile nasıl çalıştığını göstermek için iyi bir örnek.

function Animal(name) {
    Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
    this.name = name; //instance variable, using "this"
}

Animal.showCount = function () {//static method
    alert(Animal.count)
}

Animal.prototype.showName=function(){//instance method
    alert(this.name);
}

var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");

Animal.showCount();  // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from  Java

İyi bir nokta: Bu, statik işleve erişememek garip olabilir this.
TrapII

Çözüm için teşekkürler ben bu durumda hangi thisanahtar kelime erişim olacak bakıyordu
santhosh

30

Ek olarak, şimdi classve ile yapmak mümkündürstatic

'use strict'

class Foo {
 static talk() {
     console.log('talk')
 };

 speak() {
     console.log('speak')
 };

};

verecek

var a = new Foo();
Foo.talk();  // 'talk'
a.talk();    // err 'is not a function'
a.speak();   // 'speak'
Foo.speak(); // err 'is not a function'

Örnekler bin kelimeye bedel olduğu için bu en iyi cevaptır. Ancak, neden a.talk()işe yaramadığını açıklamıyor . Kabul edilen cevap, prototip zincirinin onu bulması gerektiğini söylüyor, değil mi? Ama durum böyle değil
Pynchia

11

İsim alanlarını kullanıyorum:

var Foo = {
     element: document.getElementById("id-here"),

     Talk: function(message) {
            alert("talking..." + message);
     },

     ChangeElement: function() {
            this.element.style.color = "red";
     }
};

Ve kullanmak için:

Foo.Talk("Testing");

Veya

Foo.ChangeElement();

6

ES6 şimdi destekler classve staticbir cazibe gibi anahtar kelimeler:

class Foo {
    constructor() {}

    talk() {
        console.log("i am not static");
    }

    static saying() {
        console.log(this.speech);
    }

    static get speech() {
        return "i am static method";
    }

}

Ben böyle bir cevap arıyordum. Statik yöntem statik olmayan yöntemleri / değişkenleri çağırabilir mi?
Tomasz Mularczyk

1
@Tomasz statik yöntemleri bu sınıfın herhangi bir örneğine değil, sınıfın kendisine ayarlanmıştır. Yani tabii ki, statik bir yöntem olabilir ama nedense böyle'static durukyöntem () {yeni Foo () gibi bir örneğine erişim, sahip olması gerekir, bir örnek yöntemini çağırın konuşma ().; } ´
JHH

3

ES5'te statik yöntemler yazmak zorunda kalırsanız bunun için harika bir öğretici buldum:

//Constructor
var Person = function (name, age){
//private properties
var priv = {};

//Public properties
this.name = name;
this.age = age;

//Public methods
this.sayHi = function(){
    alert('hello');
}
}


// A static method; this method only 
// exists on the class and doesn't exist  
// on child objects
Person.sayName = function() {
   alert("I am a Person object ;)");  
};

bkz. https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/


2

Sadece ek notlar. ES6 sınıfını kullanarak, statik yöntemler oluşturduğumuzda .. Javacsript altyapısı, tanımlayıcı özniteliğini eski okulun "statik" yönteminden biraz farklı olarak ayarlar

function Car() {

}

Car.brand = function() {
  console.log('Honda');
}

console.log(
  Object.getOwnPropertyDescriptors(Car)
);

brand () için internal niteliğini (tanımlayıcı özelliği)

..
brand: [object Object] {
    configurable: true,
    enumerable: true,
    value: ..
    writable: true

}
..

nazaran

class Car2 {
   static brand() {
     console.log('Honda');
   }
}

console.log(
  Object.getOwnPropertyDescriptors(Car2)
);

brand () için dahili özelliği

..
brand: [object Object] {
    configurable: true,
    enumerable: false,
    value:..
    writable: true
  }

..

görüyoruz enumerable ayarlandığında YANLıŞ ES6 statik yöntem için.

nesneyi kontrol etmek için for-in döngüsünü kullanamayacağınız anlamına gelir

for (let prop in Car) {
  console.log(prop); // brand
}

for (let prop in Car2) {
  console.log(prop); // nothing here
}

ES6'daki statik yöntem, statik yöntemin hala yazılabilir olması ve dolayısıyla yazılabilir tanımlayıcının true olarak ayarlanması dışında, diğer sınıf özel özelliği (ad, uzunluk, yapıcı) gibi davranılır { writable: true }. aynı zamanda geçersiz kılabileceğimiz anlamına gelir

Car2.brand = function() {
   console.log('Toyota');
};

console.log(
  Car2.brand() // is now changed to toyota
);

1

Eğer aramaya çalıştığımda Foo.talk, JS bir işlev arama dener talkiçinden __proto__ve, tabii ki, o bulunamaz.

Foo.__proto__olduğunu Function.prototype.


1

Statik yöntem çağrıları doğrudan sınıfta yapılır ve sınıf örneklerinde çağrılamaz. Statik yöntemler genellikle yardımcı program işlevi oluşturmak için kullanılır

Oldukça net açıklama

Doğrudan mozilla.org'dan alınmıştır

Foo'nun sınıfınıza bağlı olması gerekir Yeni bir örnek oluşturduğunuzda myNewInstance.foo () 'u çağırabilirsiniz. Sınıfınızı içe aktarırsanız statik bir yöntem çağırabilirsiniz


0

Böyle bir durumla karşılaştığımda şöyle bir şey yaptım:

Logger = {
    info: function (message, tag) {
        var fullMessage = '';        
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.log(fullMessage);
        }
    },
    warning: function (message, tag) {
        var fullMessage = '';
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.warn(fullMessage);`enter code here`
        }
    },
    _getFormatedMessage: function () {}
};

şimdi bilgi yöntemini şu şekilde çağırabilirim Logger.info("my Msg", "Tag");


Bunu her zaman yapıyorum, ama temelde sadece isim boşluğu. Example vars ile örnekler oluşturmanıza izin vermez mi?
dcsan

0

Sizin durumunuzda Foo.talk():

function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
    alert('hello~\n');
};

Foo.talk(); // 'hello~\n'

Ancak, uygulamanın verimsiz bir yolu, prototypekullanımı daha iyidir.


Başka bir yol, Yolum statik sınıf olarak tanımlanır:

var Foo = new function() {
  this.talk = function () {
    alert('hello~\n');
    };
};

Foo.talk(); // 'hello~\n'

Statik sınıfın üstünde kullanılması gerekmez, prototypeçünkü sadece statik kullanım olarak bir kez inşa edilecektir.

https://github.com/yidas/js-design-patterns/tree/master/class


@jvitoroc Teşekkürler!
Nick Tsai

0

Javascript'in gerçek sınıfları yoktur, bunun yerine nesnelerin prototip zincirleri aracılığıyla diğer nesnelerden 'miras aldığı' prototip miras sistemi kullanır. Bu en iyi kodun kendisiyle açıklanır:

function Foo() {};
// creates a new function object

Foo.prototype.talk = function () {
    console.log('hello~\n');
};
// put a new function (object) on the prototype (object) of the Foo function object

var a = new Foo;
// When foo is created using the new keyword it automatically has a reference 
// to the prototype property of the Foo function

// We can show this with the following code
console.log(Object.getPrototypeOf(a) === Foo.prototype); 

a.talk(); // 'hello~\n'
// When the talk method is invoked it will first look on the object a for the talk method,
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)

// When you want to call
// Foo.talk();
// this will not work because you haven't put the talk() property on the Foo
// function object. Rather it is located on the prototype property of Foo.

// We could make it work like this:
Foo.sayhi = function () {
    console.log('hello there');
};

Foo.sayhi();
// This works now. However it will not be present on the prototype chain 
// of objects we create out of Foo

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.