Apa masalah pemrograman umum yang paling baik diselesaikan dengan menggunakan prototipe dan penutupan?

8

Sejauh saya memahami kedua konsep, saya tidak bisa melihat bagaimana saya bisa mengambil keuntungan dari penutupan JavaScript dan prototipe selain menggunakannya untuk membuat blok kelas-suka seperti / dan enkapsulasi (yang tampaknya lebih merupakan solusi daripada aset bagi saya )

Fitur JS lainnya seperti fungsi-nilai-atau evaluasi logis dari non-boolean jauh lebih mudah untuk jatuh cinta dengan ...

Apa masalah pemrograman umum yang paling baik diselesaikan dengan menggunakan pewarisan dan penutupan propotypal?

vemv
sumber
1
OO diselesaikan dengan pewarisan prototipikal. Dalam prototipe JavaScript adalah mekanisme OO Anda. Dan penutupan adalah apa yang Anda gunakan untuk mengikat negara ke suatu fungsi. Saya sarankan Anda mencoba node.js atau apa pun yang banyak menggunakan logika asinkron, Anda akan mudah jatuh cinta pada penutupan.
Raynos
Mungkin saya tidak membuat diri saya cukup jelas - saya sudah sadar untuk mencapai OO melalui prototipe, tetapi harus ada lebih banyak kegunaan fitur itu bukan? Jangan ragu untuk menaruh beberapa contoh pada logika asinkron.
vemv
1
Anda membuatnya terdengar seperti mencapai OO melalui prototipe adalah prestasi kecil, "pasti prototipe harus memiliki lebih banyak kegunaan daripada OO"
Raynos
2
@ vemv: Prototipe ada untuk memberi Anda OO. Titik. Yang lainnya hanyalah pelecehan.
Jan Hudec
2
@vemv mereka tidak eksotis, komunitas javascript hanya melakukan pekerjaan yang sangat buruk dalam mengajar prototipe. Baca tentang
protoypes

Jawaban:

5
  1. Penutupan inilah yang menjadikan fungsi sebagai nilai yang berguna. Ketika Anda melewati fungsi, Anda hampir pasti membutuhkannya untuk membawa beberapa konteks. Itulah tepatnya yang dilakukan penutupan.

  2. Prototipe hanyalah versi sederhana dari pewarisan kelas. Alih-alih memiliki instance dan kelas (diwakili oleh jenis instance khusus dalam bahasa dinamis), Anda hanya memiliki instance dan prototipe adalah kelas (dan prototipe itu adalah kelas dasar). Jadi mereka pada dasarnya memecahkan masalah yang sama, hanya prototipe yang lebih mudah untuk diimplementasikan (itu sebabnya JavaScript memilih mereka), agak sulit untuk digunakan (yah, hanya kekurangan gula sintaksis) dan untuk lebih baik atau lebih buruk lebih mudah untuk penyalahgunaan.

Jan Hudec
sumber
"ketika melewati fungsi Anda hampir pasti membutuhkan konteks" tidak juga, lihat C dan pointer fungsi mereka, mereka tidak memerlukan konteks. Umumnya jika Anda melewati fungsi di sekitar Anda memberikan fungsi-fungsi ini konteks yang mereka butuhkan. Penutupan pasti adalah cara yang bagus untuk melewati konteks, tetapi jauh dari satu-satunya
Raynos
"prototipe lebih mudah diimplementasikan (itu sebabnya JavaScript memilihnya)" Itu tidak jelas (dan sangat mungkin pernyataan salah) tanpa referensi apa pun.
Raynos
@ Raynos: di C kita sering melewatkan konteks ke fungsi melalui void* dataargumen yang akan diturunkan oleh fungsi yang dipanggil.
kevin cline
1
@ Raynos: Saya melihat pointer fungsi C dan mereka sangat sakit untuk digunakan kecuali kode yang membawa mereka melewati kekosongan * tambahan dengan konteks. Adapun prototipe, mereka jelas lebih mudah diimplementasikan, karena Anda hanya memiliki satu jenis objek non-primitif dan satu cara seragam untuk mengakses anggota (dan tidak terpisah misalnya dan anggota kelas dan tidak ada perilaku khusus meta-objek). Meskipun saya tidak memiliki referensi bahwa perancang ECMAScript benar-benar memilih prototipe karena alasan ini, penerjemah minimal adalah tujuan desain yang penting, jadi sangat mungkin.
Jan Hudec
@ JanHudec Saya pikir itu beberapa urutan besarnya lebih mungkin bahwa JavaScript memiliki prototipe karena bahasa OO prototipikal mempengaruhi brendan ketika ia menulis javascript.
Raynos
4

Penutupan tidak memecahkan masalah pemrograman yang tidak dapat diselesaikan tanpa mereka. Namun, ini dapat dikatakan untuk fitur bahasa apa pun yang tidak diperlukan untuk kelengkapan Turing, sehingga tidak banyak berarti.

Pikirkan tentang bagaimana Anda harus menulis ulang kode apa pun yang menggunakan penutupan, untuk tidak menggunakan penutupan. Anda mungkin akan memberikan fungsi dengan properti tambahan penutupan sehingga bisa menahan keadaan di antara panggilan. Yang hanya akan menyalin variabel yang sudah dalam lingkup ke properti dengan nama yang sama pada fungsi, jadi itu akan menjadi jenis latihan mengetik tanpa berpikir yang membuat Anda ingin berteriak di layar "mengapa kompiler bodoh ( penerjemah, terserahlah). Yang ini apa penutupan yang, compiler bodoh adalah cukup pintar untuk angka itu.

psr
sumber
Kemarin saya pertama kali menyadari bagaimana menggunakan penutupan dengan cara yang bermakna - mereka dapat membuat pemisahan masalah lebih ringan: satu komponen akan mengirim fungsi anonim ke yang lain, yang badan membuat referensi ke bidang yang komponen kedua tidak sadari. Kompiler berbasis terima kasih!
vemv
@vemv - Yang bisa Anda lakukan tanpa penutupan dengan membuat objek yang memegang referensi ke bidang yang tidak disadari oleh komponen kedua dan meneruskannya ke komponen kedua, tapi itu sangat menyakitkan dibandingkan dengan penutupan yang mungkin Anda putuskan tidak melakukannya.
psr
2

Penutupan bagus untuk logika asinkron.

Ini terutama tentang pengorganisasian kode untuk saya. Memiliki banyak fungsi lokal untuk membagi apa yang dikerjakan kode itu bagus.

create: function _create(post, cb) {
    // cache the object reference
    var that = this;

    function handleAll(err, data) {
        var rows = data.rows;

        var id = rows.reduce(function(memo, item) {
            var id = +item.id.split(":")[1];
            return id > memo ? id : memo;
        }, 0);
        id++;


        var obj = {
            title: post.title,
            content: post.content,
            id: id,
            // refer to the object through the closure
            _id: that.prefix + id,
            datetime: Date.now(),
            type: "post"
        }

        PostModel.insert(obj, handleInsert);
    }

    // this function doesn't use the closure at all.
    function handleInsert(err, post) {
        PostModel.get(post.id, handleGet);
    }

    // this function references cb and that from the closure
    function handleGet(err, post) {
        cb(null, that.make(post));
    }

    PostModel.all(handleAll);
}

Berikut ini contoh lain dari penutupan

var cachedRead = (function() {
    // bind cache variable to the readFile function
    var cache = {};

    function readFile(name, cb) {
        // reference cache
        var file = cache[name];
        if (file) {
            return cb(null, file);
        }

        fs.readFile(name, function(err, file) {
            if (file) cache[name] = file;
            cb.apply(this, arguments);
        });
    }

    return readFile;
})();

Dan contoh lainnya

create: function _create(uri, cb, sync) {
    // close over count
    var count = 3;

    // next only fires cb if called three times
    function next() {
        count--;
        // close over cb
        count === 0 && cb(null);
    }

    // close over cb and next
    function errorHandler(err, func) {
        err ? cb(err) : next();
    }

    // close over cb and next
    function swallowFileDoesNotExist(err, func) {
        if (err && err.message.indexOf("No such file") === -1) {
            return cb(err);
        }
        next();
    }

    this.createJavaScript(uri, swallowFileDoesNotExist, sync)

    this.createDocumentFragment(uri, errorHandler, sync);

    this.createCSS(uri, swallowFileDoesNotExist, sync);
},

Alternatif untuk menggunakan closure adalah mengubah variabel menjadi fungsi menggunakan f.bind(null, curriedVariable).

Namun secara umum, logika pemrograman asinkron menggunakan panggilan balik dan memanipulasi keadaan dalam panggilan balik bergantung pada currying atau penutupan. secara pribadi saya lebih suka penutupan.

Adapun penggunaan pewarisan prototypical, memungkinkan OO? Apakah warisan prototipe benar-benar perlu melakukan lebih dari itu agar dianggap "berguna". Ini alat warisan, memungkinkan warisan, itu cukup berguna.

Raynos
sumber