Metode pribadi JavaScript

482

Untuk membuat kelas JavaScript dengan metode publik, saya akan melakukan sesuatu seperti:

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

Dengan cara itu pengguna kelas saya dapat:

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

Bagaimana cara membuat metode pribadi yang dapat dipanggil oleh buy_fooddan use_restroommetode tetapi tidak secara eksternal oleh pengguna kelas?

Dengan kata lain, saya ingin implementasi metode saya dapat dilakukan:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

Tetapi ini seharusnya tidak berhasil:

var r = new Restaurant();
r.private_stuff();

Bagaimana cara saya mendefinisikan private_stuffsebagai metode pribadi sehingga keduanya benar?

Saya telah membaca Lup menulis Doug Crockford beberapa kali tetapi tampaknya tidak seperti metode "pribadi" dapat dipanggil dengan metode publik dan metode "istimewa" dapat disebut secara eksternal.

Wayne Kao
sumber

Jawaban:

403

Anda dapat melakukannya, tetapi downside adalah bahwa ia tidak dapat menjadi bagian dari prototipe:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}
17 dari 26
sumber
9
Menyembunyikan menipis di dalam penutupan tidak akan menjamin privasi pada semua penerjemah. Lihat code.google.com/p/google-caja/wiki/…
Mike Samuel
51
@mikesamuel - benar, tetapi hanya ketika para penerjemah memiliki bug di dalamnya :)
jvenema
133
Ini adalah metode pribadi baik-baik saja, tetapi akan cenderung menggunakan lebih banyak memori daripada metode prototipe biasa, terutama jika Anda membuat banyak objek ini. Untuk setiap instance objek, ia menciptakan fungsi terpisah yang terikat pada objek dan bukan kelas. Juga, ini tidak mengumpulkan sampah sampai benda itu sendiri dihancurkan.
Arindam
4
Jika Anda membuat objek yang diwariskan oleh McDonalds () dari Restaurant (), McDonalds tidak dapat mengganti metode pribadi jika Anda mendeklarasikannya dengan cara ini. Sebenarnya Anda bisa, tetapi itu tidak akan berfungsi jika beberapa metode lain memanggil pribadi, itu akan memanggil versi asli dari metode dan Anda juga tidak bisa memanggil metode induk. Sejauh ini saya belum menemukan cara yang baik untuk mendeklarasikan metode pribadi yang bekerja dengan baik dengan warisan. Ini dan implikasi kinerja membuat ini bukan pola desain yang sangat baik sama sekali. Saya akan merekomendasikan melakukan semacam awalan untuk menunjukkan metode pribadi, seperti memulai dengan garis bawah.
Hoffmann
68
Metode pribadi tidak seharusnya diganti - itu pribadi.
17 dari 26
163

Menggunakan fungsi dan panggilan pemanggilan diri

JavaScript menggunakan prototipe dan tidak memiliki kelas (atau metode dalam hal ini) seperti bahasa yang Berorientasi Objek. Pengembang JavaScript perlu berpikir dalam JavaScript.

Kutipan Wikipedia:

Tidak seperti banyak bahasa berorientasi objek, tidak ada perbedaan antara definisi fungsi dan definisi metode. Sebaliknya, perbedaan terjadi selama pemanggilan fungsi; ketika suatu fungsi disebut sebagai metode suatu objek, fungsi lokal kata kunci ini terikat ke objek itu untuk doa itu.

Solusi menggunakan fungsi pemanggilan sendiri dan fungsi panggilan untuk memanggil "metode" pribadi:

var MyObject = (function () {

    // Constructor
    function MyObject (foo) {
        this._foo = foo;
    }

    function privateFun (prefix) {
        return prefix + this._foo;
    }

    MyObject.prototype.publicFun = function () {
        return privateFun.call(this, '>>');
    }

    return MyObject;
})();


var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined

Fungsi panggilan memungkinkan kita untuk memanggil fungsi pribadi dengan konteks yang sesuai ( this).


Lebih sederhana dengan Node.js

Jika Anda menggunakan node.js , Anda tidak perlu IIFE karena Anda dapat memanfaatkan sistem pemuatan modul :

function MyObject (foo) {
    this._foo = foo;
}

function privateFun (prefix) {
    return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
    return privateFun.call(this, '>>');
}

exports.MyObject = MyObject;

Muat file:

var MyObject = require('./MyObject').MyObject;

var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined


(Eksperimental) ES7 dengan Bind Operator

Operator mengikat ::adalah proposal ECMAScript dan diimplementasikan dalam Babel ( tahap 0 ).

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun('>>');
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}

Muat file:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function
Yves M.
sumber
34
Ini jawaban terbaik. Manfaat metode pribadi, tidak ada sampah.
TaylorMac
1
@TaylorMac Kecuali untuk .callbagian itu.
pishpish
1
@janje Huh? Itulah inti dari pertanyaan, tidak ada pribadi f()di javascript (tidak saat ini).
Yves M.
2
@YvesM. Inti dari pertanyaan adalah memilih pola terbaik yang mensimulasikannya.
pishpish
1
@ berubah apa kompromi terbaik untuk memiliki fungsi tersembunyi (tidak dapat dipanggil dari luar), sintax bagus (tidak .call) dan memori kecil (tidak ada fungsi pada contoh)? Apakah itu ada?
Ciprian Tomoiagă
161

Anda dapat mensimulasikan metode pribadi seperti ini:

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

Informasi lebih lanjut tentang teknik ini di sini: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html

georgebrock
sumber
7
Saya juga akan menyarankan situs Douglas Crockford sebagai sumber daya pada metode pribadi / publik dan anggota javascript.crockford.com/private.html
Jared
10
Dia menyebutkan tautan itu dalam pertanyaan.
Gulzar Nazim
8
Kelemahan dari metode ini adalah Anda tidak dapat memiliki private_stuff () mengakses data pribadi lainnya di Restoran dan metode Restoran lainnya tidak dapat memanggil private_stuff (). Keuntungannya adalah jika Anda tidak membutuhkan salah satu dari kondisi yang saya sebutkan tadi, Anda dapat tetap menggunakan use_restroom () dalam prototipe.
17 dari 26
6
Ini harus menjadi solusi dan jawaban yang diterima karena penulis jelas menggunakan properti prototipe.
Gabriel Llamas
23
Dengan pola yang diusulkan oleh @georgebrock, semua data pribadi akan dibagikan di antara semua objek restoran. Itu mirip dengan variabel pribadi statis dan fungsi di OOP berbasis kelas. Saya berasumsi bahwa OP tidak menginginkan ini. Untuk menggambarkan apa yang saya maksud, saya membuat jsFiddle .
feklee
35

Dalam situasi ini ketika Anda memiliki API publik, dan Anda ingin metode / properti pribadi dan publik, saya selalu menggunakan Pola Modul. Pola ini menjadi populer di perpustakaan YUI, dan detailnya dapat ditemukan di sini:

http://yuiblog.com/blog/2007/06/12/module-pattern/

Ini sangat mudah, dan mudah dipahami oleh pengembang lain. Sebagai contoh sederhana:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay

sumber
hal semacam ini tidak akan terdeteksi oleh autocomplete dari IDE :(
Klik Upvote
19
Tapi ini bukan kelas, jadi Anda tidak dapat memiliki 2 "contoh" ini dengan status yang berbeda.
DevAntoine
hapus bagian () dan Anda memiliki "kelas". setidaknya di mana Anda dapat instantiate isntances berbeda dengan negara yang berbeda. pola modul cukup intensif memori, meskipun ...
oligofren
@DevAntoine Lihatlah komentar untuk jawaban 17 dari 26. Dalam JavaScript, kelas yang dapat diperluas dan metode pribadi tidak mudah berjalan seiring. Saran saya dalam hal ini adalah untuk pergi dengan komposisi di atas warisan. Buat prototipe yang dapat diperluas dengan metode yang sama seperti objek beton yang terlampir. Kemudian internal ke prototipe Anda, Anda dapat memutuskan kapan harus memanggil metode pada objek konkret Anda.
Apakah ada kerugian untuk memanggil variabel publik dari fungsi-fungsi pribadi suka begitu: aPrivateMethod = function() { MYLIB.aPublicProperty}?
Hanna
21

Ini adalah kelas yang saya buat untuk memahami apa yang disarankan Douglas Crockford di situsnya Anggota Pribadi dalam JavaScript

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}
Sarath
sumber
5
Yang itu = ini adalah pola yang cukup umum, dipopulerkan oleh Crockford yang disebutkan sebelumnya dalam bukunya "Javascript: Bagian-bagian yang baik"
oligofren
8
thatdigunakan alih-alih thisuntuk menghindari masalah pelingkupan, ketika fungsi terikat ke objek yang berbeda. Di sini, Anda menyimpan thisdalam thatdan tidak pernah menggunakannya lagi yang sama dengan tidak melakukannya sama sekali. Anda harus berubah thisdengan thatsemua Constructorfungsi dalam (bukan deklarasi metode). Jika employeeini applyed atau called dalam beberapa cara metode ini mungkin melemparkan karena thisakan benar terikat.
Maroshii
Juga, setiap instance akan memiliki salinan lengkap dari fungsi pribadi, tidak efisien. Selain fakta bahwa metode publik tidak dapat mengakses vars kelas privat, membuat saya ingin beralih ke panah. Sayangnya angulardart adalah super beta.
Ray Foss
Di mana metode "konstruktor" dalam hal ini? Di mana saya akan meletakkan logika yang biasanya dieksekusi dalam metode konstruktor suatu kelas ketika itu dipakai?
BadHorsie
13

Saya menyulap ini: EDIT: Sebenarnya, seseorang telah menautkan ke solusi yang identik. Duh!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'

sumber
1
Ini adalah teknik yang bagus, tetapi bagaimana Anda mengizinkan parameter dalam konstruktor Anda? Misalnya di var getAwayVehicle = new Car(100);mana 100kecepatan dan Anda ingin mengingatkan kecepatan. Terima kasih!
Jason
1
Mencari tahu, dapat memiliki var Car = function(speed) { this.speed = speed; }dan `kembali {constructor: Car, ...`
Jason
11

Saya pikir pertanyaan seperti itu muncul lagi dan lagi karena kurangnya pemahaman tentang penutupan. Сlosures adalah hal terpenting dalam JS. Setiap programmer JS harus merasakan esensi dari itu.

1. Pertama-tama kita perlu membuat ruang lingkup yang terpisah (penutupan).

function () {

}

2. Di bidang ini, kita dapat melakukan apa pun yang kita inginkan. Dan tidak ada yang akan tahu tentang itu.

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3. Agar dunia tahu tentang kelas restoran kami, kami harus mengembalikannya dari penutupan.

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4. Pada akhirnya, kami memiliki:

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5. Juga, pendekatan ini memiliki potensi untuk pewarisan dan templating

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

Saya harap ini membantu seseorang lebih memahami masalah ini

imos
sumber
2
Apa yang Anda miliki dalam poin 4. luar biasa! Saya pikir itu satu-satunya jawaban dari semua yang ada di sini di mana Anda mendapatkan baik kinerja / memori keuntungan menggunakan metode pada prototipe DAN metode publik ini memiliki akses penuh ke anggota pribadi. +1
Hudon
7
Itu tidak bekerja. Variabel nama di sini bertindak seperti variabel statis dan dibagikan oleh semua instance Restaurant. Inilah jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shital Shah
ini merupakan percobaan yang bagus, tetapi seperti yang Shital tunjukkan, variabel nama adalah buggy.
oligofren
2
menambahkan 2c saya di sini untuk menegaskan kembali ini tidak bekerja, terlihat bagus, tetapi seperti yang disebutkan di atas "nama" akan berfungsi sebagai variabel statis yaitu dibagikan di semua contoh
Paul Carroll
10

Secara pribadi, saya lebih suka pola berikut untuk membuat kelas di JavaScript:

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

Seperti yang Anda lihat, ini memungkinkan Anda untuk mendefinisikan properti kelas dan properti instance, yang masing-masing dapat bersifat publik dan pribadi.


Demo

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<ul>' 
               + '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
               + '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
               + '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
               + '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
               + '</ul>';
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + '<h3>Overview</h3>'
              + '<ul>' 
              + '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
              + '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
              + '</ul>'
              + '<hr />';
        }
    };
}();

var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");

Restaurant.printStatus();

Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();

Restaurant.printStatus();

BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();

Restaurant.printStatus();

Lihat juga Fiddle ini .

John Slegers
sumber
Ini membuat saya ingin menggunakan kelas es6 dan melihat apa itu transpiles juga
sheriffderek
9

Semua penutupan ini akan dikenakan biaya. Pastikan Anda menguji implikasi kecepatan terutama di IE. Anda akan merasa lebih baik dengan konvensi penamaan. Masih ada banyak pengguna web perusahaan di luar sana yang terpaksa menggunakan IE6 ...

Kelly
sumber
34
Siapa yang peduli, serius?
nowayyy
17
Itu 9% yang masih menggunakan IE6 tidak peduli tentang kecepatan, optimasi dan semua fitur HTML5 modern. Jadi penutupan tidak akan dikenakan biaya apa pun.
Gabriel Llamas
6
Sekarang 0,5% (Agustus 2012) w3schools.com/browsers/browsers_explorer.asp
Lorenzo Polidori
7
@LorenzoPolidori w3schools pengguna! == pengguna web perusahaan;]
WynandB
Konvensi penamaan (mis: prepending sebuah garis bawah) adalah cara yang harus dilakukan. Kode lebih mudah dipelihara dan metode masih didefinisikan pada prototipe. Namun saat ini ... Saya baru saja menandai metode sebagai pribadi dalam naskah.
David Sherret
5

Jangan terlalu bertele-tele. Ini Javascript. Gunakan Konvensi Penamaan .

Setelah bertahun-tahun bekerja di kelas ES6, saya baru-baru ini mulai bekerja pada proyek ES5 (menggunakan requireJS yang sudah sangat verbose). Saya telah berulang-ulang semua strategi yang disebutkan di sini dan semuanya pada dasarnya bermuara pada penggunaan konvensi penamaan :

  1. Javascript tidak memiliki kata kunci lingkup seperti private. Pengembang lain yang memasukkan Javascript akan mengetahui hal ini di muka. Oleh karena itu, konvensi penamaan sederhana lebih dari cukup. Konvensi penamaan awalan sederhana dengan garis bawah memecahkan masalah properti pribadi dan metode pribadi.
  2. Mari kita manfaatkan Prototipe untuk alasan kecepatan, tetapi jangan sampai lebih dari itu. Mari kita coba untuk menjaga es5 "class" melihat sedekat apa yang kita harapkan dalam bahasa backend lainnya (dan memperlakukan setiap file sebagai kelas, bahkan jika kita tidak perlu mengembalikan sebuah instance).
  3. Mari kita tunjukkan dengan situasi modul yang lebih realistis (kita akan menggunakan es5 lama dan memerlukanJs lama).

my-tooltip.js

    define([
        'tooltip'
    ],
    function(
        tooltip
    ){

        function MyTooltip() {
            // Later, if needed, we can remove the underscore on some
            // of these (make public) and allow clients of our class
            // to set them.
            this._selector = "#my-tooltip"
            this._template = 'Hello from inside my tooltip!';
            this._initTooltip();
        }

        MyTooltip.prototype = {
            constructor: MyTooltip,

            _initTooltip: function () {
                new tooltip.tooltip(this._selector, {
                    content: this._template,
                    closeOnClick: true,
                    closeButton: true
                });
            }
        }

        return {
            init: function init() {
               new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
            }

            // You could instead return a new instantiation, 
            // if later you do more with this class.
            /* 
            create: function create() {
               return new MyTooltip();
            }
            */
        }
    });
prograhammer
sumber
2
Perlu dicatat bahwa bahasa Javascript maupun host browser tipikal tidak mendefinisikan objek yang bergantung pada konvensi penamaan untuk "menyembunyikan" keadaan pribadi, jadi walaupun Anda mungkin benar bahwa pengembang akan memahami konsepnya, itu masih mengarah pada konsep yang sangat tidak Pendekatan OO untuk pemrograman OO.
Pengingat kaya
Bolehkah saya meminta referensi yang baik dalam membuat seperti itu? Ada bagian pada contoh ini yang baru bagi saya. The define, dan constructordan struktur itu sendiri. Sementara saya setuju sebagian besar pada jawaban, saya sudah mulai bekerja dengan JS dengan banyak pengaruh OOP dan bahkan melompat terlalu dini ke TS karena saya memiliki pengalaman sebelumnya ke C #. Saya pikir saya harus menghapus hal-hal ini dan harus memahami paradigma prototyping / prosedural. (Terpilih, btw)
Cold Cerberus
1
@ColdCerberus cuplikan kode ini menggunakan es5. Anda dapat melihat gambaran lengkap dari pendekatan ini di sini: gist.github.com/jonnyreeves/2474026 . Namun perlu diingat, Anda ingin mengambil pendekatan ini dan memperbaruinya dengan menggunakan kelas es6: googlechrome.github.io/samples/classes-es6 dan modul es6 (sintaks impor / ekspor): hackernoon.com/...
prograhammer
5

Anda dapat melakukannya sekarang dengan metode pribadi ES10 . Anda hanya perlu menambahkan #sebelum nama metode.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}
D-Marc
sumber
2
Kecuali ini tahap 3 dan belum secara resmi bagian dari bahasa.
misterhtmlcss
3

Mengambil salah satu solusi yang mengikuti Crockford pribadi atau priviledged pola. Sebagai contoh:

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

Dalam setiap kasus di mana penyerang tidak memiliki hak "mengeksekusi" pada konteks JS ia tidak memiliki cara untuk mengakses bidang atau metode "publik" atau "pribadi". Seandainya penyerang memiliki akses tersebut, ia dapat menjalankan one-liner ini:

eval("Foo = " + Foo.toString().replace(
    /{/, "{ this.eval = function(code) { return eval(code); }; "
));

Perhatikan bahwa kode di atas adalah generik untuk semua privasi tipe konstruktor. Ini akan gagal dengan beberapa solusi di sini tetapi harus jelas bahwa hampir semua solusi berbasis penutupan dapat dipecah seperti ini dengan replace()parameter yang berbeda .

Setelah ini dieksekusi objek apa pun yang dibuat dengan new Foo()akan memiliki evalmetode yang dapat dipanggil untuk mengembalikan atau mengubah nilai atau metode yang didefinisikan dalam penutupan konstruktor, misalnya:

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

Satu-satunya masalah yang saya dapat lihat dengan ini bahwa itu tidak akan berfungsi untuk kasus-kasus di mana hanya ada satu contoh dan itu dibuat saat dimuat. Tetapi kemudian tidak ada alasan untuk benar-benar mendefinisikan prototipe dan dalam hal ini penyerang dapat dengan mudah menciptakan kembali objek alih-alih konstruktor selama ia memiliki cara untuk melewatkan parameter yang sama (misalnya mereka konstan atau dihitung dari nilai yang tersedia).

Menurut pendapat saya, ini cukup banyak membuat solusi Crockford tidak berguna.Karena "privasi" dengan mudah merusak kelemahan dari solusinya (mengurangi keterbacaan & pemeliharaan, penurunan kinerja, peningkatan memori) menjadikan metode berbasis prototipe "tidak privasi" menjadi pilihan yang lebih baik.

Saya biasanya menggunakan garis bawah terkemuka untuk menandai __privatedan _protectedmetode dan bidang (gaya Perl), tetapi gagasan memiliki privasi dalam JavaScript hanya menunjukkan bagaimana itu adalah bahasa yang disalahpahami.

Karena itu saya tidak setuju dengan Crockford kecuali untuk kalimat pertamanya.

Jadi, bagaimana Anda mendapatkan privasi nyata di JS? Masukkan semua yang diperlukan untuk menjadi pribadi di sisi server dan gunakan JS untuk melakukan panggilan AJAX.

Fozi
sumber
Ini adalah masalah serius yang harus lebih dikenal. Apakah ada 'pertahanan' terhadap serangan ini?
James
@ James Tidak ada yang saya ketahui, saya pikir itu adalah sifat binatang itu. Seperti yang saya tunjukkan, Anda dapat memindahkan fungsionalitas ke server di mana ia berjalan di lingkungan yang dilindungi. Poin yang saya ingin sampaikan dalam jawaban saya adalah bahwa solusi Crockford tidak membantu, tidak perlu mempersulit kode dan menyembunyikan perlunya melakukan sesuatu tentang hal itu.
Fozi
Jika pengguna memasukkan kata sandi rahasia, ia tidak dapat melakukan sisi server ini. Pada titik tertentu kata sandi akan berada dalam var 'pribadi'. Jadi bisakah penyerang membacanya? Saya mempercayai kode saya, dan toh standar rumah saya tidak mengizinkan eval (). Penyerang bisa berupa plug-in atau perpustakaan JavaScript pihak ketiga yang berbahaya yang tidak saya periksa dengan benar - jadi, ya saya perlu memeriksanya. Penyerang juga mungkin seperti iklan di samping yang tidak boleh berinteraksi dengan kode saya. Saya melindungi dari itu dengan membungkus semua kode saya dalam anonim (function () {allMyStuff}());sehingga tidak mengungkapkan global.
James
@ James Ini mendapatkan PL, jika Anda ingin melanjutkan ini, silakan buka pertanyaan baru. Ya, seorang penyerang dapat membaca kata sandi. Dari variabel "pribadi" Anda. Atau dari DOM. Atau dia bisa mengganti API AJAX. Atau dia mengganti halaman Anda dengan sesuatu yang lain. Jika dia tidak dapat melakukan hal-hal di atas, maka tidak perlu untuk JS "privasi" karena dia tidak dapat membaca variabel JS Anda juga. Intinya adalah bahwa "solusi" Crockford yang digunakan semua orang saat ini tidak membantu masalah ini.
Fozi
Saya percaya kebingungan kode pseudorandom mungkin merupakan pertahanan yang lemah terhadap serangan ini - lebih sulit untuk memodifikasi fungsi tubuh ketika Anda tidak dapat bergantung pada fungsi yang memiliki nama tetap; lebih sulit dilakukan f.eval('nameOfVariable')ketika Anda tidak tahu apa 'nameOfVariable'itu ...
Gershom
2

Jika Anda ingin berbagai fungsi publik dan pribadi dengan kemampuan fungsi publik untuk mengakses fungsi pribadi, tata letak kode untuk objek seperti ini:

function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as 
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}
domgblackwell
sumber
Dapatkah seseorang memberi tahu saya mengapa ini ditolak? Terlihat bagus untukku.
thomasrutter
10
Setiap kali Anda melakukan new MyObject, prototipe MyObjectdiganti dengan nilai yang sama.
bpierre
2
-1. Jangan pernah menugaskan bagian .prototypedalam konstruktor.
Bergi
2
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert( "privateMethod, " + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert( "publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod || "no privateMethod" );

Mirip dengan georgebrock tetapi sedikit kurang verbose (IMHO) Ada masalah dengan melakukannya dengan cara ini? (Saya belum melihatnya di mana pun)

sunting: Saya menyadari ini agak tidak berguna karena setiap lembaga independen memiliki salinan sendiri dari metode publik, sehingga merusak penggunaan prototipe.

David
sumber
2

Inilah yang saya nikmati sejauh ini mengenai metode / anggota pribadi / publik dan instantiasi dalam javascript:

di sini adalah artikelnya: http://www.sefol.com/?p=1090

dan inilah contohnya:

var Person = (function () {

    //Immediately returns an anonymous function which builds our modules 
    return function (name, location) {

        alert("createPerson called with " + name);

        var localPrivateVar = name;

        var localPublicVar = "A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called ")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");

//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");

//Prints "ben"
x.publicFunction();

//Prints "candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints "candide"
y.publicFunction();

//Should have changed this : prints "Ben 2"
x.publicFunction();

JSFiddle: http://jsfiddle.net/northkildonan/kopj3dt3/1/

low_rents
sumber
pendekatan ini memiliki satu masalah penting - jika Anda telah membuat 2 objek, dalam memori akan ada 2 metode yang sama (Fungsi Publik misalnya) 1000 objek akan memakan semua memori Anda.
Artem G
2

Pola modul benar dalam banyak kasus. Tetapi jika Anda memiliki ribuan instance, kelas menghemat memori. Jika menyimpan memori menjadi perhatian dan objek Anda berisi sejumlah kecil data pribadi, tetapi memiliki banyak fungsi publik, maka Anda ingin semua fungsi publik untuk tinggal di .protype untuk menghemat memori.

Inilah yang saya pikirkan:

var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

Objek tersebut privberisi properti pribadi. Ini dapat diakses melalui fungsi publik getPriv(), tetapi fungsi ini kembali falsekecuali Anda melewatinya secret, dan ini hanya diketahui di dalam penutupan utama.

James
sumber
Itu mensimulasikan anggota yang dilindungi, jenis yang mewarisi darinya dapat mengakses anggota yang dilindungi juga. Saya mendukung pola ini daripada yang pribadi juga
HMR
2

Bagaimana dengan ini?

var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

Pencarian variabel pribadi tidak dimungkinkan di luar ruang lingkup fungsi langsung. Tidak ada duplikasi fungsi, menghemat memori.

The downside adalah bahwa pencarian variabel pribadi kikuk privateVars[this.id].cookedadalah konyol untuk mengetik. Ada juga variabel "id" tambahan.

Evan Leis
sumber
Ini akan meninggalkan Restaurantsebagai undefinedkarena Anda tidak kembali apa-apa dari fungsi anonim.
user4815162342
Dimana dan bagaimana Dengan asumsi referensi ke restoran yang dibuat hilang, privateVars tidak akan memiliki referensi kepada pemiliknya. Grafik referensi adalah asiklik. Apa yang saya lewatkan?
Evan Leis
Sebenarnya ini adalah satu-satunya jawaban yang mendukung properti pribadi selain metode. Hanya dua masalah yang sudah dicatat dalam jawabannya.
pishpish
Saya melihat kebocoran memori: ketika sebuah instance Restauranttelah dikumpulkan, nilai-nilainya tetap di dalam privateVars. A WeakMapmungkin merupakan pengganti yang baik untuk Arraykasus ini.
Gershom
2

Bungkus semua kode dalam Fungsi Anonim: Kemudian, semua fungsi akan menjadi pribadi, HANYA fungsi yang dilampirkan ke windowobjek:

(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;   
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

Gunakan ini :

  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

BIOLA

Abdennour TOUMI
sumber
1

Saya lebih suka menyimpan data pribadi dalam suatu yang terkait WeakMap. Ini memungkinkan Anda untuk menjaga metode publik Anda pada prototipe di mana mereka berada. Ini tampaknya menjadi cara paling efisien untuk menangani masalah ini untuk sejumlah besar objek.

const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};
kaya remer
sumber
0

Fungsi pribadi tidak dapat mengakses variabel publik menggunakan pola modul

Dooma
sumber
0

Karena semua orang memposting di sini kodenya sendiri, saya akan melakukannya juga ...

Saya suka Crockford karena dia memperkenalkan pola berorientasi objek nyata dalam Javascript. Tapi dia juga datang dengan kesalahpahaman baru, yang "itu".

Jadi mengapa dia menggunakan "that = this"? Ini tidak ada hubungannya dengan fungsi pribadi sama sekali. Itu ada hubungannya dengan fungsi batin!

Karena menurut Crockford ini adalah kode kereta:

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

Jadi dia menyarankan untuk melakukan ini:

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

Jadi seperti yang saya katakan, saya cukup yakin bahwa Crockford salah penjelasan tentang itu dan ini (tapi kodenya jelas benar). Atau apakah dia hanya membodohi dunia Javascript, untuk mengetahui siapa yang menyalin kode-nya? Saya tidak tahu ... Saya bukan pecandu peramban; D

EDIT

Ah, hanya itu masalahnya: Apa artinya 'var that = this;' artinya dalam JavaScript?

Jadi Crockie benar-benar salah dengan penjelasannya .... tetapi benar dengan kodenya, jadi dia masih pria yang baik. :))

menandai
sumber
0

Secara umum saya menambahkan Obyek pribadi _ sementara ke objek. Anda harus membuka privasi secara eksplisit di "Power-constructor" untuk metode ini. Jika Anda memanggil metode dari prototipe, Anda akan dapat menimpa metode prototipe

  • Jadikan metode publik dapat diakses di "Power-constructor": (ctx adalah konteks objek)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • Sekarang saya punya openPrivacy ini:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };
Andreas Dyballa
sumber
0

Inilah yang saya kerjakan:

Membutuhkan satu kelas kode gula yang dapat Anda temukan di sini . Juga mendukung hal-hal yang dilindungi, pewarisan, virtual, statis ...

;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace[ "Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace, "Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log( "There are", this.toilets, "toilets available") }

    function buy_food     (){ return "food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

sumber
0
Class({  
    Namespace:ABC,  
    Name:"ClassL2",  
    Bases:[ABC.ClassTop],  
    Private:{  
        m_var:2  
    },  
    Protected:{  
        proval:2,  
        fight:Property(function(){  
            this.m_var--;  
            console.log("ClassL2::fight (m_var)" +this.m_var);  
        },[Property.Type.Virtual])  
    },  
    Public:{  
        Fight:function(){  
            console.log("ClassL2::Fight (m_var)"+this.m_var);  
            this.fight();  
        }  
    }  
});  

https://github.com/nooning/JSClass

snowkid
sumber
0

Saya telah membuat alat baru untuk memungkinkan Anda memiliki metode pribadi sejati pada prototipe https://github.com/TremayneChrist/ProtectJS

Contoh:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail
Trem
sumber
1
Bisakah Anda menjelaskan cara kerjanya? Bagaimana / di mana Anda dapat memanggil _privatemetode ini?
Bergi
0

Anda harus menutup fungsi konstruktor yang sebenarnya, di mana Anda dapat menentukan metode pribadi Anda. Untuk mengubah data instance melalui metode pribadi ini, Anda harus memberi mereka "ini" dengan mereka, baik sebagai argumen fungsi atau dengan memanggil fungsi ini dengan .apply (ini):

var Restaurant = (function(){
    var private_buy_food = function(that){
        that.data.soldFood = true;
    }
    var private_take_a_shit = function(){
        this.data.isdirty = true;   
    }
    // New Closure
    function restaurant()
    {
        this.data = {
            isdirty : false,
            soldFood: false,
        };
    }

    restaurant.prototype.buy_food = function()
    {
       private_buy_food(this);
    }
    restaurant.prototype.use_restroom = function()
    {
       private_take_a_shit.call(this);
    }
    return restaurant;
})()

// TEST:

var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.log(McDonalds);
console.log(McDonalds.__proto__);
Flex Elektro Deimling
sumber
Sebenarnya, itu tidak berhasil. Setiap orang new Restaurantakan memiliki restaurantkonstruktor sendiri , dan "prototipe" benar-benar disalahgunakan.
Bergi
@Bergi. Sebenarnya kamu benar. Ini akan bekerja tetapi juga akan menjadi babi sumber daya (apakah itu disebut seperti itu?). Saya mengedit jawaban saya mengenai hal itu.
Flex Elektro Deimling
Terima kasih atas pembaruannya. Tidak tahu apa yang harus disebut versi sebelumnya (tapi "bug" :-)
Bergi
0

Saya tahu ini agak terlambat tetapi bagaimana dengan ini?

var obj = function(){
    var pr = "private";
    var prt = Object.getPrototypeOf(this);
    if(!prt.hasOwnProperty("showPrivate")){
        prt.showPrivate = function(){
            console.log(pr);
        }
    }    
}

var i = new obj();
i.showPrivate();
console.log(i.hasOwnProperty("pr"));
Maxim Balaganskiy
sumber
0

Sudah banyak jawaban untuk pertanyaan ini, tetapi tidak ada yang sesuai dengan kebutuhan saya. Jadi saya datang dengan solusi saya sendiri, saya harap ini berguna bagi seseorang:

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    function getClass(line){
        var i = line.indexOf(" ");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing

Seperti yang Anda lihat, sistem ini berfungsi saat menggunakan jenis kelas ini dalam javascript. Sejauh yang saya tahu tidak ada metode yang dikomentari di atas.

thegunmaster
sumber
1
Penasaran: Apakah kebutuhan Anda benar-benar untuk mengekspos fungsi tetapi menjadikannya no-op dalam runtime - daripada menyembunyikannya dari penelepon eksternal seperti semua / sebagian besar jawaban lainnya lakukan? Jika demikian, mengapa? Apa yang Anda anggap manfaat dari pendekatan ini? Bagi saya, ini tampaknya hanya overhead kinerja yang tidak perlu, API yang tidak jelas dan, yah, mungkin akan menyebabkan debug neraka, tapi saya selalu terbuka untuk perspektif baru ...
JHH
2
@JHH sejujurnya, aku cukup banyak wajah memucat ketika melihat kembali ini. Biaya overhead pada umumnya tidak sepadan sama sekali, meskipun bagi saya itu tidak masalah banyak karena saya tidak membuat banyak panggilan ke kelas-kelas ini. Alasan saya melakukannya dengan cara ini hanya karena relatif bersih dalam cara Anda menulis dan memanggil fungsi. Saya tidak mengerti simbol dan sebagainya pada saat itu, tetapi sekarang saya mengerti, saya pikir umumnya adalah cara untuk menggunakan kelas. Saya sedang mempertimbangkan untuk menghapus jawaban ini bersama-sama. Saya telah memposting beberapa jawaban bodoh, tapi hei, Anda hidup dan belajar.
thegunmaster
Terima kasih untuk umpan baliknya! Saya tidak yakin apakah saya telah salah paham tentang sesuatu. Tapi ya, kita semua hidup dan belajar!
JHH
0

Lihat jawaban ini untuk solusi 'kelas' yang bersih & sederhana dengan antarmuka pribadi dan publik serta dukungan untuk komposisi

kofifus
sumber