ES2015 (ES6) "class" sözdizimi ne gibi faydalar sağlar?


87

ES6 sınıfları hakkında birçok sorum var.

classSözdizimi kullanmanın faydası nedir ? Genel / özel / statik'in ES7'nin bir parçası olacağını okudum, bu bir sebep mi?

Dahası, classfarklı bir OOP türü mü yoksa hala JavaScript'in prototip kalıtımı mı? Kullanarak değiştirebilir miyim .prototype? Ya da sadece aynı nesne ama onu bildirmenin iki farklı yolu mu?

Hızın faydaları var mı? Büyük uygulama gibi büyük bir uygulamanız varsa bakımı / anlaşılması daha kolay olabilir mi?


Sadece kendi fikrim: Yeni sözdiziminin anlaşılması çok daha kolaydır ve çok önceden deneyim gerektirmez (özellikle çoğu insan bir OOP dilinden geleceği için). Ancak prototip nesne modeli bilmeye değer ve yeni sözdizimini kullanarak bunu asla öğrenemezsiniz, ayrıca zaten bilmediğiniz sürece prototip kodu üzerinde çalışmak daha zor olacaktır. Bu yüzden, bu seçeneğim olduğunda kendi kodumu eski sözdizimi ile yazmayı seçerdim
Zach Smith

Yanıtlar:


120

Yeni classsözdizimi için, bir anda , çoğunlukla sözdizimsel şeker. (Ama biliyorsunuz, iyi bir şeker türü.) ES2015-ES2020'de classyapıcı işlevlerle yapamayacağınızı ve Reflect.construct(alt sınıflandırma Errorve Array¹ dahil ) yapabileceğiniz hiçbir şey yoktur . (O ise büyük olasılıkla ile yapabilirsiniz ES2021 bazı şeyler olacaktır classaksi yapamaz: Özel alanlar , özel yöntemler ve statik alanlar / özel statik yöntemleri .)

Dahası, classfarklı bir OOP türü mü yoksa hala JavaScript'in prototip kalıtımı mı?

Sadece süpürge ve daha uygun sözdizimi ile, her zaman yaşadım aynı prototipi miras var ise sen (yapıcı işlevleri kullanarak gibi new Foovs.). (Özellikle ES5 ve öncesinde yapamadığınız Arrayveya türetme durumunda Error. Artık Reflect.construct[ spec , MDN ] ile yapabilirsiniz, ancak eski ES5 tarzı ile yapamazsınız.)

Kullanarak değiştirebilir miyim .prototype?

Evet, yine de değiştirebilirsiniz. prototype sınıfı oluşturduktan sonra, sınıfın yapıcısındaki nesneyi . Örneğin, bu tamamen yasaldır:

class Foo {
    constructor(name) {
        this.name = name;
    }
    
    test1() {
        console.log("test1: name = " + this.name);
    }
}
Foo.prototype.test2 = function() {
    console.log("test2: name = " + this.name);
};

Hızın faydaları var mı?

Bunun için belirli bir deyim sağlayarak, sanırım mümkün motoru daha iyi bir iş Optimizasyon konusunda yapmak mümkün olabilir. Ancak zaten optimize etmede çok başarılılar, önemli bir fark beklemiyorum.

ES2015'in (ES6) faydaları nelerdir? class sözdizimi sağlar?

Kısaca: İlk etapta yapıcı işlevleri kullanmıyorsanız, Object.create veya benzeri classşeyler sizin için yararlı olmaz.

Yapıcı işlevleri kullanırsanız, class :

  • Sözdizimi daha basittir ve daha az hataya açıktır.

  • Bu var çok daha kolay (ve yine daha az hata) eski ile daha yeni sözdizimi kullanılarak miras hiyerarşileri kurmak.

  • classsizi newyapıcı işleviyle kullanmamanın yaygın hatasına karşı korur (yapıcının bir istisna atmasını sağlayarakthis için geçerli bir nesne , ).

  • Bir yöntemin üst prototip sürümünü çağırmak, yeni sözdizimiyle eskisinden ( veya super.method()yerine ) çok daha basittir .ParentConstructor.prototype.method.call(this)Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)

İşte bir hiyerarşi için sözdizimi karşılaştırması:

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

class Manager extends Employee {
    constructor(first, last, position, department) {
        super(first, last, position);
        this.department = department;
    }

    personMethod() {
        const result = super.personMethod();
        // ...use `result` for something...
        return result;
    }

    managerMethod() {
        // ...
    }
}

Misal:

vs.

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    // ...
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.employeeMethod = function() {
    // ...
};

var Manager = function(first, last, position, department) {
    if (!(this instanceof Manager)) {
        throw new Error("Manager is a constructor function, use new with it");
    }
    Employee.call(this, first, last, position);
    this.department = department;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.personMethod = function() {
    var result = Employee.prototype.personMethod.call(this);
    // ...use `result` for something...
    return result;
};
Manager.prototype.managerMethod = function() {
    // ...
};

Canlı Örnek:

Gördüğünüz gibi, yanlış yapılması kolay ve yeniden yazması sıkıcı olan birçok tekrarlanan ve ayrıntılı şey (bu yüzden bunu yapmak için bir senaryo yazdım) , geri döndüm).


¹ "ES2015-ES2018'de, classyapıcı işlevlerle yapamayacağınızı ve Reflect.construct(alt sınıflandırma dahil) yapabileceğiniz hiçbir şey yoktur.Error veArray ) "

Misal:


9
Bunun adil bir karşılaştırma olup olmadığını bilmiyorum. Tüm zahmete girmeden benzer bir nesne elde edebilirsiniz. Klasik kalıtıma yaklaşmak yerine, prototip zincirleri üzerinden nesne bağlamanın JS yolunu kullanır. Bunu yapmak için sizinkinden yola çıkarak basit bir örnek göstererek bir özet yaptım .
Jason Cust

8
@JasonCust: Evet, bu adil bir karşılaştırma, çünkü ES6'nın yaptığını yapmanın ES5 yolunu gösteriyorum class. JavaScript ne yaptığını olan istemiyorsanız eğer kullanım yapıcı işlevlere sahip olmadığını esnek yeterlidir, ama bu farklı dan class- bütün mesele classJavaScript basitleştirmek sözde klasik miras olduğunu.
TJ Crowder

3
@JasonCust: Evet, farklı bir yol. Ben buna "daha yerli" demezdim (Kyle'ın yaptığını biliyorum ama bu Kyle). Yapıcı işlevleri JavaScript için temeldir, tüm yerleşik türlere bakın (yapıcı karşıtı hizip bir şekilde karışmayı başardı Symbol). Ama yine de harika olan şey, onları kullanmak istemiyorsan, zorunda değilsin. Çalışmamda, yapıcı işlevlerinin birçok yerde anlam ifade ettiğini ve newnetliği iyileştirmenin anlambilimini buluyorum ; ve Object.creatediğer birçok yerde anlamlıdır cephe durumları. Tamamlayıcıdırlar. :-)
TJ Crowder

4
meh. Buna olan ihtiyacı satın almıyorum. Oop'tan uzaklaşmak için c # 'dan uzaklaştım ve şimdi oop javascript üzerinde sürünüyor. Türleri sevmem. her şey bir var / nesne / işlev olmalıdır. bu kadar. artık gereksiz anahtar kelimeler yok. ve kalıtım bir aldatmacadır. türler ve tür olmayanlar arasında iyi bir kavga görmek istiyorsanız. sabun vs dinlenmeye bir göz atın. ve bunu kimin kazandığını hepimiz biliyoruz.
Shai UI

3
@foreyez: classsözdizimi, JavaScript'i eskisinden daha fazla yazılmış yapmaz. Cevapta söylediğim gibi, çoğunlukla zaten orada olanın (çok) daha basit sözdizimi. Buna kesinlikle bir ihtiyaç vardı, insanlar sürekli olarak eski sözdizimini yanlış anlıyorlardı. Ayrıca, mirası eskisinden daha fazla kullanmanız gerektiği anlamına da gelmez. (Ve elbette, eski sözdizimiyle asla "sınıflar" ayarlamazsanız, yeni sözdizimiyle de bunu yapmanıza gerek yoktur.)
TJ Crowder

22

ES6 sınıfları, bugün kullandığımız prototip sınıf sistemi için sözdizimsel şekerdir. Kodunuzu daha özlü ve kendi kendini belgeleyen hale getiriyorlar, bu da onları kullanmak için yeterli sebeptir (bence).

Bu ES6 sınıfını aktarmak için Babel'i kullanma:

class Foo {
  constructor(bar) {
    this._bar = bar;
  }

  getBar() {
    return this._bar;
  }
}

sana şöyle bir şey verecek:

var Foo = (function () {
  function Foo(bar) {    
    this._bar = bar;
  }

  Foo.prototype.getBar = function () {
    return this._bar;
  }

  return Foo;
})();

İkinci versiyon çok daha karmaşık değil, bakımı daha çok kod. Kalıtım söz konusu olduğunda, bu kalıplar daha da karmaşık hale gelir.

Sınıflar, kullandığımız prototip kalıplara göre derlendiği için, aynı prototip manipülasyonunu onlar üzerinde de yapabilirsiniz. Bu, çalışma zamanında yöntemler ve benzerleri eklemeyi, yöntemlere erişmeyi içerir.Foo.prototype.getBar vb. .

Erişilmesini istemediğiniz nesneleri dışa aktarmamaya dayalı olmasına rağmen, bugün ES6'da bazı temel gizlilik desteği vardır. Örneğin şunları yapabilirsiniz:

const BAR_NAME = 'bar';

export default class Foo {
  static get name() {
    return BAR_NAME;
  }
}

ve BAR_NAME diğer modüllerin doğrudan başvurması için kullanılamayacaktır.

Backbone gibi birçok kütüphane bunu desteklemeye veya çözmeye çalışmıştır. extends yardımcılarıyla yöntem benzeri işlevler ve özelliklerin doğrulanmamış bir özetini alan , ancak prototiple uğraşmayı içermeyen prototip kalıtımı ortaya çıkarmak için tutarlı bir sistem yok.

JS kodu daha karmaşık hale geldikçe ve kod tabanları büyüdükçe, kalıtım ve modüller gibi şeyleri işlemek için çok sayıda model geliştirmeye başladık. Modüller için özel bir kapsam oluşturmak için kullanılan IIFE'de birçok parantez ve parantez vardır; bunlardan birinin eksik olması, tamamen farklı bir şey yapan geçerli bir betikle sonuçlanabilir (bir modül bir sonraki modülü bir parametre olarak ona geçirdikten sonra noktalı virgülü atlamak, ki bu nadiren iyidir).

tl; dr: zaten yaptığımız şey için şekerdir ve amacınızı kodda netleştirir.

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.