Pertanyaan Bagaimana cara mendapatkan warisan pseudo-klasik tepat pada deklarasi kelas?


catatan: 

Seperti yang diceritakan jawabannya, kode yang diusulkan dalam pertanyaan itu TIDAK benar-benar mencapai warisan (jika tidak, itu menjadi jawaban daripada pertanyaan ..) karena beberapa masalah yang dijelaskan dalam pertanyaan dan komentar saya. Ia bekerja seperti yang diharapkan palsu dari warisan (dan bahkan bukan prototipe).


  • Ringkasan

    Singkatnya, buatlah agar sama seperti kita menulis bahasa OO umum daripada javascript, tetapi jaga agar pewarisan itu benar.

  • Cerita 

    Object.create adalah cara yang baik untuk mencapai warisan prototypal, tetapi agak membingungkan untuk a diketik otak dan baru penggemar.

    Ada berbagai cara agar kita bisa menulis kode javascript lebih seperti kita menulis bahasa OO lainnya dengan pola pseudo-klasik. Seperti itu semu-kelas, kita harus berurusan dengan warisan prototipe yang mendasari javascript dengan benar.

    Apa yang ingin saya temukan adalah pendekatan bahwa warisan pseudo-klasik dapat dicapai tepat di deklarasi kelas. Kode untuk demonstrasi diletakkan di belakang pos, berfungsi seperti yang diharapkan, namun, ada beberapa hal yang mengganggu:

    1. Saya tidak bisa menyingkirkannya return dalam deklarasi kelas atau warisan tidak akan berfungsi.

    2. Saya tidak punya jalan kecuali lulus this dalam deklarasi kelas untuk membuat penutupan kembali tahu apa itu this.

    3. Saya juga ingin menyingkirkannya function (instance, _super) {, tetapi belum memiliki ide yang bagus.

    4. Itu statis(milik sendiri) dari kelas tidak diwarisi.

    Sebuah solusi akan lebih dari beberapa gula sintaksis daripada kerangka yang ada, pola yang baik berlaku.


Itu _extends fungsi:

function _extends(baseType) {
    return function (definition) {
        var caller=arguments.callee.caller;
        var instance=this;

        if(!(instance instanceof baseType)) {
            (caller.prototype=new baseType()).constructor=caller;
            instance=new caller();
        }

        var _super=function () {
            baseType.apply(instance, arguments);
        };

        definition(instance, _super);
        return instance;
    };
}

Itu Abc kelas:

function Abc(key, value) {
    return _extends(Object).call(this, function (instance, _super) {
        instance.What=function () {
            alert('What');
        };

        instance.getValue=function () {
            return 333+Number(value);
        };

        instance.Value=instance.getValue();
        instance.Key=key;
    });
}

Itu Xyz kelas:

function Xyz(key, value) {
    return _extends(Abc).call(this, function (instance, _super) {
        _super(key, value);

        instance.That=function () {
            alert('That');
        };
    });
}

Kode contoh:

var x=new Xyz('x', '123');
alert([x.Key, x.Value].join(': ')+'; isAbc: '+(x instanceof Abc));

var y=new Xyz('y', '456');
alert([y.Key, y.Value].join(': ')+'; isAbc: '+(y instanceof Abc));

var it=new Abc('it', '789');
alert([it.Key, it.Value].join(': ')+'; isAbc: '+(it instanceof Abc));
alert([it.Key, it.Value].join(': ')+'; isXyz: '+(it instanceof Xyz));

x.What();
y.That();

it.What();
it.That(); // will throw; it is not Xyz and does not have That method

11
2017-09-12 01:13


asal


Jawaban:


Tidak. Tidak. Ini tidak akan berhasil. Anda melakukan warisan di JavaScript semua salah. Kode Anda memberi saya migrain.

Membuat Pola Warisan Pseudo-Klasik di JavaScript

Jika Anda ingin sesuatu yang mirip dengan kelas dalam JavaScript maka ada banyak pustaka di luar sana yang menyediakannya untuk Anda. Misalnya menggunakan menambah Anda dapat merestrukturisasi kode Anda sebagai berikut:

var augment = require("augment");

var ABC = augment(Object, function () {
    this.constructor = function (key, value) {
        this.key = key;
        this.value = value;
    };

    this.what = function () {
        alert("what");
    };
});

var XYZ = augment(ABC, function (base) {
    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

Saya tidak tahu tentang Anda tetapi bagi saya ini sangat mirip dengan warisan klasik di C ++ atau Java. Jika ini memecahkan masalah Anda, bagus! Jika tidak maka lanjutkan membaca.

Prototipe-Kelas Isomorfisma

Dalam banyak hal prototipe mirip dengan kelas. Bahkan prototipe dan kelas sangat mirip sehingga kita dapat menggunakan prototipe untuk kelas model. Pertama mari kita lihat bagaimana warisan prototipe benar-benar bekerja:

Gambar di atas diambil dari jawaban berikut. Saya sarankan Anda membacanya dengan seksama. Diagram menunjukkan kepada kita:

  1. Setiap konstruktor memiliki properti yang disebut prototype yang menunjuk ke objek prototipe fungsi konstruktor.
  2. Setiap prototipe memiliki properti yang disebut constructoryang menunjuk ke fungsi konstruktor dari objek prototipe.
  3. Kami membuat sebuah instance dari fungsi konstruktor. Namun contoh sebenarnya mewarisi dari prototype, bukan konstruktor.

Ini adalah informasi yang sangat berguna. Secara tradisional kami selalu membuat fungsi konstruktor terlebih dahulu dan kemudian kami mengaturnya prototype properti. Namun informasi ini menunjukkan kepada kita bahwa kita dapat membuat objek prototipe terlebih dahulu dan kemudian menentukan constructor properti di atasnya.

Misalnya, secara tradisional kita dapat menulis:

function ABC(key, value) {
    this.key = key;
    this.value = value;
}

ABC.prototype.what = function() {
    alert("what");
};

Namun menggunakan pengetahuan baru kami dapat menulis hal yang sama seperti:

var ABC = CLASS({
    constructor: function (key, value) {
        this.key = key;
        this.value = value;
    },
    what: function () {
        alert("what");
    }
});

function CLASS(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

Seperti yang Anda lihat enkapsulasi mudah dicapai dalam JavaScript. Yang perlu Anda lakukan hanyalah berpikir menyamping. Namun warisan adalah masalah yang berbeda. Anda perlu melakukan sedikit lebih banyak pekerjaan untuk mencapai warisan.

Warisan dan Super

Lihatlah caranya augment  mencapai warisan:

function augment(body) {
    var base = typeof this === "function" ? this.prototype : this;
    var prototype = Object.create(base);
    body.apply(prototype, arrayFrom(arguments, 1).concat(base));
    if (!ownPropertyOf(prototype, "constructor")) return prototype;
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

Perhatikan bahwa tiga baris terakhir sama dengan baris terakhir CLASS dari bagian sebelumnya:

function CLASS(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

Ini memberitahu kita bahwa setelah kita memiliki objek prototipe yang perlu kita lakukan adalah mendapatkan properti konstruktor dan mengembalikannya.

Tiga baris pertama augment digunakan untuk:

  1. Dapatkan prototipe kelas dasar.
  2. Buat prototipe kelas turunan menggunakan Object.create.
  3. Mengisi prototipe kelas turunan dengan properti yang ditentukan.

Itu saja yang ada pada pewarisan dalam JavaScript. Jika Anda ingin membuat pola warisan klasik Anda sendiri maka Anda harus berpikir di sepanjang garis yang sama.

Merangkul Warisan Prototypal Sejati

Setiap programmer JavaScript yang menghargai garam mereka akan memberi tahu Anda itu warisan prototypal lebih baik dari warisan klasik. Namun demikian pemula yang berasal dari bahasa dengan warisan klasik selalu mencoba untuk menerapkan warisan klasik di atas warisan prototypal, dan mereka biasanya gagal.

Mereka gagal bukan karena itu tidak mungkin untuk menerapkan warisan klasik di atas warisan prototypal tetapi karena untuk menerapkan warisan klasik di atas warisan prototypal Anda harus terlebih dahulu memahami bagaimana karya pewarisan prototip sejati.

Namun begitu Anda memahami warisan prototipe sejati, Anda tidak akan pernah ingin kembali ke warisan klasik. saya juga mencoba untuk melaksanakan warisan klasik di atas warisan prototip sebagai seorang pemula. Sekarang saya mengerti bagaimana warisan prototip benar bekerja tetapi saya menulis kode seperti ini:

function extend(self, body) {
    var base = typeof self === "function" ? self.prototype : self;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

Di atas extend fungsi sangat mirip dengan augment. Namun alih-alih mengembalikan fungsi konstruktor, ia mengembalikan objek prototipe. Ini sebenarnya adalah trik yang sangat rapi yang memungkinkan properti statis diwarisi. Anda dapat membuat kelas menggunakan extend sebagai berikut:

var Abc = extend(Object, function () {
    this.constructor = function (key, value) {
        this.value = 333 + Number(value);
        this.key = key;
    };

    this.what = function () {
        alert("what");
    };
});

Warisannya sesederhana itu:

var Xyz = extend(Abc, function (base) {
    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

Ingat bagaimanapun itu extend tidak mengembalikan fungsi konstruktor. Ini mengembalikan objek prototipe. Ini berarti Anda tidak dapat menggunakan newkata kunci untuk membuat instance dari kelas. Sebagai gantinya Anda perlu menggunakan new sebagai metode, sebagai berikut:

var x = Xyz.new("x", "123");
var y = Xyz.new("y", "456");
var it = Abc.new("it", "789");

Ini sebenarnya hal yang bagus. Itu new kata kunci adalah dipertimbangkan  berbahaya dan saya sangat menyarankan Anda untuk berhenti menggunakannya. Misalnya saja tidak mungkin digunakan apply dengan new kata kunci. Namun itu mungkin untuk digunakan apply dengan new metode sebagai berikut:

var it = Abc.new.apply(null, ["it", "789"]);

Sejak Abc dan Xyz bukan fungsi konstruktor yang tidak bisa kita gunakan instanceof untuk menguji apakah suatu objek merupakan turunan dari Abc atau Xyz. Namun itu bukan masalah karena JavaScript memiliki metode yang disebut isPrototypeOf yang menguji apakah suatu objek adalah prototipe objek lain:

alert(x.key + ": " + x.value + "; isAbc: " + Abc.isPrototypeOf(x));
alert(y.key + ": " + y.value + "; isAbc: " + Abc.isPrototypeOf(y));

alert(it.key + ": " + it.value + "; isAbc: " + Abc.isPrototypeOf(it));
alert(it.key + ": " + it.value + "; isXyz: " + Xyz.isPrototypeOf(it));

Faktanya isPrototypeOf lebih kuat daripada instanceof karena memungkinkan kami menguji apakah satu kelas memanjang kelas lain:

alert(Abc.isPrototypeOf(Xyz)); // true

Selain perubahan kecil ini, semua hal lain bekerja seperti sebelumnya:

x.what();
y.that();

it.what();
it.that(); // will throw; it is not Xyz and does not have that method

Lihat demo untuk Anda sendiri: http://jsfiddle.net/Jee96/

Apa lagi yang ditawarkan warisan prototipe yang benar? Salah satu keuntungan terbesar dari warisan prototipe sejati adalah bahwa tidak ada perbedaan antara properti normal dan properti statis yang memungkinkan Anda untuk menulis kode seperti ini:

var Xyz = extend(Abc, function (base) {
    this.empty = this.new();

    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

Perhatikan bahwa kita dapat membuat instance kelas dari dalam kelas itu sendiri dengan menelepon this.new. Jika this.constructor belum ditentukan kemudian mengembalikan instance terinisialisasi baru. Jika tidak, ia mengembalikan instance diinisialisasi baru.

Selain itu karena Xyz adalah objek prototipe yang bisa kita akses Xyz.empty secara langsung (mis. empty adalah properti statis Xyz). Ini juga berarti bahwa properti statis secara otomatis diwariskan dan tidak berbeda dari properti normal.

Akhirnya, karena kelas dapat diakses dari dalam definisi kelas sebagai this, Anda dapat membuat kelas bertingkat yang mewarisi dari kelas yang bersarang di dalamnya dengan menggunakan extend sebagai berikut:

var ClassA = extend(Object, function () {
    var ClassB = extend(this, function () {
        // class definition
    });

    // rest of the class definition

    alert(this.isPrototypeOf(ClassB)); // true
});

Lihat demo untuk Anda sendiri: http://jsfiddle.net/Jee96/1/


22
2017-09-19 02:27



Ada tutorial lengkap tentang cara melakukan apa yang Anda cari.

oop-konsep

pola pseudo-klasik

semua-satu-konstruktor-pola


3
2017-09-21 03:50



Saya tahu ini tidak menjawab pertanyaan Anda karena sejauh yang saya tahu tidak ada cara yang baik untuk meletakkan segala sesuatu di konstruktor fungsi dan memilikinya menggunakan prototipe.

Seperti yang telah saya komentari; jika Anda memiliki masalah dengan sintaks JavaScript itu naskah bisa menjadi alternatif yang baik.

Berikut ini fungsi pembantu yang saya gunakan untuk warisan dan override (memanggil super) menggunakan JavaScript (tanpa Object.create)

var goog={};//inherits from closure library base
  //http://docs.closure-library.googlecode.com/git/closure_goog_base.js.source.html#line1466
  // with modifications for _super
goog.inherits = function(childCtor, parentCtor) {
  function tempCtor() {};
  tempCtor.prototype = parentCtor.prototype;
  childCtor.prototype = new tempCtor();
  childCtor.prototype.constructor = childCtor;
  // modified _super
  childCtor.prototype._super = parentCtor.prototype;
};

// Parent class dev
var Parent = function(){};
Parent.prototype.sayHi=function(){
  console.log("hi from Parent");
}
// end class
// Child class dev
var Child = function(){}
goog.inherits(Child,Parent);
Child.prototype.sayHi=function(){
  //_super only works on parent.prototype
  //this is where functions are usually defined
  //have to add this. to call _super as well
  this._super.sayHi();
  console.log("hi from Child");
}
// end Child

//code to test
var c = new Child();
c.sayHi();//hi from Parent and hi from Child

Bahkan jika Anda menemukan cara untuk menulis fungsi pembantu dan membuat fungsi konstruktor JS terlihat seperti kelas Java, Anda harus melakukannya mengerti prototipe.


2
2017-09-12 05:43



Anda selalu dapat mencoba jOOP, meskipun itu membutuhkan jQuery.

https://github.com/KodingSykosis/jOOP

var myClass = $.cls({
    main: function() {
        $('body').append('My App is loaded <br/>');
    }
});

var mySecondClass = $.cls({
    main: function() {
        this._super();
        $('body').append('My Second App is loaded <br/>');
    }
}, myClass);

var app = new mySecondClass();

http://jsfiddle.net/kodingsykosis/PrQWu/


-2
2017-09-20 21:45



Saya yakin Anda mencari lebih banyak fungsi daripada ini, tetapi jika Anda ingin mewarisi banyak metode dari kelas lain, Anda dapat melakukan ini

http://cdpn.io/Jqhpc

var Parent = function Parent () {
  this.fname = 'Bob';
  this.lname = 'Jones';
};
Parent.prototype.getFname = function getFname () {
  return this.fname;
};
Parent.prototype.getLname = function getLname () {
  return this.lname;
};

var Child = function Child () {
  this.fname = 'Jim';
};
Child.prototype = Parent.prototype;

var child = new Child();
document.write(child.getFname()); //=> Jim

-2
2017-10-15 17:39