วิธีส่วนตัวของ JavaScript


482

ในการสร้างคลาส JavaScript ด้วยวิธีสาธารณะฉันจะทำสิ่งที่ชอบ:

function Restaurant() {}

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

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

ด้วยวิธีนี้ผู้ใช้ในชั้นเรียนของฉันสามารถ:

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

ฉันจะสร้างวิธีส่วนตัวที่สามารถเรียกใช้โดยbuy_foodและuse_restroomวิธีการ แต่ไม่ภายนอกโดยผู้ใช้ของชั้นเรียนได้อย่างไร

ในคำอื่น ๆ ฉันต้องการใช้วิธีการของฉันสามารถทำได้:

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

แต่สิ่งนี้ไม่ได้ผล:

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

ฉันจะกำหนดprivate_stuffเป็นวิธีส่วนตัวได้อย่างไรเพื่อให้การพักทั้งสองนี้เป็นจริง

ฉันได้อ่านการเขียนของ Doug Crockfordสองสามครั้ง แต่ดูเหมือนว่าจะไม่สามารถเรียกวิธีการ "ส่วนตัว" โดยวิธีสาธารณะและวิธีการ "สิทธิพิเศษ" สามารถเรียกได้จากภายนอก

คำตอบ:


403

คุณสามารถทำได้ แต่ข้อเสียคือมันไม่สามารถเป็นส่วนหนึ่งของต้นแบบ:

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();
    }
}

9
การซ่อนเรทภายในไม่ได้รับประกันความเป็นส่วนตัวของล่ามทุกคน ดูcode.google.com/p/google-caja/wiki/…
Mike Samuel

51
@mikesamuel - จริง แต่เมื่อล่ามเหล่านั้นมีข้อบกพร่องในพวกเขา :)
jvenema

133
นี่เป็นวิธีส่วนตัวทั้งหมดถูกต้อง แต่มีแนวโน้มที่จะใช้หน่วยความจำมากขึ้นกว่าวิธีต้นแบบปกติโดยเฉพาะถ้าคุณสร้างวัตถุเหล่านี้จำนวนมาก สำหรับทุกอินสแตนซ์ของวัตถุมันสร้างฟังก์ชั่นแยกต่างหากที่ถูกผูกไว้กับวัตถุและไม่ใช่คลาส นอกจากนี้ยังไม่ได้รับการเก็บขยะจนกว่าวัตถุจะถูกทำลาย
Arindam

4
หากคุณสร้างวัตถุที่ McDonalds () สืบทอดมาจาก Restaurant () McDonalds จะไม่สามารถแทนที่วิธีการส่วนตัวได้หากคุณประกาศด้วยวิธีนี้ จริง ๆ แล้วคุณสามารถทำได้ แต่มันจะไม่ทำงานถ้ามีวิธีอื่นที่เรียกว่า private มันจะเรียกใช้เวอร์ชันดั้งเดิมของเมธอดและคุณไม่สามารถเรียกเมธอด parent ได้ จนถึงตอนนี้ฉันยังไม่พบวิธีที่ดีในการประกาศวิธีการส่วนตัวที่ทำงานได้ดีกับมรดก สิ่งนี้และความหมายของประสิทธิภาพทำให้สิ่งนี้ไม่ใช่รูปแบบการออกแบบที่ดีมากเลย ฉันขอแนะนำให้ทำคำนำหน้าบางชนิดเพื่อระบุวิธีการส่วนตัวโดยเปรียบเทียบกับขีดเส้นใต้
Hoffmann

68
วิธีการส่วนตัวไม่ควรถูกลบล้าง - เป็นวิธีการส่วนตัว
17 ของ 26

163

ใช้ฟังก์ชั่นการเรียกตนเองและการโทร

JavaScript ใช้ต้นแบบและไม่มีคลาส (หรือวิธีการสำหรับเรื่องนั้น) เช่นภาษาที่เน้นวัตถุ นักพัฒนา JavaScript ต้องคิดใน JavaScript

คำพูดของ Wikipedia:

แตกต่างจากภาษาเชิงวัตถุจำนวนมากไม่มีความแตกต่างระหว่างนิยามฟังก์ชันและนิยามวิธีการ แต่ความแตกต่างเกิดขึ้นระหว่างการเรียกใช้ฟังก์ชัน เมื่อฟังก์ชั่นถูกเรียกว่าเป็นวิธีการของวัตถุคำหลักนี้ในท้องถิ่นของฟังก์ชั่นที่ถูกผูกไว้กับวัตถุนั้นสำหรับการภาวนานั้น

วิธีการแก้ปัญหาโดยใช้ฟังก์ชั่นการเรียกตนเองและฟังก์ชั่นการโทรเพื่อเรียก "วิธี" ส่วนตัว:

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

ฟังก์ชั่นการโทรช่วยให้เราสามารถเรียกใช้ฟังก์ชันส่วนตัวที่มีบริบทที่เหมาะสม ( this)


ง่ายกว่าด้วย Node.js

หากคุณกำลังใช้node.jsคุณไม่จำเป็นต้องใช้IIFEเพราะคุณสามารถใช้ประโยชน์จากระบบการโหลดโมดูล :

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

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

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

exports.MyObject = MyObject;

โหลดไฟล์:

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

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


(ทดลอง) ES7 กับ Bind Operator

ตัวดำเนินการเชื่อมโยง::คือข้อเสนอ ECMAScript และนำไปใช้ใน Babel ( ระยะ 0 )

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

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

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

โหลดไฟล์:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function

34
นี่คือคำตอบที่ดีที่สุด ประโยชน์ของวิธีส่วนตัวไม่มีขยะ
TaylorMac

1
@TaylorMac ยกเว้น.callส่วนหนึ่ง
pishpish

1
@janje Huh? นั่นคือประเด็นของคำถามไม่มีf()javascript ส่วนตัว(ไม่ในขณะนี้)
Yves M.

2
@YvesM ประเด็นของคำถามคือการเลือกรูปแบบที่ดีที่สุดที่จำลอง
pishpish

1
@ เปลี่ยนแปลงสิ่งที่ดีที่สุดที่จะประนีประนอมกับฟังก์ชั่นที่ซ่อนอยู่ (ไม่สามารถเรียกได้จากภายนอก), sintax ที่ดี (ไม่.call) และหน่วยความจำขนาดเล็ก (ไม่มีฟังก์ชั่นในตัวอย่าง)? มันมีอยู่จริงเหรอ?
Ciprian Tomoiagă

161

คุณสามารถจำลองวิธีการส่วนตัวเช่นนี้:

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();

ข้อมูลเพิ่มเติมเกี่ยวกับเทคนิคนี้ที่นี่: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html


7
ฉันขอแนะนำไซต์ Douglas Crockford เป็นแหล่งข้อมูลสำหรับวิธีการส่วนตัว / สาธารณะและสมาชิกjavascript.crockford.com/private.html
Jared

10
เขาพูดถึงลิงก์นั้นในคำถาม
Gulzar Nazim

8
ข้อเสียของวิธีนี้คือคุณไม่สามารถมี private_stuff () เข้าถึงข้อมูลส่วนตัวอื่น ๆ ใน Restaurant และวิธี Restaurant อื่น ๆ ไม่สามารถเรียก private_stuff () ข้อเสียคือถ้าคุณไม่ต้องการเงื่อนไขใด ๆ ที่ฉันเพิ่งกล่าวถึงคุณสามารถเก็บ use_restroom () ในต้นแบบ
17 ของ 26

6
นี่ควรเป็นคำตอบและคำตอบที่ได้รับการยอมรับเนื่องจากผู้เขียนใช้คุณสมบัติต้นแบบอย่างชัดเจน
Gabriel Llamas

23
ด้วยรูปแบบที่เสนอโดย @georgebrock ข้อมูลส่วนตัวทั้งหมดจะถูกแบ่งปันในวัตถุร้านอาหารทั้งหมด นั่นคือคล้ายกับตัวแปรส่วนตัวและฟังก์ชั่นคงที่ใน OOP ตามระดับ ฉันคิดว่า OP ไม่ต้องการสิ่งนี้ เพื่อแสดงให้เห็นว่าผมหมายถึงผมสร้างjsFiddle
feklee

35

ในสถานการณ์เหล่านี้เมื่อคุณมี API สาธารณะและคุณต้องการวิธีการ / คุณสมบัติส่วนตัวและสาธารณะฉันจะใช้รูปแบบโมดูลเสมอ รูปแบบนี้ได้รับความนิยมในห้องสมุด YUI และสามารถดูรายละเอียดได้ที่นี่:

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

มันตรงไปตรงมาจริงๆและง่ายสำหรับนักพัฒนารายอื่นที่จะเข้าใจ สำหรับตัวอย่างง่ายๆ:

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

สิ่งนี้จะไม่ถูกตรวจพบโดยการเติมข้อความอัตโนมัติของ IDE :(
คลิกที่นี่โหวตขึ้น

19
แต่นี่ไม่ใช่คลาสดังนั้นคุณไม่สามารถมี 2 "อินสแตนซ์" ของสิ่งนี้ที่มีสถานะต่างกัน
DevAntoine

ลบส่วน () และคุณมี "คลาส" อย่างน้อยที่คุณสามารถยกตัวอย่าง isntances ที่แตกต่างกันกับสถานะที่แตกต่างกัน รูปแบบโมดูลหน่วยความจำค่อนข้างเข้มข้น แต่ ...
oligofren

@DevAntoine ดูความคิดเห็นสำหรับคำตอบของ 17 จาก 26 ในชั้นเรียน JavaScript ที่ขยายได้และวิธีการส่วนตัวไม่ได้ไปด้วยกันอย่างง่ายดาย ข้อเสนอแนะของฉันในกรณีนี้คือไปกับการแต่งมากกว่ามรดก สร้างต้นแบบที่ขยายได้ด้วยวิธีการเดียวกันกับวัตถุคอนกรีตที่ล้อมรอบ จากนั้นภายในต้นแบบของคุณคุณสามารถตัดสินใจได้ว่าเมื่อใดจะเรียกวิธีการบนวัตถุที่เป็นรูปธรรมของคุณ

มีข้อเสียใด ๆ ที่จะเรียกตัวแปรของประชาชนจากฟังก์ชั่นส่วนตัวชอบโดย: aPrivateMethod = function() { MYLIB.aPublicProperty}?
ฮันนา

21

นี่คือชั้นเรียนที่ฉันสร้างขึ้นเพื่อทำความเข้าใจกับสิ่งที่ Douglas Crockford แนะนำในเว็บไซต์ส่วนตัวของเขาใน 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

}

5
= นั่นคือรูปแบบที่ค่อนข้างธรรมดาได้รับความนิยมจาก Crockford ดังกล่าวในหนังสือของเขา "Javascript: The good parts"
oligofren

8
thatถูกใช้แทนthisเพื่อหลีกเลี่ยงปัญหาการกำหนดขอบเขตเมื่อฟังก์ชันถูกผูกไว้กับวัตถุอื่น ที่นี่คุณจะถูกจัดเก็บthisในthatและไม่เคยใช้มันอีกครั้งซึ่งเป็นเช่นเดียวกับไม่ได้ทำมันเลย คุณควรเปลี่ยนthisกับthatทุกคนทั่วทั้งConstructorฟังก์ชั่นภายใน (ไม่ใช่วิธีการประกาศ) ถ้าemployeeเป็นapplyed หรือcalled ในบางวิธีวิธีการเหล่านี้อาจโยนเนื่องจากthisจะถูกผูกไว้อย่างไม่ถูกต้อง
Maroshii

นอกจากนี้ทุกอินสแตนซ์จะมีสำเนาของฟังก์ชั่นส่วนตัวที่ไม่มีประสิทธิภาพ ที่จริงแล้ววิธีการสาธารณะไม่สามารถเข้าถึง vars ส่วนตัวชั้นทำให้ฉันต้องการเปลี่ยนไปโผ น่าเสียดายที่ angulardart นั้นเป็นเบต้าขั้นสูง
Ray Foss

วิธี "ตัวสร้าง" อยู่ที่ไหนในนี้? ฉันจะใส่ตรรกะที่ปกติจะดำเนินการในวิธีการสร้างของชั้นเรียนเมื่อมันเป็นอินสแตนซ์?
BadHorsie

13

ฉันคิดในสิ่งนี้: แก้ไข: จริง ๆ แล้วมีคนเชื่อมโยงกับโซลูชันที่เหมือนกัน ดุจ!

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!'

1
นี่เป็นเทคนิคที่ดี แต่คุณจะอนุญาตพารามิเตอร์ใน Constructor ของคุณได้อย่างไร ตัวอย่างเช่นvar getAwayVehicle = new Car(100);ที่100ความเร็วและคุณต้องการที่จะแจ้งเตือนความเร็ว ขอบคุณ!
Jason

1
คิดออกสามารถมีvar Car = function(speed) { this.speed = speed; }และ `คืน {คอนสตรัคเตอร์: รถยนต์ ... '
Jason

11

ฉันคิดว่าคำถามดังกล่าวเกิดขึ้นซ้ำแล้วซ้ำอีกเพราะขาดความเข้าใจในการปิด Сlosuresเป็นสิ่งสำคัญที่สุดใน JS โปรแกรมเมอร์ JS ทุกคนต้องรู้สึกถึงความสำคัญของมัน

1.ก่อนอื่นเราต้องแยกขอบเขต (การปิด)

function () {

}

2.ในพื้นที่นี้เราสามารถทำอะไรก็ได้ที่เราต้องการ และไม่มีใครรู้เกี่ยวกับมัน

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.เพื่อให้โลกรู้เกี่ยวกับชั้นเรียนร้านอาหารของเราเราต้องส่งคืนจากการปิด

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

4.ในตอนท้ายเรามี:

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.นอกจากนี้วิธีนี้มีศักยภาพในการถ่ายทอดและการสร้างเทมเพลต

// 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')

ฉันหวังว่านี่จะช่วยให้บางคนเข้าใจเรื่องนี้ดีขึ้น


2
สิ่งที่คุณมีในจุดที่ 4 นั้นยอดเยี่ยมมาก! ฉันคิดว่ามันเป็นคำตอบเดียวจากที่นี่ที่คุณได้รับทั้งประสิทธิภาพ / ความจำที่เพิ่มขึ้นของการใช้วิธีการในต้นแบบและวิธีการสาธารณะเหล่านี้มีการเข้าถึงสมาชิกส่วนตัวอย่างเต็มที่ +1
Hudon

7
มันไม่ทำงาน ตัวแปรชื่อที่นี่ทำหน้าที่เหมือนตัวแปรคงที่และใช้ร่วมกันโดยทุกกรณีของร้านอาหาร นี่คือ jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shital Shah

มันเป็นการลองที่ดี แต่เมื่อ Shital ชี้ให้เห็นตัวแปรชื่อนั้นมีข้อผิดพลาด
oligofren

2
เพิ่ม 2c ของฉันที่นี่เพื่อยืนยันว่านี่ใช้งานไม่ได้ดูดี แต่ตามที่กล่าวไว้ข้างต้น "ชื่อ" จะทำหน้าที่เป็นตัวแปรแบบคงที่เช่นใช้ร่วมกันในทุกกรณี
Paul Carroll

10

ส่วนตัวแล้วฉันชอบรูปแบบต่อไปนี้สำหรับการสร้างคลาสใน 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(); }
        ...
    };
})();

อย่างที่คุณเห็นมันช่วยให้คุณสามารถกำหนดทั้งคุณสมบัติของคลาสและคุณสมบัติของอินสแตนซ์ซึ่งแต่ละคุณสมบัติสามารถเป็นสาธารณะและส่วนตัว


การสาธิต

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();

ดูซอนี้


นี่ทำให้ฉันต้องการใช้คลาส es6 และดูว่ามันทำอะไร transpiles ด้วย
sheriffderek

9

การปิดทั้งหมดนี้จะทำให้คุณเสียค่าใช้จ่าย ตรวจสอบให้แน่ใจว่าคุณทดสอบความเร็วโดยเฉพาะใน IE คุณจะพบว่าคุณดีกว่าด้วยการตั้งชื่อ ยังมีผู้ใช้เว็บไซต์องค์กรจำนวนมากที่ถูกบังคับให้ใช้ IE6 ...


34
ใครสนใจอย่างจริงจัง?
nowayyy

17
ที่ 9% ที่ยังคงใช้ IE6 ไม่สนใจเกี่ยวกับความเร็วการเพิ่มประสิทธิภาพและคุณสมบัติ HTML5 ที่ทันสมัยทั้งหมด ดังนั้นการปิดจะไม่เสียค่าใช้จ่ายใด ๆ
Gabriel Llamas

6
ตอนนี้คือ 0.5% (สิงหาคม 2012) w3schools.com/browsers/browsers_explorer.asp
Lorenzo Polidori

7
ผู้ใช้ @LorenzoPolidori w3schools! == ผู้ใช้เว็บขององค์กร;]
WynandB

แบบแผนการตั้งชื่อ (เช่นการขีดเส้นใต้ไว้ล่วงหน้า) เป็นวิธีที่จะดำเนินการ รหัสนั้นง่ายต่อการบำรุงรักษาและวิธีการยังคงถูกกำหนดบนต้นแบบ ทุกวันนี้แม้ว่า ... ฉันเพิ่งทำเครื่องหมายวิธีเป็นส่วนตัวใน typescript
David Sherret

5

อย่าพูดมาก มันเป็นจาวาสคริปต์ ใช้แบบแผนการตั้งชื่ออนุสัญญาการตั้งชื่อ

หลังจากหลายปีของการทำงานในชั้นเรียน es6 ฉันเพิ่งเริ่มทำงานในโครงการ es5 (โดยใช้ requireJS ซึ่งดูแล้วอย่างมาก) ฉันได้รับมากกว่าทุกกลยุทธ์ที่กล่าวถึงที่นี่และทุกอย่างเดือดลงไปโดยทั่วไปเพื่อใช้แบบแผนการตั้งชื่อ :

  1. Javascript ไม่มีคำหลักขอบเขตเช่น privateจาวาสคริปต์ไม่ได้มีคำหลักขอบเขตเช่นนักพัฒนาซอฟต์แวร์อื่นที่เข้าสู่ Javascript จะรู้ล่วงหน้า ดังนั้นแบบแผนการตั้งชื่อแบบง่าย ๆ ก็เพียงพอแล้ว หลักการตั้งชื่อแบบง่าย ๆ ของคำนำหน้าด้วยการขีดล่างแก้ปัญหาของคุณสมบัติส่วนตัวและวิธีการส่วนตัว
  2. ลองใช้ประโยชน์จาก Prototype เพื่อเหตุผลด้านความเร็ว แต่อย่าให้มากกว่านั้นอีกต่อไป ลองรักษา es5 "class" ให้ใกล้เคียงกับสิ่งที่เราคาดหวังในภาษาแบ็กเอนด์อื่น ๆ (และปฏิบัติต่อทุกไฟล์เป็นคลาสแม้ว่าเราไม่ต้องการส่งคืนอินสแตนซ์)
  3. มาสาธิตด้วยสถานการณ์โมดูลที่สมจริงยิ่งขึ้น (เราจะใช้ es5 รุ่นเก่าและรุ่นเก่า needJs)

-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();
            }
            */
        }
    });

2
ควรสังเกตว่าทั้งภาษาจาวาสคริปต์และโฮสต์เบราว์เซอร์ทั่วไปไม่ได้กำหนดวัตถุใด ๆ ที่อาศัยหลักการตั้งชื่อเพื่อ "ซ่อน" สถานะส่วนตัวดังนั้นในขณะที่คุณอาจถูกต้องที่นักพัฒนาจะเข้าใจแนวคิด แต่ก็ยังนำไปสู่ วิธีการ OO ในการเขียนโปรแกรม OO
รวย remer

ฉันขออ้างอิงที่ดีในการทำเช่นนี้ได้ไหม มีบางส่วนในตัวอย่างนี้ที่ใหม่สำหรับฉัน defineและconstructorและโครงสร้างของตัวเอง ในขณะที่ฉันเห็นด้วยกับคำตอบส่วนใหญ่ฉันได้เริ่มทำงานกับ JS ด้วยอิทธิพลของ OOP มากมายและยังเร็วเกินไปที่จะ TS เพราะฉันเคยมีประสบการณ์มาก่อนใน C # ฉันคิดว่าฉันต้องคลายความเข้าใจในสิ่งเหล่านี้และต้องเข้าใจกระบวนทัศน์ต้นแบบ / ขั้นตอน (upvoted, btw)
Cold Cerberus

1
@ColdCerberus ข้อมูลโค้ดนี้กำลังใช้งาน es5 คุณสามารถดูภาพที่สมบูรณ์ของวิธีการนี้ที่นี่: gist.github.com/jonnyreeves/2474026 แต่โปรดทราบว่าคุณจะต้องใช้วิธีการนี้และอัปเดตเป็นคลาส es6: googlechrome.github.io/samples/classes-es6และโมดูล es6 (ไวยากรณ์การนำเข้า / ส่งออก): hackernoon.com/…
prograhammer

5

คุณสามารถทำได้ในขณะนี้กับES10 วิธีเอกชน คุณเพียงแค่ต้องเพิ่ม#ก่อนชื่อวิธี

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

  getPrivateMessage() {
    return #privateMethod();
  }
}

2
ยกเว้นนี่คือด่าน 3 และยังไม่เป็นส่วนหนึ่งของภาษาอย่างเป็นทางการ
misterhtmlcss

3

ใช้ใด ๆ ของการแก้ปัญหาที่เป็นไปตาม Crockford ของส่วนตัวหรือpriviledgedรูปแบบ ตัวอย่างเช่น:

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

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

ในกรณีใด ๆ ที่ผู้โจมตีไม่มี "ดำเนินการ" ที่ถูกต้องในบริบท JS เขาไม่สามารถเข้าถึงช่องหรือวิธีการ "สาธารณะ" หรือ "ส่วนตัว" ใด ๆ ในกรณีที่ผู้โจมตีมีการเข้าถึงนั้นเขาสามารถดำเนินการแบบหนึ่งซับ:

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

โปรดทราบว่ารหัสข้างต้นเป็นรหัสทั่วไปสำหรับตัวสร้างความเป็นส่วนตัวทั้งหมด มันจะล้มเหลวด้วยการแก้ปัญหาบางอย่างที่นี่ แต่ควรจะชัดเจนว่าการแก้ปัญหาแบบปิดทั้งหมดนั้นสามารถแตกหักได้เช่นนี้ด้วยวิธีที่แตกต่างกันreplace()พารามิเตอร์ที่

หลังจากนี้จะถูกดำเนินการวัตถุใด ๆ ที่สร้างขึ้นด้วยnew Foo()จะมีevalวิธีการที่สามารถเรียกได้ว่าจะส่งกลับหรือเปลี่ยนค่าหรือวิธีการที่กำหนดไว้ในการปิดของตัวสร้างเช่น:

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

ปัญหาเดียวที่ฉันเห็นด้วยนี้ไม่สามารถใช้งานได้ในกรณีที่มีเพียงหนึ่งอินสแตนซ์และสร้างขึ้นเมื่อโหลด แต่ก็ไม่มีเหตุผลใดที่จะกำหนดต้นแบบจริงและในกรณีนั้นผู้โจมตีสามารถสร้างวัตถุขึ้นมาใหม่แทนที่จะเป็นตัวสร้างตราบใดที่เขามีวิธีผ่านพารามิเตอร์เดียวกัน (เช่นมีค่าคงที่หรือคำนวณจากค่าที่มี)

ในความคิดของฉันนี้สวยมากทำให้การแก้ปัญหาของ Crockford ไร้ประโยชน์ เนื่องจาก "ความเป็นส่วนตัว" เสียง่ายข้อเสียของโซลูชันของเขา (ลดความสามารถในการอ่านและการบำรุงรักษาประสิทธิภาพที่ลดลงหน่วยความจำเพิ่มขึ้น) ทำให้วิธีการแบบ "ไม่มีความเป็นส่วนตัว" เป็นตัวเลือกที่ดีกว่า

ฉันมักจะใช้ขีดเส้นใต้ชั้นนำในการทำเครื่องหมาย__privateและ_protectedวิธีการและสาขา (สไตล์ Perl) แต่ความคิดของการมีความเป็นส่วนตัวใน JavaScript เพียงแสดงให้เห็นว่ามันเป็นภาษาที่เข้าใจผิด

ดังนั้นฉันไม่เห็นด้วยกับCrockfordยกเว้นประโยคแรกของเขา

ดังนั้นคุณจะได้รับความเป็นส่วนตัวอย่างแท้จริงใน JS ได้อย่างไร วางทุกอย่างที่จำเป็นต้องเป็นส่วนตัวทางฝั่งเซิร์ฟเวอร์และใช้ JS เพื่อทำการโทร AJAX


นี่เป็นปัญหาที่ร้ายแรงซึ่งควรเป็นที่รู้จักมากขึ้น มี 'การป้องกัน' จากการโจมตีนี้หรือไม่?
James

@ James ไม่มีที่ฉันรู้ฉันคิดว่ามันเป็นธรรมชาติของสัตว์ร้าย ในขณะที่ฉันชี้ให้เห็นว่าคุณสามารถย้ายการทำงานไปยังเซิร์ฟเวอร์ที่มันทำงานในสภาพแวดล้อมที่มีการป้องกัน ประเด็นที่ฉันต้องการคำตอบในตอนนี้ก็คือทางออกของ Crockford ไม่ได้ช่วยทำให้โค้ดยุ่งยากและซ่อนความจำเป็นในการทำบางสิ่งบางอย่างโดยไม่จำเป็น
Fozi

หากผู้ใช้ป้อนรหัสผ่านลับเขาไม่สามารถทำฝั่งเซิร์ฟเวอร์นี้ได้ ในบางจุดรหัสผ่านจะอยู่ในรูปแบบ 'ส่วนตัว' ดังนั้นผู้โจมตีสามารถอ่านมันได้หรือไม่ ฉันเชื่อถือรหัสของฉันและมาตรฐานบ้านของฉันไม่อนุญาตให้ใช้ Eval () ผู้โจมตีอาจเป็นปลั๊กอินหรือไลบรารี JavaScript ของบุคคลที่สามที่เป็นอันตรายซึ่งฉันตรวจสอบไม่ถูกต้องดังนั้นใช่ฉันต้องตรวจสอบสิ่งเหล่านี้ ผู้โจมตีอาจเป็นเหมือนโฆษณาที่อยู่ด้านข้างซึ่งไม่ควรโต้ตอบกับโค้ดของฉัน ฉันป้องกันสิ่งนั้นโดยการห่อโค้ดทั้งหมดของฉันในแบบไม่ระบุชื่อ(function () {allMyStuff}());เพื่อไม่ให้เปิดเผยทั่วโลก
James

@ James นี่กำลังได้รับ OT หากคุณต้องการดำเนินการต่อโปรดเปิดคำถามใหม่ ใช่ผู้โจมตีสามารถอ่านรหัสผ่าน จากตัวแปร "ส่วนตัว" ของคุณ หรือจาก DOM หรือเขาสามารถแทนที่ AJAX API ได้ หรือเขาจะแทนที่หน้าของคุณด้วยอย่างอื่น หากเขาไม่สามารถทำสิ่งใด ๆ ข้างต้นก็ไม่จำเป็นต้องมี JS "ความเป็นส่วนตัว" เพราะเขาไม่สามารถอ่านตัวแปร JS ของคุณได้เช่นกัน ประเด็นก็คือ "วิธีแก้ปัญหา" ของ Crockford ที่ทุกคนใช้อยู่ตอนนี้ไม่ได้ช่วยแก้ไขปัญหานี้
Fozi

ฉันเชื่อว่าการหลอกรหัสหลอกอาจเป็นการป้องกันที่อ่อนแอต่อการโจมตีนี้ - ยากที่จะแก้ไขเนื้อหาของฟังก์ชันเมื่อคุณไม่สามารถพึ่งพาฟังก์ชั่นที่มีชื่อตายตัวได้ ยากกว่าที่จะทำf.eval('nameOfVariable')เมื่อคุณไม่รู้ว่า'nameOfVariable'... คืออะไร
Gershom


2

หากคุณต้องการฟังก์ชั่นเต็มรูปแบบของฟังก์ชั่นสาธารณะและส่วนตัวที่มีความสามารถสำหรับฟังก์ชั่นสาธารณะในการเข้าถึงฟังก์ชั่นส่วนตัวรหัสรูปแบบสำหรับวัตถุเช่นนี้:

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 () {
  };
}

มีใครบอกฉันได้ไหมว่าทำไมเรื่องนี้ถึงได้ถูกโหวต? ดูดีกับผม.
thomasrutter

10
ทุกครั้งที่คุณทำnew MyObjectต้นแบบของMyObjectจะถูกแทนที่ด้วยค่าเดียวกัน
bpierre

2
-1 ไม่เคยกำหนดให้.prototypeภายในตัวสร้าง
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" );

คล้ายกับ georgebrock แต่ verbose น้อยกว่า (IMHO) ปัญหาใด ๆ กับการทำเช่นนี้ (ฉันไม่ได้เห็นมันทุกที่)

แก้ไข: ฉันรู้ว่านี่ไร้ประโยชน์เพราะทุกการสร้างอินสแตนซ์อิสระมีสำเนาวิธีการสาธารณะของตัวเองดังนั้นจึงเป็นการบ่อนทำลายการใช้ต้นแบบ


2

นี่คือสิ่งที่ฉันมีความสุขมากที่สุดเกี่ยวกับวิธีส่วนตัว / สาธารณะ / สมาชิกและการสร้างอินสแตนซ์ใน javascript:

นี่คือบทความ: http://www.sefol.com/?p=1090

และนี่คือตัวอย่าง:

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/


วิธีการนี้มีความสำคัญที่สำคัญอย่างหนึ่ง - ถ้าคุณสร้างวัตถุ 2 อันในหน่วยความจำจะมีเมทเหมือนกัน 2 อัน (PublicFunction สำหรับเช่น) วัตถุ 1,000 ชิ้นจะกินหน่วยความจำทั้งหมดของคุณ
Artem G

2

รูปแบบโมดูลถูกต้องในกรณีส่วนใหญ่ แต่ถ้าคุณมีอินสแตนซ์นับพันคลาสจะบันทึกหน่วยความจำ หากการบันทึกหน่วยความจำเป็นเรื่องที่กังวลและวัตถุของคุณมีข้อมูลส่วนตัวจำนวนเล็กน้อย แต่มีฟังก์ชั่นสาธารณะจำนวนมากคุณจะต้องการให้ฟังก์ชั่นสาธารณะทั้งหมดอยู่ใน. ต้นแบบเพื่อบันทึกหน่วยความจำ

นี่คือสิ่งที่ฉันมาด้วย:

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

วัตถุprivมีคุณสมบัติส่วนตัว มันสามารถเข้าถึงได้ผ่านฟังก์ชั่นสาธารณะgetPriv()แต่ฟังก์ชั่นนี้จะกลับมาfalseเว้นแต่คุณจะผ่านมันsecretและเป็นที่รู้จักกันเฉพาะในการปิดหลัก


ที่เลียนแบบสมาชิกที่ได้รับการป้องกันประเภทที่สืบทอดมาจากมันสามารถเข้าถึงสมาชิกที่ได้รับการคุ้มครองได้เช่นกัน ฉันชอบรูปแบบนี้มากกว่ารูปแบบส่วนตัวเช่นกัน
HMR

2

แล้วเรื่องนี้ล่ะ

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;

})();

การค้นหาตัวแปรส่วนตัวเป็นไปไม่ได้นอกขอบเขตของฟังก์ชั่นทันที ไม่มีฟังก์ชั่นซ้ำซ้อนประหยัดหน่วยความจำ

ข้อเสียคือการค้นหาตัวแปรส่วนตัวเป็น clunky privateVars[this.id].cookedนั้นไร้สาระที่จะพิมพ์ นอกจากนี้ยังมีตัวแปร "id" เพิ่มเติม


นี้จะทำให้Restaurantเป็นundefinedเพราะคุณไม่ได้กลับมาอะไรจากฟังก์ชั่นที่ไม่ระบุชื่อ
user4815162342

ที่ไหนและอย่างไร สมมติว่าการอ้างอิงถึงร้านอาหารที่สร้างหายไป privateVars จะไม่มีการอ้างอิงถึงเจ้าของ กราฟอ้างอิงคือ acyclic ฉันพลาดอะไรไป
Evan Leis

จริงๆแล้วนี่เป็นคำตอบเดียวที่สนับสนุนคุณสมบัติส่วนตัวนอกเหนือจากวิธีการ มีเพียงสองประเด็นเท่านั้นที่ระบุไว้ในคำตอบแล้ว
pishpish

ผมเห็นการรั่วไหลของหน่วยความจำ: เมื่อตัวอย่างของการได้รับขยะเก็บค่าของมันยังคงอยู่ภายในRestaurant อาจจะเปลี่ยนที่ดีสำหรับในกรณีนี้ privateVarsWeakMapArray
Gershom

2

ล้อมโค้ดทั้งหมดใน Function ที่ไม่ระบุชื่อ: จากนั้นทุกฟังก์ชั่นจะเป็นส่วนตัวเท่านั้นที่มีฟังก์ชั่นแนบมากับwindowวัตถุ

(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,{});

ใช้สิ่งนี้:

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

ซอ


1

WeakMapฉันชอบที่จะเก็บข้อมูลส่วนตัวในที่เกี่ยวข้อง วิธีนี้ช่วยให้คุณสามารถใช้วิธีสาธารณะของคุณกับต้นแบบที่พวกเขาอยู่ นี่เป็นวิธีที่มีประสิทธิภาพที่สุดในการจัดการปัญหานี้กับวัตถุจำนวนมาก

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};

0

ฟังก์ชั่นส่วนตัวไม่สามารถเข้าถึงตัวแปรสาธารณะโดยใช้รูปแบบโมดูล


0

เมื่อทุกคนโพสต์รหัสของเขาที่นี่ฉันก็จะทำเช่นนั้น ...

ฉันชอบ Crockford เพราะเขาแนะนำรูปแบบวัตถุจริงใน Javascript แต่เขาก็เกิดความเข้าใจผิดขึ้นมาใหม่ว่า "สิ่งนั้น"

แล้วทำไมเขาถึงใช้ "that = this"? มันไม่มีอะไรเกี่ยวข้องกับฟังก์ชั่นส่วนตัวเลย มันเกี่ยวกับฟังก์ชั่นด้านใน!

เพราะตาม Crockford นี่เป็นรหัสบั๊กกี้:

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

ดังนั้นเขาแนะนำให้ทำสิ่งนี้:

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

ดังนั้นอย่างที่ฉันพูดฉันค่อนข้างแน่ใจว่า Crockford ผิดคำอธิบายของเขาเกี่ยวกับเรื่องนี้และสิ่งนี้ (แต่รหัสของเขาถูกต้องแน่นอน) หรือว่าเขาหลอกโลก Javascript เพื่อรู้ว่าใครกำลังคัดลอกรหัสของเขา ฉันไม่รู้แล้ว ... ฉันไม่มีเบราว์เซอร์เกินเหตุ;

แก้ไข

อานั่นคือสิ่งที่เกี่ยวกับ: 'var that = this;' หมายถึงใน JavaScript?

ดังนั้น Crockie ผิดจริง ๆ กับคำอธิบายของเขา .... แต่ถูกต้องกับรหัสของเขาดังนั้นเขายังคงเป็นคนที่ดี :))


0

โดยทั่วไปฉันเพิ่มวัตถุส่วนตัว _ ลงในวัตถุชั่วคราว คุณต้องเปิดความเป็นส่วนตัวอย่างสุดยอดใน "Power-constructor" สำหรับวิธีการนี้ หากคุณเรียกใช้เมธอดจากต้นแบบคุณจะสามารถเขียนทับต้นแบบ - เมธอดได้

  • กำหนดให้สามารถเข้าถึงเมธอดสาธารณะใน "Power-constructor": (ctx คือบริบทวัตถุ)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • ตอนนี้ฉันมี openPrivacy นี้:

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

0

นี่คือสิ่งที่ฉันทำงานออกมา:

ความต้องการหนึ่งระดับของน้ำตาลในรหัสที่คุณสามารถหาได้ที่นี่ ยังรองรับการป้องกันการสืบทอดเสมือนสิ่งคงที่ ...

;( 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'

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


0

ฉันได้สร้างเครื่องมือใหม่เพื่อให้คุณมีวิธีการส่วนตัวที่แท้จริงในต้นแบบ https://github.com/TremayneChrist/ProtectJS

ตัวอย่าง:

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

1
คุณช่วยอธิบายวิธีการทำงานได้ไหม วิธี / ที่สามารถคุณเรียก_privateวิธี?
Bergi

0

คุณต้องปิดตัวสร้างจริงของฟังก์ชันที่คุณสามารถกำหนดวิธีการส่วนตัวของคุณ ในการเปลี่ยนข้อมูลของอินสแตนซ์ด้วยวิธีส่วนตัวเหล่านี้คุณต้องให้ "นี่" กับพวกเขาไม่ว่าจะเป็นอาร์กิวเมนต์ของฟังก์ชันหรือโดยการเรียกใช้ฟังก์ชันนี้ด้วย. apply (this):

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__);

จริงๆแล้วมันใช้งานไม่ได้ ทุกคนnew Restaurantจะมีrestaurantคอนสตรัคเตอร์ของตัวเองและ "ต้นแบบ" ถูกทารุณกรรมโดยสิ้นเชิง
Bergi

@Bergi ที่จริงแล้วคุณพูดถูก มันจะใช้งานได้ แต่จะเป็นหมูที่มาจากแหล่ง Ressource ด้วย ฉันแก้ไขคำตอบของฉันในเรื่องนั้น
Flex Elektro Deimling

ขอบคุณสำหรับการอัพเดท. ไม่ทราบว่าจะเรียกเวอร์ชันก่อนหน้านี้อย่างไร (แต่ "บั๊ก" :-)
Bergi

0

ฉันรู้ว่ามันสายเกินไป แต่แล้วเรื่องนี้ล่ะ?

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"));

0

มีคำตอบมากมายสำหรับคำถามนี้อยู่แล้วแต่ไม่มีอะไรเหมาะสมกับความต้องการของฉัน ดังนั้นฉันจึงคิดวิธีแก้ปัญหาของตัวเองฉันหวังว่ามันจะมีประโยชน์สำหรับใครบางคน:

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

ตามที่คุณเห็นว่าระบบนี้ใช้งานได้เมื่อใช้คลาสประเภทนี้ใน javascript เท่าที่ฉันคิดได้ว่าไม่มีวิธีการใดที่ให้ความเห็นด้านบน


1
อยากรู้อยากเห็น: ความต้องการของคุณที่จะเปิดเผยฟังก์ชั่นจริง ๆ แต่ทำให้มันไม่ทำงานในรันไทม์แทนที่จะซ่อนไว้จากผู้โทรภายนอกเหมือนคำตอบอื่น ๆ ทั้งหมด ถ้าเป็นเช่นนั้นทำไม คุณคิดว่าอะไรคือประโยชน์ของแนวทางนี้ สำหรับฉันดูเหมือนว่าจะเป็นเพียงค่าใช้จ่ายที่ไม่จำเป็นเท่านั้น API ที่ไม่ชัดเจนและอาจจะทำให้นรกดีขึ้น แต่ฉันเปิดมุมมองใหม่เสมอ ...
JHH

2
@JHH พูดตามตรงฉันหน้าตาน่ากลัวเมื่อมองย้อนกลับไป ค่าใช้จ่ายโดยทั่วไปไม่คุ้มค่าเลย แต่สำหรับฉันมันไม่สำคัญมากนักเพราะฉันไม่ได้โทรไปยังชั้นเรียนเหล่านี้มากมาย เหตุผลที่ฉันทำแบบนี้ก็เพราะมันค่อนข้างสะอาดในแบบที่คุณเขียนและเรียกใช้ฟังก์ชั่น ฉันไม่เข้าใจสัญลักษณ์และเวลาเช่นนั้น แต่ตอนนี้ที่ฉันทำฉันคิดว่าโดยทั่วไปเป็นวิธีที่จะไปเมื่อใช้เรียน ฉันกำลังพิจารณาที่จะลบคำตอบนี้ด้วยกันทั้งหมด ฉันโพสต์คำตอบโง่ ๆ ไว้หลายคำ แต่เดี๋ยวก่อนคุณมีชีวิตและเรียนรู้
thegunmaster

ขอบคุณสำหรับความคิดเห็น! ฉันไม่แน่ใจว่าฉันเข้าใจผิดบางอย่าง แต่ใช่เราทุกคนมีชีวิตอยู่และเรียนรู้!
JHH

0

ดูคำตอบนี้สำหรับโซลูชัน 'คลาส' ที่สะอาดและเรียบง่ายพร้อมอินเทอร์เฟซส่วนตัวและสาธารณะและการสนับสนุนการจัดองค์ประกอบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.